WordPress.org

Make WordPress Core

Ticket #25088: 25088.4.diff

File 25088.4.diff, 70.5 KB (added by jorbin, 8 months ago)
  • tests/qunit/index.html

     
     1<!DOCTYPE HTML> 
     2<html> 
     3<head> 
     4  <title>WordPress QUnit Test Suite</title> 
     5 
     6  <!-- jquery --> 
     7  <!--<script src="http://code.jquery.com/jquery-1.7.min.js"></script>--> 
     8  <script src="../../src/wp-includes/js/jquery/jquery.js"></script> 
     9 
     10  <!-- qunit --> 
     11  <link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen" /> 
     12  <script src="vendor/qunit.js"></script> 
     13 
     14 
     15  <!-- Tested Files --> 
     16  <script src="../../src/wp-includes/js/zxcvbn.min.js"></script> 
     17  <script src="../../src/wp-admin/js/password-strength-meter.js"></script> 
     18 
     19 
     20 
     21  <!-- Unit Tests --> 
     22  <script src="wp-admin/js/password-strength-meter.js"></script> 
     23</head> 
     24<body> 
     25  <div> 
     26    <h1 id="qunit-header">WordPress QUnit Test Suite</h1> 
     27    <h2 id="qunit-banner"></h2> 
     28    <h2 id="qunit-userAgent"></h2> 
     29    <ol id="qunit-tests"></ol> 
     30    <div id="qunit-fixture"></div> 
     31  </div> 
     32</body> 
     33</html> 
     34 
  • tests/qunit/vendor/qunit.css

    Property changes on: tests/qunit/index.html
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1/** 
     2 * QUnit v1.12.0 - A JavaScript Unit Testing Framework 
     3 * 
     4 * http://qunitjs.com 
     5 * 
     6 * Copyright 2012 jQuery Foundation and other contributors 
     7 * Released under the MIT license. 
     8 * http://jquery.org/license 
     9 */ 
     10 
     11/** Font Family and Sizes */ 
     12 
     13#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 
     14        font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 
     15} 
     16 
     17#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 
     18#qunit-tests { font-size: smaller; } 
     19 
     20 
     21/** Resets */ 
     22 
     23#qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { 
     24        margin: 0; 
     25        padding: 0; 
     26} 
     27 
     28 
     29/** Header */ 
     30 
     31#qunit-header { 
     32        padding: 0.5em 0 0.5em 1em; 
     33 
     34        color: #8699a4; 
     35        background-color: #0d3349; 
     36 
     37        font-size: 1.5em; 
     38        line-height: 1em; 
     39        font-weight: normal; 
     40 
     41        border-radius: 5px 5px 0 0; 
     42        -moz-border-radius: 5px 5px 0 0; 
     43        -webkit-border-top-right-radius: 5px; 
     44        -webkit-border-top-left-radius: 5px; 
     45} 
     46 
     47#qunit-header a { 
     48        text-decoration: none; 
     49        color: #c2ccd1; 
     50} 
     51 
     52#qunit-header a:hover, 
     53#qunit-header a:focus { 
     54        color: #fff; 
     55} 
     56 
     57#qunit-testrunner-toolbar label { 
     58        display: inline-block; 
     59        padding: 0 .5em 0 .1em; 
     60} 
     61 
     62#qunit-banner { 
     63        height: 5px; 
     64} 
     65 
     66#qunit-testrunner-toolbar { 
     67        padding: 0.5em 0 0.5em 2em; 
     68        color: #5E740B; 
     69        background-color: #eee; 
     70        overflow: hidden; 
     71} 
     72 
     73#qunit-userAgent { 
     74        padding: 0.5em 0 0.5em 2.5em; 
     75        background-color: #2b81af; 
     76        color: #fff; 
     77        text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 
     78} 
     79 
     80#qunit-modulefilter-container { 
     81        float: right; 
     82} 
     83 
     84/** Tests: Pass/Fail */ 
     85 
     86#qunit-tests { 
     87        list-style-position: inside; 
     88} 
     89 
     90#qunit-tests li { 
     91        padding: 0.4em 0.5em 0.4em 2.5em; 
     92        border-bottom: 1px solid #fff; 
     93        list-style-position: inside; 
     94} 
     95 
     96#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running  { 
     97        display: none; 
     98} 
     99 
     100#qunit-tests li strong { 
     101        cursor: pointer; 
     102} 
     103 
     104#qunit-tests li a { 
     105        padding: 0.5em; 
     106        color: #c2ccd1; 
     107        text-decoration: none; 
     108} 
     109#qunit-tests li a:hover, 
     110#qunit-tests li a:focus { 
     111        color: #000; 
     112} 
     113 
     114#qunit-tests li .runtime { 
     115        float: right; 
     116        font-size: smaller; 
     117} 
     118 
     119.qunit-assert-list { 
     120        margin-top: 0.5em; 
     121        padding: 0.5em; 
     122 
     123        background-color: #fff; 
     124 
     125        border-radius: 5px; 
     126        -moz-border-radius: 5px; 
     127        -webkit-border-radius: 5px; 
     128} 
     129 
     130.qunit-collapsed { 
     131        display: none; 
     132} 
     133 
     134#qunit-tests table { 
     135        border-collapse: collapse; 
     136        margin-top: .2em; 
     137} 
     138 
     139#qunit-tests th { 
     140        text-align: right; 
     141        vertical-align: top; 
     142        padding: 0 .5em 0 0; 
     143} 
     144 
     145#qunit-tests td { 
     146        vertical-align: top; 
     147} 
     148 
     149#qunit-tests pre { 
     150        margin: 0; 
     151        white-space: pre-wrap; 
     152        word-wrap: break-word; 
     153} 
     154 
     155#qunit-tests del { 
     156        background-color: #e0f2be; 
     157        color: #374e0c; 
     158        text-decoration: none; 
     159} 
     160 
     161#qunit-tests ins { 
     162        background-color: #ffcaca; 
     163        color: #500; 
     164        text-decoration: none; 
     165} 
     166 
     167/*** Test Counts */ 
     168 
     169#qunit-tests b.counts                       { color: black; } 
     170#qunit-tests b.passed                       { color: #5E740B; } 
     171#qunit-tests b.failed                       { color: #710909; } 
     172 
     173#qunit-tests li li { 
     174        padding: 5px; 
     175        background-color: #fff; 
     176        border-bottom: none; 
     177        list-style-position: inside; 
     178} 
     179 
     180/*** Passing Styles */ 
     181 
     182#qunit-tests li li.pass { 
     183        color: #3c510c; 
     184        background-color: #fff; 
     185        border-left: 10px solid #C6E746; 
     186} 
     187 
     188#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; } 
     189#qunit-tests .pass .test-name               { color: #366097; } 
     190 
     191#qunit-tests .pass .test-actual, 
     192#qunit-tests .pass .test-expected           { color: #999999; } 
     193 
     194#qunit-banner.qunit-pass                    { background-color: #C6E746; } 
     195 
     196/*** Failing Styles */ 
     197 
     198#qunit-tests li li.fail { 
     199        color: #710909; 
     200        background-color: #fff; 
     201        border-left: 10px solid #EE5757; 
     202        white-space: pre; 
     203} 
     204 
     205#qunit-tests > li:last-child { 
     206        border-radius: 0 0 5px 5px; 
     207        -moz-border-radius: 0 0 5px 5px; 
     208        -webkit-border-bottom-right-radius: 5px; 
     209        -webkit-border-bottom-left-radius: 5px; 
     210} 
     211 
     212#qunit-tests .fail                          { color: #000000; background-color: #EE5757; } 
     213#qunit-tests .fail .test-name, 
     214#qunit-tests .fail .module-name             { color: #000000; } 
     215 
     216#qunit-tests .fail .test-actual             { color: #EE5757; } 
     217#qunit-tests .fail .test-expected           { color: green;   } 
     218 
     219#qunit-banner.qunit-fail                    { background-color: #EE5757; } 
     220 
     221 
     222/** Result */ 
     223 
     224#qunit-testresult { 
     225        padding: 0.5em 0.5em 0.5em 2.5em; 
     226 
     227        color: #2b81af; 
     228        background-color: #D2E0E6; 
     229 
     230        border-bottom: 1px solid white; 
     231} 
     232#qunit-testresult .module-name { 
     233        font-weight: bold; 
     234} 
     235 
     236/** Fixture */ 
     237 
     238#qunit-fixture { 
     239        position: absolute; 
     240        top: -10000px; 
     241        left: -10000px; 
     242        width: 1000px; 
     243        height: 1000px; 
     244} 
  • tests/qunit/vendor/qunit.js

    Property changes on: tests/qunit/vendor/qunit.css
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1/** 
     2 * QUnit v1.12.0 - A JavaScript Unit Testing Framework 
     3 * 
     4 * http://qunitjs.com 
     5 * 
     6 * Copyright 2013 jQuery Foundation and other contributors 
     7 * Released under the MIT license. 
     8 * https://jquery.org/license/ 
     9 */ 
     10 
     11(function( window ) { 
     12 
     13var QUnit, 
     14        assert, 
     15        config, 
     16        onErrorFnPrev, 
     17        testId = 0, 
     18        fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""), 
     19        toString = Object.prototype.toString, 
     20        hasOwn = Object.prototype.hasOwnProperty, 
     21        // Keep a local reference to Date (GH-283) 
     22        Date = window.Date, 
     23        setTimeout = window.setTimeout, 
     24        defined = { 
     25                setTimeout: typeof window.setTimeout !== "undefined", 
     26                sessionStorage: (function() { 
     27                        var x = "qunit-test-string"; 
     28                        try { 
     29                                sessionStorage.setItem( x, x ); 
     30                                sessionStorage.removeItem( x ); 
     31                                return true; 
     32                        } catch( e ) { 
     33                                return false; 
     34                        } 
     35                }()) 
     36        }, 
     37        /** 
     38         * Provides a normalized error string, correcting an issue 
     39         * with IE 7 (and prior) where Error.prototype.toString is 
     40         * not properly implemented 
     41         * 
     42         * Based on http://es5.github.com/#x15.11.4.4 
     43         * 
     44         * @param {String|Error} error 
     45         * @return {String} error message 
     46         */ 
     47        errorString = function( error ) { 
     48                var name, message, 
     49                        errorString = error.toString(); 
     50                if ( errorString.substring( 0, 7 ) === "[object" ) { 
     51                        name = error.name ? error.name.toString() : "Error"; 
     52                        message = error.message ? error.message.toString() : ""; 
     53                        if ( name && message ) { 
     54                                return name + ": " + message; 
     55                        } else if ( name ) { 
     56                                return name; 
     57                        } else if ( message ) { 
     58                                return message; 
     59                        } else { 
     60                                return "Error"; 
     61                        } 
     62                } else { 
     63                        return errorString; 
     64                } 
     65        }, 
     66        /** 
     67         * Makes a clone of an object using only Array or Object as base, 
     68         * and copies over the own enumerable properties. 
     69         * 
     70         * @param {Object} obj 
     71         * @return {Object} New object with only the own properties (recursively). 
     72         */ 
     73        objectValues = function( obj ) { 
     74                // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392. 
     75                /*jshint newcap: false */ 
     76                var key, val, 
     77                        vals = QUnit.is( "array", obj ) ? [] : {}; 
     78                for ( key in obj ) { 
     79                        if ( hasOwn.call( obj, key ) ) { 
     80                                val = obj[key]; 
     81                                vals[key] = val === Object(val) ? objectValues(val) : val; 
     82                        } 
     83                } 
     84                return vals; 
     85        }; 
     86 
     87function Test( settings ) { 
     88        extend( this, settings ); 
     89        this.assertions = []; 
     90        this.testNumber = ++Test.count; 
     91} 
     92 
     93Test.count = 0; 
     94 
     95Test.prototype = { 
     96        init: function() { 
     97                var a, b, li, 
     98                        tests = id( "qunit-tests" ); 
     99 
     100                if ( tests ) { 
     101                        b = document.createElement( "strong" ); 
     102                        b.innerHTML = this.nameHtml; 
     103 
     104                        // `a` initialized at top of scope 
     105                        a = document.createElement( "a" ); 
     106                        a.innerHTML = "Rerun"; 
     107                        a.href = QUnit.url({ testNumber: this.testNumber }); 
     108 
     109                        li = document.createElement( "li" ); 
     110                        li.appendChild( b ); 
     111                        li.appendChild( a ); 
     112                        li.className = "running"; 
     113                        li.id = this.id = "qunit-test-output" + testId++; 
     114 
     115                        tests.appendChild( li ); 
     116                } 
     117        }, 
     118        setup: function() { 
     119                if ( 
     120                        // Emit moduleStart when we're switching from one module to another 
     121                        this.module !== config.previousModule || 
     122                                // They could be equal (both undefined) but if the previousModule property doesn't 
     123                                // yet exist it means this is the first test in a suite that isn't wrapped in a 
     124                                // module, in which case we'll just emit a moduleStart event for 'undefined'. 
     125                                // Without this, reporters can get testStart before moduleStart  which is a problem. 
     126                                !hasOwn.call( config, "previousModule" ) 
     127                ) { 
     128                        if ( hasOwn.call( config, "previousModule" ) ) { 
     129                                runLoggingCallbacks( "moduleDone", QUnit, { 
     130                                        name: config.previousModule, 
     131                                        failed: config.moduleStats.bad, 
     132                                        passed: config.moduleStats.all - config.moduleStats.bad, 
     133                                        total: config.moduleStats.all 
     134                                }); 
     135                        } 
     136                        config.previousModule = this.module; 
     137                        config.moduleStats = { all: 0, bad: 0 }; 
     138                        runLoggingCallbacks( "moduleStart", QUnit, { 
     139                                name: this.module 
     140                        }); 
     141                } 
     142 
     143                config.current = this; 
     144 
     145                this.testEnvironment = extend({ 
     146                        setup: function() {}, 
     147                        teardown: function() {} 
     148                }, this.moduleTestEnvironment ); 
     149 
     150                this.started = +new Date(); 
     151                runLoggingCallbacks( "testStart", QUnit, { 
     152                        name: this.testName, 
     153                        module: this.module 
     154                }); 
     155 
     156                /*jshint camelcase:false */ 
     157 
     158 
     159                /** 
     160                 * Expose the current test environment. 
     161                 * 
     162                 * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead. 
     163                 */ 
     164                QUnit.current_testEnvironment = this.testEnvironment; 
     165 
     166                /*jshint camelcase:true */ 
     167 
     168                if ( !config.pollution ) { 
     169                        saveGlobal(); 
     170                } 
     171                if ( config.notrycatch ) { 
     172                        this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert ); 
     173                        return; 
     174                } 
     175                try { 
     176                        this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert ); 
     177                } catch( e ) { 
     178                        QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); 
     179                } 
     180        }, 
     181        run: function() { 
     182                config.current = this; 
     183 
     184                var running = id( "qunit-testresult" ); 
     185 
     186                if ( running ) { 
     187                        running.innerHTML = "Running: <br/>" + this.nameHtml; 
     188                } 
     189 
     190                if ( this.async ) { 
     191                        QUnit.stop(); 
     192                } 
     193 
     194                this.callbackStarted = +new Date(); 
     195 
     196                if ( config.notrycatch ) { 
     197                        this.callback.call( this.testEnvironment, QUnit.assert ); 
     198                        this.callbackRuntime = +new Date() - this.callbackStarted; 
     199                        return; 
     200                } 
     201 
     202                try { 
     203                        this.callback.call( this.testEnvironment, QUnit.assert ); 
     204                        this.callbackRuntime = +new Date() - this.callbackStarted; 
     205                } catch( e ) { 
     206                        this.callbackRuntime = +new Date() - this.callbackStarted; 
     207 
     208                        QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); 
     209                        // else next test will carry the responsibility 
     210                        saveGlobal(); 
     211 
     212                        // Restart the tests if they're blocking 
     213                        if ( config.blocking ) { 
     214                                QUnit.start(); 
     215                        } 
     216                } 
     217        }, 
     218        teardown: function() { 
     219                config.current = this; 
     220                if ( config.notrycatch ) { 
     221                        if ( typeof this.callbackRuntime === "undefined" ) { 
     222                                this.callbackRuntime = +new Date() - this.callbackStarted; 
     223                        } 
     224                        this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert ); 
     225                        return; 
     226                } else { 
     227                        try { 
     228                                this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert ); 
     229                        } catch( e ) { 
     230                                QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) ); 
     231                        } 
     232                } 
     233                checkPollution(); 
     234        }, 
     235        finish: function() { 
     236                config.current = this; 
     237                if ( config.requireExpects && this.expected === null ) { 
     238                        QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack ); 
     239                } else if ( this.expected !== null && this.expected !== this.assertions.length ) { 
     240                        QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); 
     241                } else if ( this.expected === null && !this.assertions.length ) { 
     242                        QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack ); 
     243                } 
     244 
     245                var i, assertion, a, b, time, li, ol, 
     246                        test = this, 
     247                        good = 0, 
     248                        bad = 0, 
     249                        tests = id( "qunit-tests" ); 
     250 
     251                this.runtime = +new Date() - this.started; 
     252                config.stats.all += this.assertions.length; 
     253                config.moduleStats.all += this.assertions.length; 
     254 
     255                if ( tests ) { 
     256                        ol = document.createElement( "ol" ); 
     257                        ol.className = "qunit-assert-list"; 
     258 
     259                        for ( i = 0; i < this.assertions.length; i++ ) { 
     260                                assertion = this.assertions[i]; 
     261 
     262                                li = document.createElement( "li" ); 
     263                                li.className = assertion.result ? "pass" : "fail"; 
     264                                li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" ); 
     265                                ol.appendChild( li ); 
     266 
     267                                if ( assertion.result ) { 
     268                                        good++; 
     269                                } else { 
     270                                        bad++; 
     271                                        config.stats.bad++; 
     272                                        config.moduleStats.bad++; 
     273                                } 
     274                        } 
     275 
     276                        // store result when possible 
     277                        if ( QUnit.config.reorder && defined.sessionStorage ) { 
     278                                if ( bad ) { 
     279                                        sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad ); 
     280                                } else { 
     281                                        sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName ); 
     282                                } 
     283                        } 
     284 
     285                        if ( bad === 0 ) { 
     286                                addClass( ol, "qunit-collapsed" ); 
     287                        } 
     288 
     289                        // `b` initialized at top of scope 
     290                        b = document.createElement( "strong" ); 
     291                        b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; 
     292 
     293                        addEvent(b, "click", function() { 
     294                                var next = b.parentNode.lastChild, 
     295                                        collapsed = hasClass( next, "qunit-collapsed" ); 
     296                                ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" ); 
     297                        }); 
     298 
     299                        addEvent(b, "dblclick", function( e ) { 
     300                                var target = e && e.target ? e.target : window.event.srcElement; 
     301                                if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { 
     302                                        target = target.parentNode; 
     303                                } 
     304                                if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 
     305                                        window.location = QUnit.url({ testNumber: test.testNumber }); 
     306                                } 
     307                        }); 
     308 
     309                        // `time` initialized at top of scope 
     310                        time = document.createElement( "span" ); 
     311                        time.className = "runtime"; 
     312                        time.innerHTML = this.runtime + " ms"; 
     313 
     314                        // `li` initialized at top of scope 
     315                        li = id( this.id ); 
     316                        li.className = bad ? "fail" : "pass"; 
     317                        li.removeChild( li.firstChild ); 
     318                        a = li.firstChild; 
     319                        li.appendChild( b ); 
     320                        li.appendChild( a ); 
     321                        li.appendChild( time ); 
     322                        li.appendChild( ol ); 
     323 
     324                } else { 
     325                        for ( i = 0; i < this.assertions.length; i++ ) { 
     326                                if ( !this.assertions[i].result ) { 
     327                                        bad++; 
     328                                        config.stats.bad++; 
     329                                        config.moduleStats.bad++; 
     330                                } 
     331                        } 
     332                } 
     333 
     334                runLoggingCallbacks( "testDone", QUnit, { 
     335                        name: this.testName, 
     336                        module: this.module, 
     337                        failed: bad, 
     338                        passed: this.assertions.length - bad, 
     339                        total: this.assertions.length, 
     340                        duration: this.runtime 
     341                }); 
     342 
     343                QUnit.reset(); 
     344 
     345                config.current = undefined; 
     346        }, 
     347 
     348        queue: function() { 
     349                var bad, 
     350                        test = this; 
     351 
     352                synchronize(function() { 
     353                        test.init(); 
     354                }); 
     355                function run() { 
     356                        // each of these can by async 
     357                        synchronize(function() { 
     358                                test.setup(); 
     359                        }); 
     360                        synchronize(function() { 
     361                                test.run(); 
     362                        }); 
     363                        synchronize(function() { 
     364                                test.teardown(); 
     365                        }); 
     366                        synchronize(function() { 
     367                                test.finish(); 
     368                        }); 
     369                } 
     370 
     371                // `bad` initialized at top of scope 
     372                // defer when previous test run passed, if storage is available 
     373                bad = QUnit.config.reorder && defined.sessionStorage && 
     374                                                +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName ); 
     375 
     376                if ( bad ) { 
     377                        run(); 
     378                } else { 
     379                        synchronize( run, true ); 
     380                } 
     381        } 
     382}; 
     383 
     384// Root QUnit object. 
     385// `QUnit` initialized at top of scope 
     386QUnit = { 
     387 
     388        // call on start of module test to prepend name to all tests 
     389        module: function( name, testEnvironment ) { 
     390                config.currentModule = name; 
     391                config.currentModuleTestEnvironment = testEnvironment; 
     392                config.modules[name] = true; 
     393        }, 
     394 
     395        asyncTest: function( testName, expected, callback ) { 
     396                if ( arguments.length === 2 ) { 
     397                        callback = expected; 
     398                        expected = null; 
     399                } 
     400 
     401                QUnit.test( testName, expected, callback, true ); 
     402        }, 
     403 
     404        test: function( testName, expected, callback, async ) { 
     405                var test, 
     406                        nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>"; 
     407 
     408                if ( arguments.length === 2 ) { 
     409                        callback = expected; 
     410                        expected = null; 
     411                } 
     412 
     413                if ( config.currentModule ) { 
     414                        nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml; 
     415                } 
     416 
     417                test = new Test({ 
     418                        nameHtml: nameHtml, 
     419                        testName: testName, 
     420                        expected: expected, 
     421                        async: async, 
     422                        callback: callback, 
     423                        module: config.currentModule, 
     424                        moduleTestEnvironment: config.currentModuleTestEnvironment, 
     425                        stack: sourceFromStacktrace( 2 ) 
     426                }); 
     427 
     428                if ( !validTest( test ) ) { 
     429                        return; 
     430                } 
     431 
     432                test.queue(); 
     433        }, 
     434 
     435        // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through. 
     436        expect: function( asserts ) { 
     437                if (arguments.length === 1) { 
     438                        config.current.expected = asserts; 
     439                } else { 
     440                        return config.current.expected; 
     441                } 
     442        }, 
     443 
     444        start: function( count ) { 
     445                // QUnit hasn't been initialized yet. 
     446                // Note: RequireJS (et al) may delay onLoad 
     447                if ( config.semaphore === undefined ) { 
     448                        QUnit.begin(function() { 
     449                                // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first 
     450                                setTimeout(function() { 
     451                                        QUnit.start( count ); 
     452                                }); 
     453                        }); 
     454                        return; 
     455                } 
     456 
     457                config.semaphore -= count || 1; 
     458                // don't start until equal number of stop-calls 
     459                if ( config.semaphore > 0 ) { 
     460                        return; 
     461                } 
     462                // ignore if start is called more often then stop 
     463                if ( config.semaphore < 0 ) { 
     464                        config.semaphore = 0; 
     465                        QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) ); 
     466                        return; 
     467                } 
     468                // A slight delay, to avoid any current callbacks 
     469                if ( defined.setTimeout ) { 
     470                        setTimeout(function() { 
     471                                if ( config.semaphore > 0 ) { 
     472                                        return; 
     473                                } 
     474                                if ( config.timeout ) { 
     475                                        clearTimeout( config.timeout ); 
     476                                } 
     477 
     478                                config.blocking = false; 
     479                                process( true ); 
     480                        }, 13); 
     481                } else { 
     482                        config.blocking = false; 
     483                        process( true ); 
     484                } 
     485        }, 
     486 
     487        stop: function( count ) { 
     488                config.semaphore += count || 1; 
     489                config.blocking = true; 
     490 
     491                if ( config.testTimeout && defined.setTimeout ) { 
     492                        clearTimeout( config.timeout ); 
     493                        config.timeout = setTimeout(function() { 
     494                                QUnit.ok( false, "Test timed out" ); 
     495                                config.semaphore = 1; 
     496                                QUnit.start(); 
     497                        }, config.testTimeout ); 
     498                } 
     499        } 
     500}; 
     501 
     502// `assert` initialized at top of scope 
     503// Assert helpers 
     504// All of these must either call QUnit.push() or manually do: 
     505// - runLoggingCallbacks( "log", .. ); 
     506// - config.current.assertions.push({ .. }); 
     507// We attach it to the QUnit object *after* we expose the public API, 
     508// otherwise `assert` will become a global variable in browsers (#341). 
     509assert = { 
     510        /** 
     511         * Asserts rough true-ish result. 
     512         * @name ok 
     513         * @function 
     514         * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 
     515         */ 
     516        ok: function( result, msg ) { 
     517                if ( !config.current ) { 
     518                        throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) ); 
     519                } 
     520                result = !!result; 
     521                msg = msg || (result ? "okay" : "failed" ); 
     522 
     523                var source, 
     524                        details = { 
     525                                module: config.current.module, 
     526                                name: config.current.testName, 
     527                                result: result, 
     528                                message: msg 
     529                        }; 
     530 
     531                msg = "<span class='test-message'>" + escapeText( msg ) + "</span>"; 
     532 
     533                if ( !result ) { 
     534                        source = sourceFromStacktrace( 2 ); 
     535                        if ( source ) { 
     536                                details.source = source; 
     537                                msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>"; 
     538                        } 
     539                } 
     540                runLoggingCallbacks( "log", QUnit, details ); 
     541                config.current.assertions.push({ 
     542                        result: result, 
     543                        message: msg 
     544                }); 
     545        }, 
     546 
     547        /** 
     548         * Assert that the first two arguments are equal, with an optional message. 
     549         * Prints out both actual and expected values. 
     550         * @name equal 
     551         * @function 
     552         * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" ); 
     553         */ 
     554        equal: function( actual, expected, message ) { 
     555                /*jshint eqeqeq:false */ 
     556                QUnit.push( expected == actual, actual, expected, message ); 
     557        }, 
     558 
     559        /** 
     560         * @name notEqual 
     561         * @function 
     562         */ 
     563        notEqual: function( actual, expected, message ) { 
     564                /*jshint eqeqeq:false */ 
     565                QUnit.push( expected != actual, actual, expected, message ); 
     566        }, 
     567 
     568        /** 
     569         * @name propEqual 
     570         * @function 
     571         */ 
     572        propEqual: function( actual, expected, message ) { 
     573                actual = objectValues(actual); 
     574                expected = objectValues(expected); 
     575                QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); 
     576        }, 
     577 
     578        /** 
     579         * @name notPropEqual 
     580         * @function 
     581         */ 
     582        notPropEqual: function( actual, expected, message ) { 
     583                actual = objectValues(actual); 
     584                expected = objectValues(expected); 
     585                QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); 
     586        }, 
     587 
     588        /** 
     589         * @name deepEqual 
     590         * @function 
     591         */ 
     592        deepEqual: function( actual, expected, message ) { 
     593                QUnit.push( QUnit.equiv(actual, expected), actual, expected, message ); 
     594        }, 
     595 
     596        /** 
     597         * @name notDeepEqual 
     598         * @function 
     599         */ 
     600        notDeepEqual: function( actual, expected, message ) { 
     601                QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message ); 
     602        }, 
     603 
     604        /** 
     605         * @name strictEqual 
     606         * @function 
     607         */ 
     608        strictEqual: function( actual, expected, message ) { 
     609                QUnit.push( expected === actual, actual, expected, message ); 
     610        }, 
     611 
     612        /** 
     613         * @name notStrictEqual 
     614         * @function 
     615         */ 
     616        notStrictEqual: function( actual, expected, message ) { 
     617                QUnit.push( expected !== actual, actual, expected, message ); 
     618        }, 
     619 
     620        "throws": function( block, expected, message ) { 
     621                var actual, 
     622                        expectedOutput = expected, 
     623                        ok = false; 
     624 
     625                // 'expected' is optional 
     626                if ( typeof expected === "string" ) { 
     627                        message = expected; 
     628                        expected = null; 
     629                } 
     630 
     631                config.current.ignoreGlobalErrors = true; 
     632                try { 
     633                        block.call( config.current.testEnvironment ); 
     634                } catch (e) { 
     635                        actual = e; 
     636                } 
     637                config.current.ignoreGlobalErrors = false; 
     638 
     639                if ( actual ) { 
     640                        // we don't want to validate thrown error 
     641                        if ( !expected ) { 
     642                                ok = true; 
     643                                expectedOutput = null; 
     644                        // expected is a regexp 
     645                        } else if ( QUnit.objectType( expected ) === "regexp" ) { 
     646                                ok = expected.test( errorString( actual ) ); 
     647                        // expected is a constructor 
     648                        } else if ( actual instanceof expected ) { 
     649                                ok = true; 
     650                        // expected is a validation function which returns true is validation passed 
     651                        } else if ( expected.call( {}, actual ) === true ) { 
     652                                expectedOutput = null; 
     653                                ok = true; 
     654                        } 
     655 
     656                        QUnit.push( ok, actual, expectedOutput, message ); 
     657                } else { 
     658                        QUnit.pushFailure( message, null, "No exception was thrown." ); 
     659                } 
     660        } 
     661}; 
     662 
     663/** 
     664 * @deprecated since 1.8.0 
     665 * Kept assertion helpers in root for backwards compatibility. 
     666 */ 
     667extend( QUnit, assert ); 
     668 
     669/** 
     670 * @deprecated since 1.9.0 
     671 * Kept root "raises()" for backwards compatibility. 
     672 * (Note that we don't introduce assert.raises). 
     673 */ 
     674QUnit.raises = assert[ "throws" ]; 
     675 
     676/** 
     677 * @deprecated since 1.0.0, replaced with error pushes since 1.3.0 
     678 * Kept to avoid TypeErrors for undefined methods. 
     679 */ 
     680QUnit.equals = function() { 
     681        QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" ); 
     682}; 
     683QUnit.same = function() { 
     684        QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" ); 
     685}; 
     686 
     687// We want access to the constructor's prototype 
     688(function() { 
     689        function F() {} 
     690        F.prototype = QUnit; 
     691        QUnit = new F(); 
     692        // Make F QUnit's constructor so that we can add to the prototype later 
     693        QUnit.constructor = F; 
     694}()); 
     695 
     696/** 
     697 * Config object: Maintain internal state 
     698 * Later exposed as QUnit.config 
     699 * `config` initialized at top of scope 
     700 */ 
     701config = { 
     702        // The queue of tests to run 
     703        queue: [], 
     704 
     705        // block until document ready 
     706        blocking: true, 
     707 
     708        // when enabled, show only failing tests 
     709        // gets persisted through sessionStorage and can be changed in UI via checkbox 
     710        hidepassed: false, 
     711 
     712        // by default, run previously failed tests first 
     713        // very useful in combination with "Hide passed tests" checked 
     714        reorder: true, 
     715 
     716        // by default, modify document.title when suite is done 
     717        altertitle: true, 
     718 
     719        // when enabled, all tests must call expect() 
     720        requireExpects: false, 
     721 
     722        // add checkboxes that are persisted in the query-string 
     723        // when enabled, the id is set to `true` as a `QUnit.config` property 
     724        urlConfig: [ 
     725                { 
     726                        id: "noglobals", 
     727                        label: "Check for Globals", 
     728                        tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings." 
     729                }, 
     730                { 
     731                        id: "notrycatch", 
     732                        label: "No try-catch", 
     733                        tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings." 
     734                } 
     735        ], 
     736 
     737        // Set of all modules. 
     738        modules: {}, 
     739 
     740        // logging callback queues 
     741        begin: [], 
     742        done: [], 
     743        log: [], 
     744        testStart: [], 
     745        testDone: [], 
     746        moduleStart: [], 
     747        moduleDone: [] 
     748}; 
     749 
     750// Export global variables, unless an 'exports' object exists, 
     751// in that case we assume we're in CommonJS (dealt with on the bottom of the script) 
     752if ( typeof exports === "undefined" ) { 
     753        extend( window, QUnit.constructor.prototype ); 
     754 
     755        // Expose QUnit object 
     756        window.QUnit = QUnit; 
     757} 
     758 
     759// Initialize more QUnit.config and QUnit.urlParams 
     760(function() { 
     761        var i, 
     762                location = window.location || { search: "", protocol: "file:" }, 
     763                params = location.search.slice( 1 ).split( "&" ), 
     764                length = params.length, 
     765                urlParams = {}, 
     766                current; 
     767 
     768        if ( params[ 0 ] ) { 
     769                for ( i = 0; i < length; i++ ) { 
     770                        current = params[ i ].split( "=" ); 
     771                        current[ 0 ] = decodeURIComponent( current[ 0 ] ); 
     772                        // allow just a key to turn on a flag, e.g., test.html?noglobals 
     773                        current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; 
     774                        urlParams[ current[ 0 ] ] = current[ 1 ]; 
     775                } 
     776        } 
     777 
     778        QUnit.urlParams = urlParams; 
     779 
     780        // String search anywhere in moduleName+testName 
     781        config.filter = urlParams.filter; 
     782 
     783        // Exact match of the module name 
     784        config.module = urlParams.module; 
     785 
     786        config.testNumber = parseInt( urlParams.testNumber, 10 ) || null; 
     787 
     788        // Figure out if we're running the tests from a server or not 
     789        QUnit.isLocal = location.protocol === "file:"; 
     790}()); 
     791 
     792// Extend QUnit object, 
     793// these after set here because they should not be exposed as global functions 
     794extend( QUnit, { 
     795        assert: assert, 
     796 
     797        config: config, 
     798 
     799        // Initialize the configuration options 
     800        init: function() { 
     801                extend( config, { 
     802                        stats: { all: 0, bad: 0 }, 
     803                        moduleStats: { all: 0, bad: 0 }, 
     804                        started: +new Date(), 
     805                        updateRate: 1000, 
     806                        blocking: false, 
     807                        autostart: true, 
     808                        autorun: false, 
     809                        filter: "", 
     810                        queue: [], 
     811                        semaphore: 1 
     812                }); 
     813 
     814                var tests, banner, result, 
     815                        qunit = id( "qunit" ); 
     816 
     817                if ( qunit ) { 
     818                        qunit.innerHTML = 
     819                                "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" + 
     820                                "<h2 id='qunit-banner'></h2>" + 
     821                                "<div id='qunit-testrunner-toolbar'></div>" + 
     822                                "<h2 id='qunit-userAgent'></h2>" + 
     823                                "<ol id='qunit-tests'></ol>"; 
     824                } 
     825 
     826                tests = id( "qunit-tests" ); 
     827                banner = id( "qunit-banner" ); 
     828                result = id( "qunit-testresult" ); 
     829 
     830                if ( tests ) { 
     831                        tests.innerHTML = ""; 
     832                } 
     833 
     834                if ( banner ) { 
     835                        banner.className = ""; 
     836                } 
     837 
     838                if ( result ) { 
     839                        result.parentNode.removeChild( result ); 
     840                } 
     841 
     842                if ( tests ) { 
     843                        result = document.createElement( "p" ); 
     844                        result.id = "qunit-testresult"; 
     845                        result.className = "result"; 
     846                        tests.parentNode.insertBefore( result, tests ); 
     847                        result.innerHTML = "Running...<br/>&nbsp;"; 
     848                } 
     849        }, 
     850 
     851        // Resets the test setup. Useful for tests that modify the DOM. 
     852        /* 
     853        DEPRECATED: Use multiple tests instead of resetting inside a test. 
     854        Use testStart or testDone for custom cleanup. 
     855        This method will throw an error in 2.0, and will be removed in 2.1 
     856        */ 
     857        reset: function() { 
     858                var fixture = id( "qunit-fixture" ); 
     859                if ( fixture ) { 
     860                        fixture.innerHTML = config.fixture; 
     861                } 
     862        }, 
     863 
     864        // Trigger an event on an element. 
     865        // @example triggerEvent( document.body, "click" ); 
     866        triggerEvent: function( elem, type, event ) { 
     867                if ( document.createEvent ) { 
     868                        event = document.createEvent( "MouseEvents" ); 
     869                        event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 
     870                                0, 0, 0, 0, 0, false, false, false, false, 0, null); 
     871 
     872                        elem.dispatchEvent( event ); 
     873                } else if ( elem.fireEvent ) { 
     874                        elem.fireEvent( "on" + type ); 
     875                } 
     876        }, 
     877 
     878        // Safe object type checking 
     879        is: function( type, obj ) { 
     880                return QUnit.objectType( obj ) === type; 
     881        }, 
     882 
     883        objectType: function( obj ) { 
     884                if ( typeof obj === "undefined" ) { 
     885                                return "undefined"; 
     886                // consider: typeof null === object 
     887                } 
     888                if ( obj === null ) { 
     889                                return "null"; 
     890                } 
     891 
     892                var match = toString.call( obj ).match(/^\[object\s(.*)\]$/), 
     893                        type = match && match[1] || ""; 
     894 
     895                switch ( type ) { 
     896                        case "Number": 
     897                                if ( isNaN(obj) ) { 
     898                                        return "nan"; 
     899                                } 
     900                                return "number"; 
     901                        case "String": 
     902                        case "Boolean": 
     903                        case "Array": 
     904                        case "Date": 
     905                        case "RegExp": 
     906                        case "Function": 
     907                                return type.toLowerCase(); 
     908                } 
     909                if ( typeof obj === "object" ) { 
     910                        return "object"; 
     911                } 
     912                return undefined; 
     913        }, 
     914 
     915        push: function( result, actual, expected, message ) { 
     916                if ( !config.current ) { 
     917                        throw new Error( "assertion outside test context, was " + sourceFromStacktrace() ); 
     918                } 
     919 
     920                var output, source, 
     921                        details = { 
     922                                module: config.current.module, 
     923                                name: config.current.testName, 
     924                                result: result, 
     925                                message: message, 
     926                                actual: actual, 
     927                                expected: expected 
     928                        }; 
     929 
     930                message = escapeText( message ) || ( result ? "okay" : "failed" ); 
     931                message = "<span class='test-message'>" + message + "</span>"; 
     932                output = message; 
     933 
     934                if ( !result ) { 
     935                        expected = escapeText( QUnit.jsDump.parse(expected) ); 
     936                        actual = escapeText( QUnit.jsDump.parse(actual) ); 
     937                        output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>"; 
     938 
     939                        if ( actual !== expected ) { 
     940                                output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>"; 
     941                                output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>"; 
     942                        } 
     943 
     944                        source = sourceFromStacktrace(); 
     945 
     946                        if ( source ) { 
     947                                details.source = source; 
     948                                output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>"; 
     949                        } 
     950 
     951                        output += "</table>"; 
     952                } 
     953 
     954                runLoggingCallbacks( "log", QUnit, details ); 
     955 
     956                config.current.assertions.push({ 
     957                        result: !!result, 
     958                        message: output 
     959                }); 
     960        }, 
     961 
     962        pushFailure: function( message, source, actual ) { 
     963                if ( !config.current ) { 
     964                        throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) ); 
     965                } 
     966 
     967                var output, 
     968                        details = { 
     969                                module: config.current.module, 
     970                                name: config.current.testName, 
     971                                result: false, 
     972                                message: message 
     973                        }; 
     974 
     975                message = escapeText( message ) || "error"; 
     976                message = "<span class='test-message'>" + message + "</span>"; 
     977                output = message; 
     978 
     979                output += "<table>"; 
     980 
     981                if ( actual ) { 
     982                        output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>"; 
     983                } 
     984 
     985                if ( source ) { 
     986                        details.source = source; 
     987                        output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>"; 
     988                } 
     989 
     990                output += "</table>"; 
     991 
     992                runLoggingCallbacks( "log", QUnit, details ); 
     993 
     994                config.current.assertions.push({ 
     995                        result: false, 
     996                        message: output 
     997                }); 
     998        }, 
     999 
     1000        url: function( params ) { 
     1001                params = extend( extend( {}, QUnit.urlParams ), params ); 
     1002                var key, 
     1003                        querystring = "?"; 
     1004 
     1005                for ( key in params ) { 
     1006                        if ( hasOwn.call( params, key ) ) { 
     1007                                querystring += encodeURIComponent( key ) + "=" + 
     1008                                        encodeURIComponent( params[ key ] ) + "&"; 
     1009                        } 
     1010                } 
     1011                return window.location.protocol + "//" + window.location.host + 
     1012                        window.location.pathname + querystring.slice( 0, -1 ); 
     1013        }, 
     1014 
     1015        extend: extend, 
     1016        id: id, 
     1017        addEvent: addEvent, 
     1018        addClass: addClass, 
     1019        hasClass: hasClass, 
     1020        removeClass: removeClass 
     1021        // load, equiv, jsDump, diff: Attached later 
     1022}); 
     1023 
     1024/** 
     1025 * @deprecated: Created for backwards compatibility with test runner that set the hook function 
     1026 * into QUnit.{hook}, instead of invoking it and passing the hook function. 
     1027 * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here. 
     1028 * Doing this allows us to tell if the following methods have been overwritten on the actual 
     1029 * QUnit object. 
     1030 */ 
     1031extend( QUnit.constructor.prototype, { 
     1032 
     1033        // Logging callbacks; all receive a single argument with the listed properties 
     1034        // run test/logs.html for any related changes 
     1035        begin: registerLoggingCallback( "begin" ), 
     1036 
     1037        // done: { failed, passed, total, runtime } 
     1038        done: registerLoggingCallback( "done" ), 
     1039 
     1040        // log: { result, actual, expected, message } 
     1041        log: registerLoggingCallback( "log" ), 
     1042 
     1043        // testStart: { name } 
     1044        testStart: registerLoggingCallback( "testStart" ), 
     1045 
     1046        // testDone: { name, failed, passed, total, duration } 
     1047        testDone: registerLoggingCallback( "testDone" ), 
     1048 
     1049        // moduleStart: { name } 
     1050        moduleStart: registerLoggingCallback( "moduleStart" ), 
     1051 
     1052        // moduleDone: { name, failed, passed, total } 
     1053        moduleDone: registerLoggingCallback( "moduleDone" ) 
     1054}); 
     1055 
     1056if ( typeof document === "undefined" || document.readyState === "complete" ) { 
     1057        config.autorun = true; 
     1058} 
     1059 
     1060QUnit.load = function() { 
     1061        runLoggingCallbacks( "begin", QUnit, {} ); 
     1062 
     1063        // Initialize the config, saving the execution queue 
     1064        var banner, filter, i, label, len, main, ol, toolbar, userAgent, val, 
     1065                urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter, 
     1066                numModules = 0, 
     1067                moduleNames = [], 
     1068                moduleFilterHtml = "", 
     1069                urlConfigHtml = "", 
     1070                oldconfig = extend( {}, config ); 
     1071 
     1072        QUnit.init(); 
     1073        extend(config, oldconfig); 
     1074 
     1075        config.blocking = false; 
     1076 
     1077        len = config.urlConfig.length; 
     1078 
     1079        for ( i = 0; i < len; i++ ) { 
     1080                val = config.urlConfig[i]; 
     1081                if ( typeof val === "string" ) { 
     1082                        val = { 
     1083                                id: val, 
     1084                                label: val, 
     1085                                tooltip: "[no tooltip available]" 
     1086                        }; 
     1087                } 
     1088                config[ val.id ] = QUnit.urlParams[ val.id ]; 
     1089                urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) + 
     1090                        "' name='" + escapeText( val.id ) + 
     1091                        "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) + 
     1092                        " title='" + escapeText( val.tooltip ) + 
     1093                        "'><label for='qunit-urlconfig-" + escapeText( val.id ) + 
     1094                        "' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>"; 
     1095        } 
     1096        for ( i in config.modules ) { 
     1097                if ( config.modules.hasOwnProperty( i ) ) { 
     1098                        moduleNames.push(i); 
     1099                } 
     1100        } 
     1101        numModules = moduleNames.length; 
     1102        moduleNames.sort( function( a, b ) { 
     1103                return a.localeCompare( b ); 
     1104        }); 
     1105        moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " + 
     1106                ( config.module === undefined  ? "selected='selected'" : "" ) + 
     1107                ">< All Modules ></option>"; 
     1108 
     1109 
     1110        for ( i = 0; i < numModules; i++) { 
     1111                        moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " + 
     1112                                ( config.module === moduleNames[i] ? "selected='selected'" : "" ) + 
     1113                                ">" + escapeText(moduleNames[i]) + "</option>"; 
     1114        } 
     1115        moduleFilterHtml += "</select>"; 
     1116 
     1117        // `userAgent` initialized at top of scope 
     1118        userAgent = id( "qunit-userAgent" ); 
     1119        if ( userAgent ) { 
     1120                userAgent.innerHTML = navigator.userAgent; 
     1121        } 
     1122 
     1123        // `banner` initialized at top of scope 
     1124        banner = id( "qunit-header" ); 
     1125        if ( banner ) { 
     1126                banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> "; 
     1127        } 
     1128 
     1129        // `toolbar` initialized at top of scope 
     1130        toolbar = id( "qunit-testrunner-toolbar" ); 
     1131        if ( toolbar ) { 
     1132                // `filter` initialized at top of scope 
     1133                filter = document.createElement( "input" ); 
     1134                filter.type = "checkbox"; 
     1135                filter.id = "qunit-filter-pass"; 
     1136 
     1137                addEvent( filter, "click", function() { 
     1138                        var tmp, 
     1139                                ol = document.getElementById( "qunit-tests" ); 
     1140 
     1141                        if ( filter.checked ) { 
     1142                                ol.className = ol.className + " hidepass"; 
     1143                        } else { 
     1144                                tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; 
     1145                                ol.className = tmp.replace( / hidepass /, " " ); 
     1146                        } 
     1147                        if ( defined.sessionStorage ) { 
     1148                                if (filter.checked) { 
     1149                                        sessionStorage.setItem( "qunit-filter-passed-tests", "true" ); 
     1150                                } else { 
     1151                                        sessionStorage.removeItem( "qunit-filter-passed-tests" ); 
     1152                                } 
     1153                        } 
     1154                }); 
     1155 
     1156                if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) { 
     1157                        filter.checked = true; 
     1158                        // `ol` initialized at top of scope 
     1159                        ol = document.getElementById( "qunit-tests" ); 
     1160                        ol.className = ol.className + " hidepass"; 
     1161                } 
     1162                toolbar.appendChild( filter ); 
     1163 
     1164                // `label` initialized at top of scope 
     1165                label = document.createElement( "label" ); 
     1166                label.setAttribute( "for", "qunit-filter-pass" ); 
     1167                label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." ); 
     1168                label.innerHTML = "Hide passed tests"; 
     1169                toolbar.appendChild( label ); 
     1170 
     1171                urlConfigCheckboxesContainer = document.createElement("span"); 
     1172                urlConfigCheckboxesContainer.innerHTML = urlConfigHtml; 
     1173                urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input"); 
     1174                // For oldIE support: 
     1175                // * Add handlers to the individual elements instead of the container 
     1176                // * Use "click" instead of "change" 
     1177                // * Fallback from event.target to event.srcElement 
     1178                addEvents( urlConfigCheckboxes, "click", function( event ) { 
     1179                        var params = {}, 
     1180                                target = event.target || event.srcElement; 
     1181                        params[ target.name ] = target.checked ? true : undefined; 
     1182                        window.location = QUnit.url( params ); 
     1183                }); 
     1184                toolbar.appendChild( urlConfigCheckboxesContainer ); 
     1185 
     1186                if (numModules > 1) { 
     1187                        moduleFilter = document.createElement( "span" ); 
     1188                        moduleFilter.setAttribute( "id", "qunit-modulefilter-container" ); 
     1189                        moduleFilter.innerHTML = moduleFilterHtml; 
     1190                        addEvent( moduleFilter.lastChild, "change", function() { 
     1191                                var selectBox = moduleFilter.getElementsByTagName("select")[0], 
     1192                                        selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value); 
     1193 
     1194                                window.location = QUnit.url({ 
     1195                                        module: ( selectedModule === "" ) ? undefined : selectedModule, 
     1196                                        // Remove any existing filters 
     1197                                        filter: undefined, 
     1198                                        testNumber: undefined 
     1199                                }); 
     1200                        }); 
     1201                        toolbar.appendChild(moduleFilter); 
     1202                } 
     1203        } 
     1204 
     1205        // `main` initialized at top of scope 
     1206        main = id( "qunit-fixture" ); 
     1207        if ( main ) { 
     1208                config.fixture = main.innerHTML; 
     1209        } 
     1210 
     1211        if ( config.autostart ) { 
     1212                QUnit.start(); 
     1213        } 
     1214}; 
     1215 
     1216addEvent( window, "load", QUnit.load ); 
     1217 
     1218// `onErrorFnPrev` initialized at top of scope 
     1219// Preserve other handlers 
     1220onErrorFnPrev = window.onerror; 
     1221 
     1222// Cover uncaught exceptions 
     1223// Returning true will suppress the default browser handler, 
     1224// returning false will let it run. 
     1225window.onerror = function ( error, filePath, linerNr ) { 
     1226        var ret = false; 
     1227        if ( onErrorFnPrev ) { 
     1228                ret = onErrorFnPrev( error, filePath, linerNr ); 
     1229        } 
     1230 
     1231        // Treat return value as window.onerror itself does, 
     1232        // Only do our handling if not suppressed. 
     1233        if ( ret !== true ) { 
     1234                if ( QUnit.config.current ) { 
     1235                        if ( QUnit.config.current.ignoreGlobalErrors ) { 
     1236                                return true; 
     1237                        } 
     1238                        QUnit.pushFailure( error, filePath + ":" + linerNr ); 
     1239                } else { 
     1240                        QUnit.test( "global failure", extend( function() { 
     1241                                QUnit.pushFailure( error, filePath + ":" + linerNr ); 
     1242                        }, { validTest: validTest } ) ); 
     1243                } 
     1244                return false; 
     1245        } 
     1246 
     1247        return ret; 
     1248}; 
     1249 
     1250function done() { 
     1251        config.autorun = true; 
     1252 
     1253        // Log the last module results 
     1254        if ( config.currentModule ) { 
     1255                runLoggingCallbacks( "moduleDone", QUnit, { 
     1256                        name: config.currentModule, 
     1257                        failed: config.moduleStats.bad, 
     1258                        passed: config.moduleStats.all - config.moduleStats.bad, 
     1259                        total: config.moduleStats.all 
     1260                }); 
     1261        } 
     1262        delete config.previousModule; 
     1263 
     1264        var i, key, 
     1265                banner = id( "qunit-banner" ), 
     1266                tests = id( "qunit-tests" ), 
     1267                runtime = +new Date() - config.started, 
     1268                passed = config.stats.all - config.stats.bad, 
     1269                html = [ 
     1270                        "Tests completed in ", 
     1271                        runtime, 
     1272                        " milliseconds.<br/>", 
     1273                        "<span class='passed'>", 
     1274                        passed, 
     1275                        "</span> assertions of <span class='total'>", 
     1276                        config.stats.all, 
     1277                        "</span> passed, <span class='failed'>", 
     1278                        config.stats.bad, 
     1279                        "</span> failed." 
     1280                ].join( "" ); 
     1281 
     1282        if ( banner ) { 
     1283                banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" ); 
     1284        } 
     1285 
     1286        if ( tests ) { 
     1287                id( "qunit-testresult" ).innerHTML = html; 
     1288        } 
     1289 
     1290        if ( config.altertitle && typeof document !== "undefined" && document.title ) { 
     1291                // show ✖ for good, ✔ for bad suite result in title 
     1292                // use escape sequences in case file gets loaded with non-utf-8-charset 
     1293                document.title = [ 
     1294                        ( config.stats.bad ? "\u2716" : "\u2714" ), 
     1295                        document.title.replace( /^[\u2714\u2716] /i, "" ) 
     1296                ].join( " " ); 
     1297        } 
     1298 
     1299        // clear own sessionStorage items if all tests passed 
     1300        if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) { 
     1301                // `key` & `i` initialized at top of scope 
     1302                for ( i = 0; i < sessionStorage.length; i++ ) { 
     1303                        key = sessionStorage.key( i++ ); 
     1304                        if ( key.indexOf( "qunit-test-" ) === 0 ) { 
     1305                                sessionStorage.removeItem( key ); 
     1306                        } 
     1307                } 
     1308        } 
     1309 
     1310        // scroll back to top to show results 
     1311        if ( window.scrollTo ) { 
     1312                window.scrollTo(0, 0); 
     1313        } 
     1314 
     1315        runLoggingCallbacks( "done", QUnit, { 
     1316                failed: config.stats.bad, 
     1317                passed: passed, 
     1318                total: config.stats.all, 
     1319                runtime: runtime 
     1320        }); 
     1321} 
     1322 
     1323/** @return Boolean: true if this test should be ran */ 
     1324function validTest( test ) { 
     1325        var include, 
     1326                filter = config.filter && config.filter.toLowerCase(), 
     1327                module = config.module && config.module.toLowerCase(), 
     1328                fullName = (test.module + ": " + test.testName).toLowerCase(); 
     1329 
     1330        // Internally-generated tests are always valid 
     1331        if ( test.callback && test.callback.validTest === validTest ) { 
     1332                delete test.callback.validTest; 
     1333                return true; 
     1334        } 
     1335 
     1336        if ( config.testNumber ) { 
     1337                return test.testNumber === config.testNumber; 
     1338        } 
     1339 
     1340        if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) { 
     1341                return false; 
     1342        } 
     1343 
     1344        if ( !filter ) { 
     1345                return true; 
     1346        } 
     1347 
     1348        include = filter.charAt( 0 ) !== "!"; 
     1349        if ( !include ) { 
     1350                filter = filter.slice( 1 ); 
     1351        } 
     1352 
     1353        // If the filter matches, we need to honour include 
     1354        if ( fullName.indexOf( filter ) !== -1 ) { 
     1355                return include; 
     1356        } 
     1357 
     1358        // Otherwise, do the opposite 
     1359        return !include; 
     1360} 
     1361 
     1362// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions) 
     1363// Later Safari and IE10 are supposed to support error.stack as well 
     1364// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack 
     1365function extractStacktrace( e, offset ) { 
     1366        offset = offset === undefined ? 3 : offset; 
     1367 
     1368        var stack, include, i; 
     1369 
     1370        if ( e.stacktrace ) { 
     1371                // Opera 
     1372                return e.stacktrace.split( "\n" )[ offset + 3 ]; 
     1373        } else if ( e.stack ) { 
     1374                // Firefox, Chrome 
     1375                stack = e.stack.split( "\n" ); 
     1376                if (/^error$/i.test( stack[0] ) ) { 
     1377                        stack.shift(); 
     1378                } 
     1379                if ( fileName ) { 
     1380                        include = []; 
     1381                        for ( i = offset; i < stack.length; i++ ) { 
     1382                                if ( stack[ i ].indexOf( fileName ) !== -1 ) { 
     1383                                        break; 
     1384                                } 
     1385                                include.push( stack[ i ] ); 
     1386                        } 
     1387                        if ( include.length ) { 
     1388                                return include.join( "\n" ); 
     1389                        } 
     1390                } 
     1391                return stack[ offset ]; 
     1392        } else if ( e.sourceURL ) { 
     1393                // Safari, PhantomJS 
     1394                // hopefully one day Safari provides actual stacktraces 
     1395                // exclude useless self-reference for generated Error objects 
     1396                if ( /qunit.js$/.test( e.sourceURL ) ) { 
     1397                        return; 
     1398                } 
     1399                // for actual exceptions, this is useful 
     1400                return e.sourceURL + ":" + e.line; 
     1401        } 
     1402} 
     1403function sourceFromStacktrace( offset ) { 
     1404        try { 
     1405                throw new Error(); 
     1406        } catch ( e ) { 
     1407                return extractStacktrace( e, offset ); 
     1408        } 
     1409} 
     1410 
     1411/** 
     1412 * Escape text for attribute or text content. 
     1413 */ 
     1414function escapeText( s ) { 
     1415        if ( !s ) { 
     1416                return ""; 
     1417        } 
     1418        s = s + ""; 
     1419        // Both single quotes and double quotes (for attributes) 
     1420        return s.replace( /['"<>&]/g, function( s ) { 
     1421                switch( s ) { 
     1422                        case "'": 
     1423                                return "&#039;"; 
     1424                        case "\"": 
     1425                                return "&quot;"; 
     1426                        case "<": 
     1427                                return "&lt;"; 
     1428                        case ">": 
     1429                                return "&gt;"; 
     1430                        case "&": 
     1431                                return "&amp;"; 
     1432                } 
     1433        }); 
     1434} 
     1435 
     1436function synchronize( callback, last ) { 
     1437        config.queue.push( callback ); 
     1438 
     1439        if ( config.autorun && !config.blocking ) { 
     1440                process( last ); 
     1441        } 
     1442} 
     1443 
     1444function process( last ) { 
     1445        function next() { 
     1446                process( last ); 
     1447        } 
     1448        var start = new Date().getTime(); 
     1449        config.depth = config.depth ? config.depth + 1 : 1; 
     1450 
     1451        while ( config.queue.length && !config.blocking ) { 
     1452                if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { 
     1453                        config.queue.shift()(); 
     1454                } else { 
     1455                        setTimeout( next, 13 ); 
     1456                        break; 
     1457                } 
     1458        } 
     1459        config.depth--; 
     1460        if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { 
     1461                done(); 
     1462        } 
     1463} 
     1464 
     1465function saveGlobal() { 
     1466        config.pollution = []; 
     1467 
     1468        if ( config.noglobals ) { 
     1469                for ( var key in window ) { 
     1470                        if ( hasOwn.call( window, key ) ) { 
     1471                                // in Opera sometimes DOM element ids show up here, ignore them 
     1472                                if ( /^qunit-test-output/.test( key ) ) { 
     1473                                        continue; 
     1474                                } 
     1475                                config.pollution.push( key ); 
     1476                        } 
     1477                } 
     1478        } 
     1479} 
     1480 
     1481function checkPollution() { 
     1482        var newGlobals, 
     1483                deletedGlobals, 
     1484                old = config.pollution; 
     1485 
     1486        saveGlobal(); 
     1487 
     1488        newGlobals = diff( config.pollution, old ); 
     1489        if ( newGlobals.length > 0 ) { 
     1490                QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") ); 
     1491        } 
     1492 
     1493        deletedGlobals = diff( old, config.pollution ); 
     1494        if ( deletedGlobals.length > 0 ) { 
     1495                QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") ); 
     1496        } 
     1497} 
     1498 
     1499// returns a new Array with the elements that are in a but not in b 
     1500function diff( a, b ) { 
     1501        var i, j, 
     1502                result = a.slice(); 
     1503 
     1504        for ( i = 0; i < result.length; i++ ) { 
     1505                for ( j = 0; j < b.length; j++ ) { 
     1506                        if ( result[i] === b[j] ) { 
     1507                                result.splice( i, 1 ); 
     1508                                i--; 
     1509                                break; 
     1510                        } 
     1511                } 
     1512        } 
     1513        return result; 
     1514} 
     1515 
     1516function extend( a, b ) { 
     1517        for ( var prop in b ) { 
     1518                if ( hasOwn.call( b, prop ) ) { 
     1519                        // Avoid "Member not found" error in IE8 caused by messing with window.constructor 
     1520                        if ( !( prop === "constructor" && a === window ) ) { 
     1521                                if ( b[ prop ] === undefined ) { 
     1522                                        delete a[ prop ]; 
     1523                                } else { 
     1524                                        a[ prop ] = b[ prop ]; 
     1525                                } 
     1526                        } 
     1527                } 
     1528        } 
     1529 
     1530        return a; 
     1531} 
     1532 
     1533/** 
     1534 * @param {HTMLElement} elem 
     1535 * @param {string} type 
     1536 * @param {Function} fn 
     1537 */ 
     1538function addEvent( elem, type, fn ) { 
     1539        // Standards-based browsers 
     1540        if ( elem.addEventListener ) { 
     1541                elem.addEventListener( type, fn, false ); 
     1542        // IE 
     1543        } else { 
     1544                elem.attachEvent( "on" + type, fn ); 
     1545        } 
     1546} 
     1547 
     1548/** 
     1549 * @param {Array|NodeList} elems 
     1550 * @param {string} type 
     1551 * @param {Function} fn 
     1552 */ 
     1553function addEvents( elems, type, fn ) { 
     1554        var i = elems.length; 
     1555        while ( i-- ) { 
     1556                addEvent( elems[i], type, fn ); 
     1557        } 
     1558} 
     1559 
     1560function hasClass( elem, name ) { 
     1561        return (" " + elem.className + " ").indexOf(" " + name + " ") > -1; 
     1562} 
     1563 
     1564function addClass( elem, name ) { 
     1565        if ( !hasClass( elem, name ) ) { 
     1566                elem.className += (elem.className ? " " : "") + name; 
     1567        } 
     1568} 
     1569 
     1570function removeClass( elem, name ) { 
     1571        var set = " " + elem.className + " "; 
     1572        // Class name may appear multiple times 
     1573        while ( set.indexOf(" " + name + " ") > -1 ) { 
     1574                set = set.replace(" " + name + " " , " "); 
     1575        } 
     1576        // If possible, trim it for prettiness, but not necessarily 
     1577        elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, ""); 
     1578} 
     1579 
     1580function id( name ) { 
     1581        return !!( typeof document !== "undefined" && document && document.getElementById ) && 
     1582                document.getElementById( name ); 
     1583} 
     1584 
     1585function registerLoggingCallback( key ) { 
     1586        return function( callback ) { 
     1587                config[key].push( callback ); 
     1588        }; 
     1589} 
     1590 
     1591// Supports deprecated method of completely overwriting logging callbacks 
     1592function runLoggingCallbacks( key, scope, args ) { 
     1593        var i, callbacks; 
     1594        if ( QUnit.hasOwnProperty( key ) ) { 
     1595                QUnit[ key ].call(scope, args ); 
     1596        } else { 
     1597                callbacks = config[ key ]; 
     1598                for ( i = 0; i < callbacks.length; i++ ) { 
     1599                        callbacks[ i ].call( scope, args ); 
     1600                } 
     1601        } 
     1602} 
     1603 
     1604// Test for equality any JavaScript type. 
     1605// Author: Philippe Rathé <prathe@gmail.com> 
     1606QUnit.equiv = (function() { 
     1607 
     1608        // Call the o related callback with the given arguments. 
     1609        function bindCallbacks( o, callbacks, args ) { 
     1610                var prop = QUnit.objectType( o ); 
     1611                if ( prop ) { 
     1612                        if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) { 
     1613                                return callbacks[ prop ].apply( callbacks, args ); 
     1614                        } else { 
     1615                                return callbacks[ prop ]; // or undefined 
     1616                        } 
     1617                } 
     1618        } 
     1619 
     1620        // the real equiv function 
     1621        var innerEquiv, 
     1622                // stack to decide between skip/abort functions 
     1623                callers = [], 
     1624                // stack to avoiding loops from circular referencing 
     1625                parents = [], 
     1626                parentsB = [], 
     1627 
     1628                getProto = Object.getPrototypeOf || function ( obj ) { 
     1629                        /*jshint camelcase:false */ 
     1630                        return obj.__proto__; 
     1631                }, 
     1632                callbacks = (function () { 
     1633 
     1634                        // for string, boolean, number and null 
     1635                        function useStrictEquality( b, a ) { 
     1636                                /*jshint eqeqeq:false */ 
     1637                                if ( b instanceof a.constructor || a instanceof b.constructor ) { 
     1638                                        // to catch short annotation VS 'new' annotation of a 
     1639                                        // declaration 
     1640                                        // e.g. var i = 1; 
     1641                                        // var j = new Number(1); 
     1642                                        return a == b; 
     1643                                } else { 
     1644                                        return a === b; 
     1645                                } 
     1646                        } 
     1647 
     1648                        return { 
     1649                                "string": useStrictEquality, 
     1650                                "boolean": useStrictEquality, 
     1651                                "number": useStrictEquality, 
     1652                                "null": useStrictEquality, 
     1653                                "undefined": useStrictEquality, 
     1654 
     1655                                "nan": function( b ) { 
     1656                                        return isNaN( b ); 
     1657                                }, 
     1658 
     1659                                "date": function( b, a ) { 
     1660                                        return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf(); 
     1661                                }, 
     1662 
     1663                                "regexp": function( b, a ) { 
     1664                                        return QUnit.objectType( b ) === "regexp" && 
     1665                                                // the regex itself 
     1666                                                a.source === b.source && 
     1667                                                // and its modifiers 
     1668                                                a.global === b.global && 
     1669                                                // (gmi) ... 
     1670                                                a.ignoreCase === b.ignoreCase && 
     1671                                                a.multiline === b.multiline && 
     1672                                                a.sticky === b.sticky; 
     1673                                }, 
     1674 
     1675                                // - skip when the property is a method of an instance (OOP) 
     1676                                // - abort otherwise, 
     1677                                // initial === would have catch identical references anyway 
     1678                                "function": function() { 
     1679                                        var caller = callers[callers.length - 1]; 
     1680                                        return caller !== Object && typeof caller !== "undefined"; 
     1681                                }, 
     1682 
     1683                                "array": function( b, a ) { 
     1684                                        var i, j, len, loop, aCircular, bCircular; 
     1685 
     1686                                        // b could be an object literal here 
     1687                                        if ( QUnit.objectType( b ) !== "array" ) { 
     1688                                                return false; 
     1689                                        } 
     1690 
     1691                                        len = a.length; 
     1692                                        if ( len !== b.length ) { 
     1693                                                // safe and faster 
     1694                                                return false; 
     1695                                        } 
     1696 
     1697                                        // track reference to avoid circular references 
     1698                                        parents.push( a ); 
     1699                                        parentsB.push( b ); 
     1700                                        for ( i = 0; i < len; i++ ) { 
     1701                                                loop = false; 
     1702                                                for ( j = 0; j < parents.length; j++ ) { 
     1703                                                        aCircular = parents[j] === a[i]; 
     1704                                                        bCircular = parentsB[j] === b[i]; 
     1705                                                        if ( aCircular || bCircular ) { 
     1706                                                                if ( a[i] === b[i] || aCircular && bCircular ) { 
     1707                                                                        loop = true; 
     1708                                                                } else { 
     1709                                                                        parents.pop(); 
     1710                                                                        parentsB.pop(); 
     1711                                                                        return false; 
     1712                                                                } 
     1713                                                        } 
     1714                                                } 
     1715                                                if ( !loop && !innerEquiv(a[i], b[i]) ) { 
     1716                                                        parents.pop(); 
     1717                                                        parentsB.pop(); 
     1718                                                        return false; 
     1719                                                } 
     1720                                        } 
     1721                                        parents.pop(); 
     1722                                        parentsB.pop(); 
     1723                                        return true; 
     1724                                }, 
     1725 
     1726                                "object": function( b, a ) { 
     1727                                        /*jshint forin:false */ 
     1728                                        var i, j, loop, aCircular, bCircular, 
     1729                                                // Default to true 
     1730                                                eq = true, 
     1731                                                aProperties = [], 
     1732                                                bProperties = []; 
     1733 
     1734                                        // comparing constructors is more strict than using 
     1735                                        // instanceof 
     1736                                        if ( a.constructor !== b.constructor ) { 
     1737                                                // Allow objects with no prototype to be equivalent to 
     1738                                                // objects with Object as their constructor. 
     1739                                                if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) || 
     1740                                                        ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) { 
     1741                                                                return false; 
     1742                                                } 
     1743                                        } 
     1744 
     1745                                        // stack constructor before traversing properties 
     1746                                        callers.push( a.constructor ); 
     1747 
     1748                                        // track reference to avoid circular references 
     1749                                        parents.push( a ); 
     1750                                        parentsB.push( b ); 
     1751 
     1752                                        // be strict: don't ensure hasOwnProperty and go deep 
     1753                                        for ( i in a ) { 
     1754                                                loop = false; 
     1755                                                for ( j = 0; j < parents.length; j++ ) { 
     1756                                                        aCircular = parents[j] === a[i]; 
     1757                                                        bCircular = parentsB[j] === b[i]; 
     1758                                                        if ( aCircular || bCircular ) { 
     1759                                                                if ( a[i] === b[i] || aCircular && bCircular ) { 
     1760                                                                        loop = true; 
     1761                                                                } else { 
     1762                                                                        eq = false; 
     1763                                                                        break; 
     1764                                                                } 
     1765                                                        } 
     1766                                                } 
     1767                                                aProperties.push(i); 
     1768                                                if ( !loop && !innerEquiv(a[i], b[i]) ) { 
     1769                                                        eq = false; 
     1770                                                        break; 
     1771                                                } 
     1772                                        } 
     1773 
     1774                                        parents.pop(); 
     1775                                        parentsB.pop(); 
     1776                                        callers.pop(); // unstack, we are done 
     1777 
     1778                                        for ( i in b ) { 
     1779                                                bProperties.push( i ); // collect b's properties 
     1780                                        } 
     1781 
     1782                                        // Ensures identical properties name 
     1783                                        return eq && innerEquiv( aProperties.sort(), bProperties.sort() ); 
     1784                                } 
     1785                        }; 
     1786                }()); 
     1787 
     1788        innerEquiv = function() { // can take multiple arguments 
     1789                var args = [].slice.apply( arguments ); 
     1790                if ( args.length < 2 ) { 
     1791                        return true; // end transition 
     1792                } 
     1793 
     1794                return (function( a, b ) { 
     1795                        if ( a === b ) { 
     1796                                return true; // catch the most you can 
     1797                        } else if ( a === null || b === null || typeof a === "undefined" || 
     1798                                        typeof b === "undefined" || 
     1799                                        QUnit.objectType(a) !== QUnit.objectType(b) ) { 
     1800                                return false; // don't lose time with error prone cases 
     1801                        } else { 
     1802                                return bindCallbacks(a, callbacks, [ b, a ]); 
     1803                        } 
     1804 
     1805                        // apply transition with (1..n) arguments 
     1806                }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) ); 
     1807        }; 
     1808 
     1809        return innerEquiv; 
     1810}()); 
     1811 
     1812/** 
     1813 * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | 
     1814 * http://flesler.blogspot.com Licensed under BSD 
     1815 * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 
     1816 * 
     1817 * @projectDescription Advanced and extensible data dumping for Javascript. 
     1818 * @version 1.0.0 
     1819 * @author Ariel Flesler 
     1820 * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} 
     1821 */ 
     1822QUnit.jsDump = (function() { 
     1823        function quote( str ) { 
     1824                return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\""; 
     1825        } 
     1826        function literal( o ) { 
     1827                return o + ""; 
     1828        } 
     1829        function join( pre, arr, post ) { 
     1830                var s = jsDump.separator(), 
     1831                        base = jsDump.indent(), 
     1832                        inner = jsDump.indent(1); 
     1833                if ( arr.join ) { 
     1834                        arr = arr.join( "," + s + inner ); 
     1835                } 
     1836                if ( !arr ) { 
     1837                        return pre + post; 
     1838                } 
     1839                return [ pre, inner + arr, base + post ].join(s); 
     1840        } 
     1841        function array( arr, stack ) { 
     1842                var i = arr.length, ret = new Array(i); 
     1843                this.up(); 
     1844                while ( i-- ) { 
     1845                        ret[i] = this.parse( arr[i] , undefined , stack); 
     1846                } 
     1847                this.down(); 
     1848                return join( "[", ret, "]" ); 
     1849        } 
     1850 
     1851        var reName = /^function (\w+)/, 
     1852                jsDump = { 
     1853                        // type is used mostly internally, you can fix a (custom)type in advance 
     1854                        parse: function( obj, type, stack ) { 
     1855                                stack = stack || [ ]; 
     1856                                var inStack, res, 
     1857                                        parser = this.parsers[ type || this.typeOf(obj) ]; 
     1858 
     1859                                type = typeof parser; 
     1860                                inStack = inArray( obj, stack ); 
     1861 
     1862                                if ( inStack !== -1 ) { 
     1863                                        return "recursion(" + (inStack - stack.length) + ")"; 
     1864                                } 
     1865                                if ( type === "function" )  { 
     1866                                        stack.push( obj ); 
     1867                                        res = parser.call( this, obj, stack ); 
     1868                                        stack.pop(); 
     1869                                        return res; 
     1870                                } 
     1871                                return ( type === "string" ) ? parser : this.parsers.error; 
     1872                        }, 
     1873                        typeOf: function( obj ) { 
     1874                                var type; 
     1875                                if ( obj === null ) { 
     1876                                        type = "null"; 
     1877                                } else if ( typeof obj === "undefined" ) { 
     1878                                        type = "undefined"; 
     1879                                } else if ( QUnit.is( "regexp", obj) ) { 
     1880                                        type = "regexp"; 
     1881                                } else if ( QUnit.is( "date", obj) ) { 
     1882                                        type = "date"; 
     1883                                } else if ( QUnit.is( "function", obj) ) { 
     1884                                        type = "function"; 
     1885                                } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) { 
     1886                                        type = "window"; 
     1887                                } else if ( obj.nodeType === 9 ) { 
     1888                                        type = "document"; 
     1889                                } else if ( obj.nodeType ) { 
     1890                                        type = "node"; 
     1891                                } else if ( 
     1892                                        // native arrays 
     1893                                        toString.call( obj ) === "[object Array]" || 
     1894                                        // NodeList objects 
     1895                                        ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) 
     1896                                ) { 
     1897                                        type = "array"; 
     1898                                } else if ( obj.constructor === Error.prototype.constructor ) { 
     1899                                        type = "error"; 
     1900                                } else { 
     1901                                        type = typeof obj; 
     1902                                } 
     1903                                return type; 
     1904                        }, 
     1905                        separator: function() { 
     1906                                return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " "; 
     1907                        }, 
     1908                        // extra can be a number, shortcut for increasing-calling-decreasing 
     1909                        indent: function( extra ) { 
     1910                                if ( !this.multiline ) { 
     1911                                        return ""; 
     1912                                } 
     1913                                var chr = this.indentChar; 
     1914                                if ( this.HTML ) { 
     1915                                        chr = chr.replace( /\t/g, "   " ).replace( / /g, "&nbsp;" ); 
     1916                                } 
     1917                                return new Array( this.depth + ( extra || 0 ) ).join(chr); 
     1918                        }, 
     1919                        up: function( a ) { 
     1920                                this.depth += a || 1; 
     1921                        }, 
     1922                        down: function( a ) { 
     1923                                this.depth -= a || 1; 
     1924                        }, 
     1925                        setParser: function( name, parser ) { 
     1926                                this.parsers[name] = parser; 
     1927                        }, 
     1928                        // The next 3 are exposed so you can use them 
     1929                        quote: quote, 
     1930                        literal: literal, 
     1931                        join: join, 
     1932                        // 
     1933                        depth: 1, 
     1934                        // This is the list of parsers, to modify them, use jsDump.setParser 
     1935                        parsers: { 
     1936                                window: "[Window]", 
     1937                                document: "[Document]", 
     1938                                error: function(error) { 
     1939                                        return "Error(\"" + error.message + "\")"; 
     1940                                }, 
     1941                                unknown: "[Unknown]", 
     1942                                "null": "null", 
     1943                                "undefined": "undefined", 
     1944                                "function": function( fn ) { 
     1945                                        var ret = "function", 
     1946                                                // functions never have name in IE 
     1947                                                name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1]; 
     1948 
     1949                                        if ( name ) { 
     1950                                                ret += " " + name; 
     1951                                        } 
     1952                                        ret += "( "; 
     1953 
     1954                                        ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" ); 
     1955                                        return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" ); 
     1956                                }, 
     1957                                array: array, 
     1958                                nodelist: array, 
     1959                                "arguments": array, 
     1960                                object: function( map, stack ) { 
     1961                                        /*jshint forin:false */ 
     1962                                        var ret = [ ], keys, key, val, i; 
     1963                                        QUnit.jsDump.up(); 
     1964                                        keys = []; 
     1965                                        for ( key in map ) { 
     1966                                                keys.push( key ); 
     1967                                        } 
     1968                                        keys.sort(); 
     1969                                        for ( i = 0; i < keys.length; i++ ) { 
     1970                                                key = keys[ i ]; 
     1971                                                val = map[ key ]; 
     1972                                                ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) ); 
     1973                                        } 
     1974                                        QUnit.jsDump.down(); 
     1975                                        return join( "{", ret, "}" ); 
     1976                                }, 
     1977                                node: function( node ) { 
     1978                                        var len, i, val, 
     1979                                                open = QUnit.jsDump.HTML ? "&lt;" : "<", 
     1980                                                close = QUnit.jsDump.HTML ? "&gt;" : ">", 
     1981                                                tag = node.nodeName.toLowerCase(), 
     1982                                                ret = open + tag, 
     1983                                                attrs = node.attributes; 
     1984 
     1985                                        if ( attrs ) { 
     1986                                                for ( i = 0, len = attrs.length; i < len; i++ ) { 
     1987                                                        val = attrs[i].nodeValue; 
     1988                                                        // IE6 includes all attributes in .attributes, even ones not explicitly set. 
     1989                                                        // Those have values like undefined, null, 0, false, "" or "inherit". 
     1990                                                        if ( val && val !== "inherit" ) { 
     1991                                                                ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" ); 
     1992                                                        } 
     1993                                                } 
     1994                                        } 
     1995                                        ret += close; 
     1996 
     1997                                        // Show content of TextNode or CDATASection 
     1998                                        if ( node.nodeType === 3 || node.nodeType === 4 ) { 
     1999                                                ret += node.nodeValue; 
     2000                                        } 
     2001 
     2002                                        return ret + open + "/" + tag + close; 
     2003                                }, 
     2004                                // function calls it internally, it's the arguments part of the function 
     2005                                functionArgs: function( fn ) { 
     2006                                        var args, 
     2007                                                l = fn.length; 
     2008 
     2009                                        if ( !l ) { 
     2010                                                return ""; 
     2011                                        } 
     2012 
     2013                                        args = new Array(l); 
     2014                                        while ( l-- ) { 
     2015                                                // 97 is 'a' 
     2016                                                args[l] = String.fromCharCode(97+l); 
     2017                                        } 
     2018                                        return " " + args.join( ", " ) + " "; 
     2019                                }, 
     2020                                // object calls it internally, the key part of an item in a map 
     2021                                key: quote, 
     2022                                // function calls it internally, it's the content of the function 
     2023                                functionCode: "[code]", 
     2024                                // node calls it internally, it's an html attribute value 
     2025                                attribute: quote, 
     2026                                string: quote, 
     2027                                date: quote, 
     2028                                regexp: literal, 
     2029                                number: literal, 
     2030                                "boolean": literal 
     2031                        }, 
     2032                        // if true, entities are escaped ( <, >, \t, space and \n ) 
     2033                        HTML: false, 
     2034                        // indentation unit 
     2035                        indentChar: "  ", 
     2036                        // if true, items in a collection, are separated by a \n, else just a space. 
     2037                        multiline: true 
     2038                }; 
     2039 
     2040        return jsDump; 
     2041}()); 
     2042 
     2043// from jquery.js 
     2044function inArray( elem, array ) { 
     2045        if ( array.indexOf ) { 
     2046                return array.indexOf( elem ); 
     2047        } 
     2048 
     2049        for ( var i = 0, length = array.length; i < length; i++ ) { 
     2050                if ( array[ i ] === elem ) { 
     2051                        return i; 
     2052                } 
     2053        } 
     2054 
     2055        return -1; 
     2056} 
     2057 
     2058/* 
     2059 * Javascript Diff Algorithm 
     2060 *  By John Resig (http://ejohn.org/) 
     2061 *  Modified by Chu Alan "sprite" 
     2062 * 
     2063 * Released under the MIT license. 
     2064 * 
     2065 * More Info: 
     2066 *  http://ejohn.org/projects/javascript-diff-algorithm/ 
     2067 * 
     2068 * Usage: QUnit.diff(expected, actual) 
     2069 * 
     2070 * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the  quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" 
     2071 */ 
     2072QUnit.diff = (function() { 
     2073        /*jshint eqeqeq:false, eqnull:true */ 
     2074        function diff( o, n ) { 
     2075                var i, 
     2076                        ns = {}, 
     2077                        os = {}; 
     2078 
     2079                for ( i = 0; i < n.length; i++ ) { 
     2080                        if ( !hasOwn.call( ns, n[i] ) ) { 
     2081                                ns[ n[i] ] = { 
     2082                                        rows: [], 
     2083                                        o: null 
     2084                                }; 
     2085                        } 
     2086                        ns[ n[i] ].rows.push( i ); 
     2087                } 
     2088 
     2089                for ( i = 0; i < o.length; i++ ) { 
     2090                        if ( !hasOwn.call( os, o[i] ) ) { 
     2091                                os[ o[i] ] = { 
     2092                                        rows: [], 
     2093                                        n: null 
     2094                                }; 
     2095                        } 
     2096                        os[ o[i] ].rows.push( i ); 
     2097                } 
     2098 
     2099                for ( i in ns ) { 
     2100                        if ( hasOwn.call( ns, i ) ) { 
     2101                                if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) { 
     2102                                        n[ ns[i].rows[0] ] = { 
     2103                                                text: n[ ns[i].rows[0] ], 
     2104                                                row: os[i].rows[0] 
     2105                                        }; 
     2106                                        o[ os[i].rows[0] ] = { 
     2107                                                text: o[ os[i].rows[0] ], 
     2108                                                row: ns[i].rows[0] 
     2109                                        }; 
     2110                                } 
     2111                        } 
     2112                } 
     2113 
     2114                for ( i = 0; i < n.length - 1; i++ ) { 
     2115                        if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && 
     2116                                                n[ i + 1 ] == o[ n[i].row + 1 ] ) { 
     2117 
     2118                                n[ i + 1 ] = { 
     2119                                        text: n[ i + 1 ], 
     2120                                        row: n[i].row + 1 
     2121                                }; 
     2122                                o[ n[i].row + 1 ] = { 
     2123                                        text: o[ n[i].row + 1 ], 
     2124                                        row: i + 1 
     2125                                }; 
     2126                        } 
     2127                } 
     2128 
     2129                for ( i = n.length - 1; i > 0; i-- ) { 
     2130                        if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && 
     2131                                                n[ i - 1 ] == o[ n[i].row - 1 ]) { 
     2132 
     2133                                n[ i - 1 ] = { 
     2134                                        text: n[ i - 1 ], 
     2135                                        row: n[i].row - 1 
     2136                                }; 
     2137                                o[ n[i].row - 1 ] = { 
     2138                                        text: o[ n[i].row - 1 ], 
     2139                                        row: i - 1 
     2140                                }; 
     2141                        } 
     2142                } 
     2143 
     2144                return { 
     2145                        o: o, 
     2146                        n: n 
     2147                }; 
     2148        } 
     2149 
     2150        return function( o, n ) { 
     2151                o = o.replace( /\s+$/, "" ); 
     2152                n = n.replace( /\s+$/, "" ); 
     2153 
     2154                var i, pre, 
     2155                        str = "", 
     2156                        out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ), 
     2157                        oSpace = o.match(/\s+/g), 
     2158                        nSpace = n.match(/\s+/g); 
     2159 
     2160                if ( oSpace == null ) { 
     2161                        oSpace = [ " " ]; 
     2162                } 
     2163                else { 
     2164                        oSpace.push( " " ); 
     2165                } 
     2166 
     2167                if ( nSpace == null ) { 
     2168                        nSpace = [ " " ]; 
     2169                } 
     2170                else { 
     2171                        nSpace.push( " " ); 
     2172                } 
     2173 
     2174                if ( out.n.length === 0 ) { 
     2175                        for ( i = 0; i < out.o.length; i++ ) { 
     2176                                str += "<del>" + out.o[i] + oSpace[i] + "</del>"; 
     2177                        } 
     2178                } 
     2179                else { 
     2180                        if ( out.n[0].text == null ) { 
     2181                                for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) { 
     2182                                        str += "<del>" + out.o[n] + oSpace[n] + "</del>"; 
     2183                                } 
     2184                        } 
     2185 
     2186                        for ( i = 0; i < out.n.length; i++ ) { 
     2187                                if (out.n[i].text == null) { 
     2188                                        str += "<ins>" + out.n[i] + nSpace[i] + "</ins>"; 
     2189                                } 
     2190                                else { 
     2191                                        // `pre` initialized at top of scope 
     2192                                        pre = ""; 
     2193 
     2194                                        for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { 
     2195                                                pre += "<del>" + out.o[n] + oSpace[n] + "</del>"; 
     2196                                        } 
     2197                                        str += " " + out.n[i].text + nSpace[i] + pre; 
     2198                                } 
     2199                        } 
     2200                } 
     2201 
     2202                return str; 
     2203        }; 
     2204}()); 
     2205 
     2206// for CommonJS environments, export everything 
     2207if ( typeof exports !== "undefined" ) { 
     2208        extend( exports, QUnit.constructor.prototype ); 
     2209} 
     2210 
     2211// get at whatever the global object is, like window in browsers 
     2212}( (function() {return this;}.call()) )); 
  • tests/qunit/wp-admin/js/password-strength-meter.js

    Property changes on: tests/qunit/vendor/qunit.js
    ___________________________________________________________________
    Added: svn:executable
    ## -0,0 +1 ##
    +*
    \ No newline at end of property
     
     1jQuery(function() { 
     2        module('password-strength-meter'); 
     3 
     4        test('missmached passwords should return 5', function(){ 
     5                equal( passwordStrength( 'password1',  'username', 'password2' ) , 5, 'miss matched passwords return 5'); 
     6        }); 
     7 
     8        test('passwords shorter than 4 charachters should return 0', function(){ 
     9                equal( passwordStrength( 'abc',  'username', 'abc' ) , 0, 'short passwords return 0'); 
     10        }); 
     11 
     12        test('long complicated passwords should return 4', function(){ 
     13                var password = function( length ){ 
     14                        var possability = 'abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', 
     15                                retVal = ""; 
     16                        for( var i = 0, n = possability.length; i < length; ++i) { 
     17                                retVal += possability.charAt( Math.floor( Math.random() * n ) ); 
     18                        } 
     19                        return retVal + 'aB2'; // add a lower case, uppercase and number just to make sure we always have one of each 
     20                }, 
     21                twofifty = password( 250 ); 
     22 
     23                equal( passwordStrength( twofifty , 'username', twofifty ), 4, '250 charachter complicated password returns 4'); 
     24        }); 
     25 
     26        test('short uncomplicated passwords should return 0', function(){ 
     27                var letters = 'aaaa', 
     28                        numbers = '1111', 
     29            password = 'password', 
     30                        uppercase = 'AAAA'; 
     31                equal( passwordStrength( letters, 'username', letters ), 0, 'password of `' + letters + '` returns 0' ); 
     32                equal( passwordStrength( numbers, 'username', numbers ), 0, 'password of `' + numbers + '` returns 0' ); 
     33                equal( passwordStrength( uppercase, 'username', uppercase ), 0, 'password of `' + uppercase + '` returns 0' ); 
     34                equal( passwordStrength( password, 'username', password ), 0, 'password of `' + password + '` returns 0' ); 
     35        });      
     36 
     37    test('zxcvbn passward tests should return the score we expect', function(){ 
     38        var passwords = [ 
     39            { pw: 'zxcvbn', score: 0}, 
     40            { pw: 'qwER43@!', score: 1}, 
     41            { pw: 'Tr0ub4dour&3', score: 2}, 
     42            { pw: 'correcthorsebatterystaple', score: 4}, 
     43            { pw: 'coRrecth0rseba++ery9.23.2007staple$', score: 4}, 
     44            { pw: 'D0g..................', score: 0}, 
     45            { pw: 'abcdefghijk987654321', score: 0}, 
     46            { pw: 'neverforget13/3/1997', score: 2}, 
     47            { pw: '1qaz2wsx3edc', score: 0}, 
     48            { pw: 'temppass22', score: 1}, 
     49            { pw: 'briansmith', score: 0}, 
     50            { pw: 'briansmith4mayor', score: 0}, 
     51            { pw: 'password1', score: 0}, 
     52            { pw: 'viking', score: 0}, 
     53            { pw: 'thx1138', score: 0}, 
     54            { pw: 'ScoRpi0ns', score: 0}, 
     55            { pw: 'do you know', score: 0}, 
     56            { pw: 'ryanhunter2000', score: 0}, 
     57            { pw: 'rianhunter2000', score: 1}, 
     58            { pw: 'asdfghju7654rewq', score: 2}, 
     59            { pw: 'AOEUIDHG&*()LS_', score: 2}, 
     60            { pw: '12345678', score: 0}, 
     61            { pw: 'defghi6789', score: 0}, 
     62            { pw: 'rosebud', score: 0}, 
     63            { pw: 'Rosebud', score: 0}, 
     64            { pw: 'ROSEBUD', score: 0}, 
     65            { pw: 'rosebuD', score: 0}, 
     66            { pw: 'ros3bud99', score: 0}, 
     67            { pw: 'r0s3bud99', score: 0}, 
     68            { pw: 'R0$38uD99', score: 1}, 
     69            { pw: 'verlineVANDERMARK', score: 1}, 
     70            { pw: 'eheuczkqyq', score: 4}, 
     71            { pw: 'rWibMFACxAUGZmxhVncy', score: 4}, 
     72            { pw: 'Ba9ZyWABu99[BK#6MBgbH88Tofv)vs$w', score: 4} 
     73        ]; 
     74 
     75        for(var i=0; i < passwords.length; i++) { 
     76                    equal( passwordStrength( passwords[i].pw, 'username', passwords[i].pw ), passwords[i].score, 'password of `' + passwords[i].pw + '` returns '+passwords[i].score  ); 
     77        } 
     78    }); 
     79});