Make WordPress Core

Ticket #37117: 37117.diff

File 37117.diff, 408.9 KB (added by SergeyBiryukov, 5 years ago)
  • package.json

     
    6363                "jquery-migrate": "1.4.1",
    6464                "matchdep": "~2.0.0",
    6565                "node-sass": "~4.13.1",
     66                "qunit": "~2.9.0",
     67                "sinon": "~9.0.0",
     68                "sinon-test": "~3.0.0",
    6669                "source-map-loader": "^0.2.4",
    6770                "uglify-js": "^3.6.0",
    6871                "uglifyjs-webpack-plugin": "2.2.0",
  • tests/qunit/index.html

     
    5656                <script src="../../build/wp-includes/js/mce-view.js"></script>
    5757
    5858                <!-- QUnit -->
    59                 <link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen" />
    60                 <script src="vendor/qunit.js"></script>
    61                 <script src="vendor/sinon.js"></script>
    62                 <script src="vendor/sinon-qunit.js"></script>
    63                 <script>QUnit.config.hidepassed = false;</script>
     59                <link rel="stylesheet" href="../../node_modules/qunit/qunit/qunit.css" type="text/css" media="screen" />
     60                <script src="../../node_modules/qunit/qunit/qunit.js"></script>
     61                <script src="../../node_modules/sinon/pkg/sinon.js"></script>
     62                <script src="../../node_modules/sinon-test/dist/sinon-test.js"></script>
     63                <script>
     64                        var qTest = QUnit.test, sTest = sinonTest( sinon );
     65
     66                        QUnit.config.hidepassed = false;
     67
     68                        QUnit.test = function( testName, callback ) {
     69                                return qTest( testName, sTest(callback) );
     70                        };
     71                </script>
    6472        </head>
    6573        <body>
    6674                <div id="qunit"></div>
  • tests/qunit/vendor/qunit.css

     
    1 /*!
    2  * QUnit 1.18.0
    3  * http://qunitjs.com/
    4  *
    5  * Copyright jQuery Foundation and other contributors
    6  * Released under the MIT license
    7  * http://jquery.org/license
    8  *
    9  * Date: 2015-04-03T10:23Z
    10  */
    11 
    12 /** Font Family and Sizes */
    13 
    14 #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
    15         font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
    16 }
    17 
    18 #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
    19 #qunit-tests { font-size: smaller; }
    20 
    21 
    22 /** Resets */
    23 
    24 #qunit-tests, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
    25         margin: 0;
    26         padding: 0;
    27 }
    28 
    29 
    30 /** Header */
    31 
    32 #qunit-header {
    33         padding: 0.5em 0 0.5em 1em;
    34 
    35         color: #8699A4;
    36         background-color: #0D3349;
    37 
    38         font-size: 1.5em;
    39         line-height: 1em;
    40         font-weight: 400;
    41 
    42         border-radius: 5px 5px 0 0;
    43 }
    44 
    45 #qunit-header a {
    46         text-decoration: none;
    47         color: #C2CCD1;
    48 }
    49 
    50 #qunit-header a:hover,
    51 #qunit-header a:focus {
    52         color: #FFF;
    53 }
    54 
    55 #qunit-testrunner-toolbar label {
    56         display: inline-block;
    57         padding: 0 0.5em 0 0.1em;
    58 }
    59 
    60 #qunit-banner {
    61         height: 5px;
    62 }
    63 
    64 #qunit-testrunner-toolbar {
    65         padding: 0.5em 1em 0.5em 1em;
    66         color: #5E740B;
    67         background-color: #EEE;
    68         overflow: hidden;
    69 }
    70 
    71 #qunit-userAgent {
    72         padding: 0.5em 1em 0.5em 1em;
    73         background-color: #2B81AF;
    74         color: #FFF;
    75         text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
    76 }
    77 
    78 #qunit-modulefilter-container {
    79         float: right;
    80         padding: 0.2em;
    81 }
    82 
    83 .qunit-url-config {
    84         display: inline-block;
    85         padding: 0.1em;
    86 }
    87 
    88 .qunit-filter {
    89         display: block;
    90         float: right;
    91         margin-left: 1em;
    92 }
    93 
    94 /** Tests: Pass/Fail */
    95 
    96 #qunit-tests {
    97         list-style-position: inside;
    98 }
    99 
    100 #qunit-tests li {
    101         padding: 0.4em 1em 0.4em 1em;
    102         border-bottom: 1px solid #FFF;
    103         list-style-position: inside;
    104 }
    105 
    106 #qunit-tests > li {
    107         display: none;
    108 }
    109 
    110 #qunit-tests li.running,
    111 #qunit-tests li.pass,
    112 #qunit-tests li.fail,
    113 #qunit-tests li.skipped {
    114         display: list-item;
    115 }
    116 
    117 #qunit-tests.hidepass li.running,
    118 #qunit-tests.hidepass li.pass {
    119         visibility: hidden;
    120         position: absolute;
    121         width:   0px;
    122         height:  0px;
    123         padding: 0;
    124         border:  0;
    125         margin:  0;
    126 }
    127 
    128 #qunit-tests li strong {
    129         cursor: pointer;
    130 }
    131 
    132 #qunit-tests li.skipped strong {
    133         cursor: default;
    134 }
    135 
    136 #qunit-tests li a {
    137         padding: 0.5em;
    138         color: #C2CCD1;
    139         text-decoration: none;
    140 }
    141 
    142 #qunit-tests li p a {
    143         padding: 0.25em;
    144         color: #6B6464;
    145 }
    146 #qunit-tests li a:hover,
    147 #qunit-tests li a:focus {
    148         color: #000;
    149 }
    150 
    151 #qunit-tests li .runtime {
    152         float: right;
    153         font-size: smaller;
    154 }
    155 
    156 .qunit-assert-list {
    157         margin-top: 0.5em;
    158         padding: 0.5em;
    159 
    160         background-color: #FFF;
    161 
    162         border-radius: 5px;
    163 }
    164 
    165 .qunit-collapsed {
    166         display: none;
    167 }
    168 
    169 #qunit-tests table {
    170         border-collapse: collapse;
    171         margin-top: 0.2em;
    172 }
    173 
    174 #qunit-tests th {
    175         text-align: right;
    176         vertical-align: top;
    177         padding: 0 0.5em 0 0;
    178 }
    179 
    180 #qunit-tests td {
    181         vertical-align: top;
    182 }
    183 
    184 #qunit-tests pre {
    185         margin: 0;
    186         white-space: pre-wrap;
    187         word-wrap: break-word;
    188 }
    189 
    190 #qunit-tests del {
    191         background-color: #E0F2BE;
    192         color: #374E0C;
    193         text-decoration: none;
    194 }
    195 
    196 #qunit-tests ins {
    197         background-color: #FFCACA;
    198         color: #500;
    199         text-decoration: none;
    200 }
    201 
    202 /*** Test Counts */
    203 
    204 #qunit-tests b.counts                       { color: #000; }
    205 #qunit-tests b.passed                       { color: #5E740B; }
    206 #qunit-tests b.failed                       { color: #710909; }
    207 
    208 #qunit-tests li li {
    209         padding: 5px;
    210         background-color: #FFF;
    211         border-bottom: none;
    212         list-style-position: inside;
    213 }
    214 
    215 /*** Passing Styles */
    216 
    217 #qunit-tests li li.pass {
    218         color: #3C510C;
    219         background-color: #FFF;
    220         border-left: 10px solid #C6E746;
    221 }
    222 
    223 #qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
    224 #qunit-tests .pass .test-name               { color: #366097; }
    225 
    226 #qunit-tests .pass .test-actual,
    227 #qunit-tests .pass .test-expected           { color: #999; }
    228 
    229 #qunit-banner.qunit-pass                    { background-color: #C6E746; }
    230 
    231 /*** Failing Styles */
    232 
    233 #qunit-tests li li.fail {
    234         color: #710909;
    235         background-color: #FFF;
    236         border-left: 10px solid #EE5757;
    237         white-space: pre;
    238 }
    239 
    240 #qunit-tests > li:last-child {
    241         border-radius: 0 0 5px 5px;
    242 }
    243 
    244 #qunit-tests .fail                          { color: #000; background-color: #EE5757; }
    245 #qunit-tests .fail .test-name,
    246 #qunit-tests .fail .module-name             { color: #000; }
    247 
    248 #qunit-tests .fail .test-actual             { color: #EE5757; }
    249 #qunit-tests .fail .test-expected           { color: #008000; }
    250 
    251 #qunit-banner.qunit-fail                    { background-color: #EE5757; }
    252 
    253 /*** Skipped tests */
    254 
    255 #qunit-tests .skipped {
    256         background-color: #EBECE9;
    257 }
    258 
    259 #qunit-tests .qunit-skipped-label {
    260         background-color: #F4FF77;
    261         display: inline-block;
    262         font-style: normal;
    263         color: #366097;
    264         line-height: 1.8em;
    265         padding: 0 0.5em;
    266         margin: -0.4em 0.4em -0.4em 0;
    267 }
    268 
    269 /** Result */
    270 
    271 #qunit-testresult {
    272         padding: 0.5em 1em 0.5em 1em;
    273 
    274         color: #2B81AF;
    275         background-color: #D2E0E6;
    276 
    277         border-bottom: 1px solid #FFF;
    278 }
    279 #qunit-testresult .module-name {
    280         font-weight: 700;
    281 }
    282 
    283 /** Fixture */
    284 
    285 #qunit-fixture {
    286         position: absolute;
    287         top: -10000px;
    288         left: -10000px;
    289         width: 1000px;
    290         height: 1000px;
    291 }
  • tests/qunit/vendor/qunit.js

    Property changes on: tests/qunit/vendor/qunit.css
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    1 /*!
    2  * QUnit 1.18.0
    3  * http://qunitjs.com/
    4  *
    5  * Copyright jQuery Foundation and other contributors
    6  * Released under the MIT license
    7  * http://jquery.org/license
    8  *
    9  * Date: 2015-04-03T10:23Z
    10  */
    11 
    12 (function( window ) {
    13 
    14 var QUnit,
    15         config,
    16         onErrorFnPrev,
    17         loggingCallbacks = {},
    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         now = Date.now || function() {
    24                 return new Date().getTime();
    25         },
    26         globalStartCalled = false,
    27         runStarted = false,
    28         setTimeout = window.setTimeout,
    29         clearTimeout = window.clearTimeout,
    30         defined = {
    31                 document: window.document !== undefined,
    32                 setTimeout: window.setTimeout !== undefined,
    33                 sessionStorage: (function() {
    34                         var x = "qunit-test-string";
    35                         try {
    36                                 sessionStorage.setItem( x, x );
    37                                 sessionStorage.removeItem( x );
    38                                 return true;
    39                         } catch ( e ) {
    40                                 return false;
    41                         }
    42                 }())
    43         },
    44         /**
    45          * Provides a normalized error string, correcting an issue
    46          * with IE 7 (and prior) where Error.prototype.toString is
    47          * not properly implemented
    48          *
    49          * Based on http://es5.github.com/#x15.11.4.4
    50          *
    51          * @param {String|Error} error
    52          * @return {String} error message
    53          */
    54         errorString = function( error ) {
    55                 var name, message,
    56                         errorString = error.toString();
    57                 if ( errorString.substring( 0, 7 ) === "[object" ) {
    58                         name = error.name ? error.name.toString() : "Error";
    59                         message = error.message ? error.message.toString() : "";
    60                         if ( name && message ) {
    61                                 return name + ": " + message;
    62                         } else if ( name ) {
    63                                 return name;
    64                         } else if ( message ) {
    65                                 return message;
    66                         } else {
    67                                 return "Error";
    68                         }
    69                 } else {
    70                         return errorString;
    71                 }
    72         },
    73         /**
    74          * Makes a clone of an object using only Array or Object as base,
    75          * and copies over the own enumerable properties.
    76          *
    77          * @param {Object} obj
    78          * @return {Object} New object with only the own properties (recursively).
    79          */
    80         objectValues = function( obj ) {
    81                 var key, val,
    82                         vals = QUnit.is( "array", obj ) ? [] : {};
    83                 for ( key in obj ) {
    84                         if ( hasOwn.call( obj, key ) ) {
    85                                 val = obj[ key ];
    86                                 vals[ key ] = val === Object( val ) ? objectValues( val ) : val;
    87                         }
    88                 }
    89                 return vals;
    90         };
    91 
    92 QUnit = {};
    93 
    94 /**
    95  * Config object: Maintain internal state
    96  * Later exposed as QUnit.config
    97  * `config` initialized at top of scope
    98  */
    99 config = {
    100         // The queue of tests to run
    101         queue: [],
    102 
    103         // block until document ready
    104         blocking: true,
    105 
    106         // by default, run previously failed tests first
    107         // very useful in combination with "Hide passed tests" checked
    108         reorder: true,
    109 
    110         // by default, modify document.title when suite is done
    111         altertitle: true,
    112 
    113         // by default, scroll to top of the page when suite is done
    114         scrolltop: true,
    115 
    116         // when enabled, all tests must call expect()
    117         requireExpects: false,
    118 
    119         // depth up-to which object will be dumped
    120         maxDepth: 5,
    121 
    122         // add checkboxes that are persisted in the query-string
    123         // when enabled, the id is set to `true` as a `QUnit.config` property
    124         urlConfig: [
    125                 {
    126                         id: "hidepassed",
    127                         label: "Hide passed tests",
    128                         tooltip: "Only show tests and assertions that fail. Stored as query-strings."
    129                 },
    130                 {
    131                         id: "noglobals",
    132                         label: "Check for Globals",
    133                         tooltip: "Enabling this will test if any test introduces new properties on the " +
    134                                 "`window` object. Stored as query-strings."
    135                 },
    136                 {
    137                         id: "notrycatch",
    138                         label: "No try-catch",
    139                         tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging " +
    140                                 "exceptions in IE reasonable. Stored as query-strings."
    141                 }
    142         ],
    143 
    144         // Set of all modules.
    145         modules: [],
    146 
    147         // The first unnamed module
    148         currentModule: {
    149                 name: "",
    150                 tests: []
    151         },
    152 
    153         callbacks: {}
    154 };
    155 
    156 // Push a loose unnamed module to the modules collection
    157 config.modules.push( config.currentModule );
    158 
    159 // Initialize more QUnit.config and QUnit.urlParams
    160 (function() {
    161         var i, current,
    162                 location = window.location || { search: "", protocol: "file:" },
    163                 params = location.search.slice( 1 ).split( "&" ),
    164                 length = params.length,
    165                 urlParams = {};
    166 
    167         if ( params[ 0 ] ) {
    168                 for ( i = 0; i < length; i++ ) {
    169                         current = params[ i ].split( "=" );
    170                         current[ 0 ] = decodeURIComponent( current[ 0 ] );
    171 
    172                         // allow just a key to turn on a flag, e.g., test.html?noglobals
    173                         current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
    174                         if ( urlParams[ current[ 0 ] ] ) {
    175                                 urlParams[ current[ 0 ] ] = [].concat( urlParams[ current[ 0 ] ], current[ 1 ] );
    176                         } else {
    177                                 urlParams[ current[ 0 ] ] = current[ 1 ];
    178                         }
    179                 }
    180         }
    181 
    182         if ( urlParams.filter === true ) {
    183                 delete urlParams.filter;
    184         }
    185 
    186         QUnit.urlParams = urlParams;
    187 
    188         // String search anywhere in moduleName+testName
    189         config.filter = urlParams.filter;
    190 
    191         if ( urlParams.maxDepth ) {
    192                 config.maxDepth = parseInt( urlParams.maxDepth, 10 ) === -1 ?
    193                         Number.POSITIVE_INFINITY :
    194                         urlParams.maxDepth;
    195         }
    196 
    197         config.testId = [];
    198         if ( urlParams.testId ) {
    199 
    200                 // Ensure that urlParams.testId is an array
    201                 urlParams.testId = decodeURIComponent( urlParams.testId ).split( "," );
    202                 for ( i = 0; i < urlParams.testId.length; i++ ) {
    203                         config.testId.push( urlParams.testId[ i ] );
    204                 }
    205         }
    206 
    207         // Figure out if we're running the tests from a server or not
    208         QUnit.isLocal = location.protocol === "file:";
    209 
    210         // Expose the current QUnit version
    211         QUnit.version = "1.18.0";
    212 }());
    213 
    214 // Root QUnit object.
    215 // `QUnit` initialized at top of scope
    216 extend( QUnit, {
    217 
    218         // call on start of module test to prepend name to all tests
    219         module: function( name, testEnvironment ) {
    220                 var currentModule = {
    221                         name: name,
    222                         testEnvironment: testEnvironment,
    223                         tests: []
    224                 };
    225 
    226                 // DEPRECATED: handles setup/teardown functions,
    227                 // beforeEach and afterEach should be used instead
    228                 if ( testEnvironment && testEnvironment.setup ) {
    229                         testEnvironment.beforeEach = testEnvironment.setup;
    230                         delete testEnvironment.setup;
    231                 }
    232                 if ( testEnvironment && testEnvironment.teardown ) {
    233                         testEnvironment.afterEach = testEnvironment.teardown;
    234                         delete testEnvironment.teardown;
    235                 }
    236 
    237                 config.modules.push( currentModule );
    238                 config.currentModule = currentModule;
    239         },
    240 
    241         // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0.
    242         asyncTest: function( testName, expected, callback ) {
    243                 if ( arguments.length === 2 ) {
    244                         callback = expected;
    245                         expected = null;
    246                 }
    247 
    248                 QUnit.test( testName, expected, callback, true );
    249         },
    250 
    251         test: function( testName, expected, callback, async ) {
    252                 var test;
    253 
    254                 if ( arguments.length === 2 ) {
    255                         callback = expected;
    256                         expected = null;
    257                 }
    258 
    259                 test = new Test({
    260                         testName: testName,
    261                         expected: expected,
    262                         async: async,
    263                         callback: callback
    264                 });
    265 
    266                 test.queue();
    267         },
    268 
    269         skip: function( testName ) {
    270                 var test = new Test({
    271                         testName: testName,
    272                         skip: true
    273                 });
    274 
    275                 test.queue();
    276         },
    277 
    278         // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0.
    279         // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior.
    280         start: function( count ) {
    281                 var globalStartAlreadyCalled = globalStartCalled;
    282 
    283                 if ( !config.current ) {
    284                         globalStartCalled = true;
    285 
    286                         if ( runStarted ) {
    287                                 throw new Error( "Called start() outside of a test context while already started" );
    288                         } else if ( globalStartAlreadyCalled || count > 1 ) {
    289                                 throw new Error( "Called start() outside of a test context too many times" );
    290                         } else if ( config.autostart ) {
    291                                 throw new Error( "Called start() outside of a test context when " +
    292                                         "QUnit.config.autostart was true" );
    293                         } else if ( !config.pageLoaded ) {
    294 
    295                                 // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it
    296                                 config.autostart = true;
    297                                 return;
    298                         }
    299                 } else {
    300 
    301                         // If a test is running, adjust its semaphore
    302                         config.current.semaphore -= count || 1;
    303 
    304                         // Don't start until equal number of stop-calls
    305                         if ( config.current.semaphore > 0 ) {
    306                                 return;
    307                         }
    308 
    309                         // throw an Error if start is called more often than stop
    310                         if ( config.current.semaphore < 0 ) {
    311                                 config.current.semaphore = 0;
    312 
    313                                 QUnit.pushFailure(
    314                                         "Called start() while already started (test's semaphore was 0 already)",
    315                                         sourceFromStacktrace( 2 )
    316                                 );
    317                                 return;
    318                         }
    319                 }
    320 
    321                 resumeProcessing();
    322         },
    323 
    324         // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0.
    325         stop: function( count ) {
    326 
    327                 // If there isn't a test running, don't allow QUnit.stop() to be called
    328                 if ( !config.current ) {
    329                         throw new Error( "Called stop() outside of a test context" );
    330                 }
    331 
    332                 // If a test is running, adjust its semaphore
    333                 config.current.semaphore += count || 1;
    334 
    335                 pauseProcessing();
    336         },
    337 
    338         config: config,
    339 
    340         // Safe object type checking
    341         is: function( type, obj ) {
    342                 return QUnit.objectType( obj ) === type;
    343         },
    344 
    345         objectType: function( obj ) {
    346                 if ( typeof obj === "undefined" ) {
    347                         return "undefined";
    348                 }
    349 
    350                 // Consider: typeof null === object
    351                 if ( obj === null ) {
    352                         return "null";
    353                 }
    354 
    355                 var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ),
    356                         type = match && match[ 1 ] || "";
    357 
    358                 switch ( type ) {
    359                         case "Number":
    360                                 if ( isNaN( obj ) ) {
    361                                         return "nan";
    362                                 }
    363                                 return "number";
    364                         case "String":
    365                         case "Boolean":
    366                         case "Array":
    367                         case "Date":
    368                         case "RegExp":
    369                         case "Function":
    370                                 return type.toLowerCase();
    371                 }
    372                 if ( typeof obj === "object" ) {
    373                         return "object";
    374                 }
    375                 return undefined;
    376         },
    377 
    378         extend: extend,
    379 
    380         load: function() {
    381                 config.pageLoaded = true;
    382 
    383                 // Initialize the configuration options
    384                 extend( config, {
    385                         stats: { all: 0, bad: 0 },
    386                         moduleStats: { all: 0, bad: 0 },
    387                         started: 0,
    388                         updateRate: 1000,
    389                         autostart: true,
    390                         filter: ""
    391                 }, true );
    392 
    393                 config.blocking = false;
    394 
    395                 if ( config.autostart ) {
    396                         resumeProcessing();
    397                 }
    398         }
    399 });
    400 
    401 // Register logging callbacks
    402 (function() {
    403         var i, l, key,
    404                 callbacks = [ "begin", "done", "log", "testStart", "testDone",
    405                         "moduleStart", "moduleDone" ];
    406 
    407         function registerLoggingCallback( key ) {
    408                 var loggingCallback = function( callback ) {
    409                         if ( QUnit.objectType( callback ) !== "function" ) {
    410                                 throw new Error(
    411                                         "QUnit logging methods require a callback function as their first parameters."
    412                                 );
    413                         }
    414 
    415                         config.callbacks[ key ].push( callback );
    416                 };
    417 
    418                 // DEPRECATED: This will be removed on QUnit 2.0.0+
    419                 // Stores the registered functions allowing restoring
    420                 // at verifyLoggingCallbacks() if modified
    421                 loggingCallbacks[ key ] = loggingCallback;
    422 
    423                 return loggingCallback;
    424         }
    425 
    426         for ( i = 0, l = callbacks.length; i < l; i++ ) {
    427                 key = callbacks[ i ];
    428 
    429                 // Initialize key collection of logging callback
    430                 if ( QUnit.objectType( config.callbacks[ key ] ) === "undefined" ) {
    431                         config.callbacks[ key ] = [];
    432                 }
    433 
    434                 QUnit[ key ] = registerLoggingCallback( key );
    435         }
    436 })();
    437 
    438 // `onErrorFnPrev` initialized at top of scope
    439 // Preserve other handlers
    440 onErrorFnPrev = window.onerror;
    441 
    442 // Cover uncaught exceptions
    443 // Returning true will suppress the default browser handler,
    444 // returning false will let it run.
    445 window.onerror = function( error, filePath, linerNr ) {
    446         var ret = false;
    447         if ( onErrorFnPrev ) {
    448                 ret = onErrorFnPrev( error, filePath, linerNr );
    449         }
    450 
    451         // Treat return value as window.onerror itself does,
    452         // Only do our handling if not suppressed.
    453         if ( ret !== true ) {
    454                 if ( QUnit.config.current ) {
    455                         if ( QUnit.config.current.ignoreGlobalErrors ) {
    456                                 return true;
    457                         }
    458                         QUnit.pushFailure( error, filePath + ":" + linerNr );
    459                 } else {
    460                         QUnit.test( "global failure", extend(function() {
    461                                 QUnit.pushFailure( error, filePath + ":" + linerNr );
    462                         }, { validTest: true } ) );
    463                 }
    464                 return false;
    465         }
    466 
    467         return ret;
    468 };
    469 
    470 function done() {
    471         var runtime, passed;
    472 
    473         config.autorun = true;
    474 
    475         // Log the last module results
    476         if ( config.previousModule ) {
    477                 runLoggingCallbacks( "moduleDone", {
    478                         name: config.previousModule.name,
    479                         tests: config.previousModule.tests,
    480                         failed: config.moduleStats.bad,
    481                         passed: config.moduleStats.all - config.moduleStats.bad,
    482                         total: config.moduleStats.all,
    483                         runtime: now() - config.moduleStats.started
    484                 });
    485         }
    486         delete config.previousModule;
    487 
    488         runtime = now() - config.started;
    489         passed = config.stats.all - config.stats.bad;
    490 
    491         runLoggingCallbacks( "done", {
    492                 failed: config.stats.bad,
    493                 passed: passed,
    494                 total: config.stats.all,
    495                 runtime: runtime
    496         });
    497 }
    498 
    499 // Doesn't support IE6 to IE9, it will return undefined on these browsers
    500 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
    501 function extractStacktrace( e, offset ) {
    502         offset = offset === undefined ? 4 : offset;
    503 
    504         var stack, include, i;
    505 
    506         if ( e.stack ) {
    507                 stack = e.stack.split( "\n" );
    508                 if ( /^error$/i.test( stack[ 0 ] ) ) {
    509                         stack.shift();
    510                 }
    511                 if ( fileName ) {
    512                         include = [];
    513                         for ( i = offset; i < stack.length; i++ ) {
    514                                 if ( stack[ i ].indexOf( fileName ) !== -1 ) {
    515                                         break;
    516                                 }
    517                                 include.push( stack[ i ] );
    518                         }
    519                         if ( include.length ) {
    520                                 return include.join( "\n" );
    521                         }
    522                 }
    523                 return stack[ offset ];
    524 
    525         // Support: Safari <=6 only
    526         } else if ( e.sourceURL ) {
    527 
    528                 // exclude useless self-reference for generated Error objects
    529                 if ( /qunit.js$/.test( e.sourceURL ) ) {
    530                         return;
    531                 }
    532 
    533                 // for actual exceptions, this is useful
    534                 return e.sourceURL + ":" + e.line;
    535         }
    536 }
    537 
    538 function sourceFromStacktrace( offset ) {
    539         var error = new Error();
    540 
    541         // Support: Safari <=7 only, IE <=10 - 11 only
    542         // Not all browsers generate the `stack` property for `new Error()`, see also #636
    543         if ( !error.stack ) {
    544                 try {
    545                         throw error;
    546                 } catch ( err ) {
    547                         error = err;
    548                 }
    549         }
    550 
    551         return extractStacktrace( error, offset );
    552 }
    553 
    554 function synchronize( callback, last ) {
    555         if ( QUnit.objectType( callback ) === "array" ) {
    556                 while ( callback.length ) {
    557                         synchronize( callback.shift() );
    558                 }
    559                 return;
    560         }
    561         config.queue.push( callback );
    562 
    563         if ( config.autorun && !config.blocking ) {
    564                 process( last );
    565         }
    566 }
    567 
    568 function process( last ) {
    569         function next() {
    570                 process( last );
    571         }
    572         var start = now();
    573         config.depth = ( config.depth || 0 ) + 1;
    574 
    575         while ( config.queue.length && !config.blocking ) {
    576                 if ( !defined.setTimeout || config.updateRate <= 0 ||
    577                                 ( ( now() - start ) < config.updateRate ) ) {
    578                         if ( config.current ) {
    579 
    580                                 // Reset async tracking for each phase of the Test lifecycle
    581                                 config.current.usedAsync = false;
    582                         }
    583                         config.queue.shift()();
    584                 } else {
    585                         setTimeout( next, 13 );
    586                         break;
    587                 }
    588         }
    589         config.depth--;
    590         if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
    591                 done();
    592         }
    593 }
    594 
    595 function begin() {
    596         var i, l,
    597                 modulesLog = [];
    598 
    599         // If the test run hasn't officially begun yet
    600         if ( !config.started ) {
    601 
    602                 // Record the time of the test run's beginning
    603                 config.started = now();
    604 
    605                 verifyLoggingCallbacks();
    606 
    607                 // Delete the loose unnamed module if unused.
    608                 if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) {
    609                         config.modules.shift();
    610                 }
    611 
    612                 // Avoid unnecessary information by not logging modules' test environments
    613                 for ( i = 0, l = config.modules.length; i < l; i++ ) {
    614                         modulesLog.push({
    615                                 name: config.modules[ i ].name,
    616                                 tests: config.modules[ i ].tests
    617                         });
    618                 }
    619 
    620                 // The test run is officially beginning now
    621                 runLoggingCallbacks( "begin", {
    622                         totalTests: Test.count,
    623                         modules: modulesLog
    624                 });
    625         }
    626 
    627         config.blocking = false;
    628         process( true );
    629 }
    630 
    631 function resumeProcessing() {
    632         runStarted = true;
    633 
    634         // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.)
    635         if ( defined.setTimeout ) {
    636                 setTimeout(function() {
    637                         if ( config.current && config.current.semaphore > 0 ) {
    638                                 return;
    639                         }
    640                         if ( config.timeout ) {
    641                                 clearTimeout( config.timeout );
    642                         }
    643 
    644                         begin();
    645                 }, 13 );
    646         } else {
    647                 begin();
    648         }
    649 }
    650 
    651 function pauseProcessing() {
    652         config.blocking = true;
    653 
    654         if ( config.testTimeout && defined.setTimeout ) {
    655                 clearTimeout( config.timeout );
    656                 config.timeout = setTimeout(function() {
    657                         if ( config.current ) {
    658                                 config.current.semaphore = 0;
    659                                 QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) );
    660                         } else {
    661                                 throw new Error( "Test timed out" );
    662                         }
    663                         resumeProcessing();
    664                 }, config.testTimeout );
    665         }
    666 }
    667 
    668 function saveGlobal() {
    669         config.pollution = [];
    670 
    671         if ( config.noglobals ) {
    672                 for ( var key in window ) {
    673                         if ( hasOwn.call( window, key ) ) {
    674                                 // in Opera sometimes DOM element ids show up here, ignore them
    675                                 if ( /^qunit-test-output/.test( key ) ) {
    676                                         continue;
    677                                 }
    678                                 config.pollution.push( key );
    679                         }
    680                 }
    681         }
    682 }
    683 
    684 function checkPollution() {
    685         var newGlobals,
    686                 deletedGlobals,
    687                 old = config.pollution;
    688 
    689         saveGlobal();
    690 
    691         newGlobals = diff( config.pollution, old );
    692         if ( newGlobals.length > 0 ) {
    693                 QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) );
    694         }
    695 
    696         deletedGlobals = diff( old, config.pollution );
    697         if ( deletedGlobals.length > 0 ) {
    698                 QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) );
    699         }
    700 }
    701 
    702 // returns a new Array with the elements that are in a but not in b
    703 function diff( a, b ) {
    704         var i, j,
    705                 result = a.slice();
    706 
    707         for ( i = 0; i < result.length; i++ ) {
    708                 for ( j = 0; j < b.length; j++ ) {
    709                         if ( result[ i ] === b[ j ] ) {
    710                                 result.splice( i, 1 );
    711                                 i--;
    712                                 break;
    713                         }
    714                 }
    715         }
    716         return result;
    717 }
    718 
    719 function extend( a, b, undefOnly ) {
    720         for ( var prop in b ) {
    721                 if ( hasOwn.call( b, prop ) ) {
    722 
    723                         // Avoid "Member not found" error in IE8 caused by messing with window.constructor
    724                         if ( !( prop === "constructor" && a === window ) ) {
    725                                 if ( b[ prop ] === undefined ) {
    726                                         delete a[ prop ];
    727                                 } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) {
    728                                         a[ prop ] = b[ prop ];
    729                                 }
    730                         }
    731                 }
    732         }
    733 
    734         return a;
    735 }
    736 
    737 function runLoggingCallbacks( key, args ) {
    738         var i, l, callbacks;
    739 
    740         callbacks = config.callbacks[ key ];
    741         for ( i = 0, l = callbacks.length; i < l; i++ ) {
    742                 callbacks[ i ]( args );
    743         }
    744 }
    745 
    746 // DEPRECATED: This will be removed on 2.0.0+
    747 // This function verifies if the loggingCallbacks were modified by the user
    748 // If so, it will restore it, assign the given callback and print a console warning
    749 function verifyLoggingCallbacks() {
    750         var loggingCallback, userCallback;
    751 
    752         for ( loggingCallback in loggingCallbacks ) {
    753                 if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) {
    754 
    755                         userCallback = QUnit[ loggingCallback ];
    756 
    757                         // Restore the callback function
    758                         QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ];
    759 
    760                         // Assign the deprecated given callback
    761                         QUnit[ loggingCallback ]( userCallback );
    762 
    763                         if ( window.console && window.console.warn ) {
    764                                 window.console.warn(
    765                                         "QUnit." + loggingCallback + " was replaced with a new value.\n" +
    766                                         "Please, check out the documentation on how to apply logging callbacks.\n" +
    767                                         "Reference: http://api.qunitjs.com/category/callbacks/"
    768                                 );
    769                         }
    770                 }
    771         }
    772 }
    773 
    774 // from jquery.js
    775 function inArray( elem, array ) {
    776         if ( array.indexOf ) {
    777                 return array.indexOf( elem );
    778         }
    779 
    780         for ( var i = 0, length = array.length; i < length; i++ ) {
    781                 if ( array[ i ] === elem ) {
    782                         return i;
    783                 }
    784         }
    785 
    786         return -1;
    787 }
    788 
    789 function Test( settings ) {
    790         var i, l;
    791 
    792         ++Test.count;
    793 
    794         extend( this, settings );
    795         this.assertions = [];
    796         this.semaphore = 0;
    797         this.usedAsync = false;
    798         this.module = config.currentModule;
    799         this.stack = sourceFromStacktrace( 3 );
    800 
    801         // Register unique strings
    802         for ( i = 0, l = this.module.tests; i < l.length; i++ ) {
    803                 if ( this.module.tests[ i ].name === this.testName ) {
    804                         this.testName += " ";
    805                 }
    806         }
    807 
    808         this.testId = generateHash( this.module.name, this.testName );
    809 
    810         this.module.tests.push({
    811                 name: this.testName,
    812                 testId: this.testId
    813         });
    814 
    815         if ( settings.skip ) {
    816 
    817                 // Skipped tests will fully ignore any sent callback
    818                 this.callback = function() {};
    819                 this.async = false;
    820                 this.expected = 0;
    821         } else {
    822                 this.assert = new Assert( this );
    823         }
    824 }
    825 
    826 Test.count = 0;
    827 
    828 Test.prototype = {
    829         before: function() {
    830                 if (
    831 
    832                         // Emit moduleStart when we're switching from one module to another
    833                         this.module !== config.previousModule ||
    834 
    835                                 // They could be equal (both undefined) but if the previousModule property doesn't
    836                                 // yet exist it means this is the first test in a suite that isn't wrapped in a
    837                                 // module, in which case we'll just emit a moduleStart event for 'undefined'.
    838                                 // Without this, reporters can get testStart before moduleStart  which is a problem.
    839                                 !hasOwn.call( config, "previousModule" )
    840                 ) {
    841                         if ( hasOwn.call( config, "previousModule" ) ) {
    842                                 runLoggingCallbacks( "moduleDone", {
    843                                         name: config.previousModule.name,
    844                                         tests: config.previousModule.tests,
    845                                         failed: config.moduleStats.bad,
    846                                         passed: config.moduleStats.all - config.moduleStats.bad,
    847                                         total: config.moduleStats.all,
    848                                         runtime: now() - config.moduleStats.started
    849                                 });
    850                         }
    851                         config.previousModule = this.module;
    852                         config.moduleStats = { all: 0, bad: 0, started: now() };
    853                         runLoggingCallbacks( "moduleStart", {
    854                                 name: this.module.name,
    855                                 tests: this.module.tests
    856                         });
    857                 }
    858 
    859                 config.current = this;
    860 
    861                 this.testEnvironment = extend( {}, this.module.testEnvironment );
    862                 delete this.testEnvironment.beforeEach;
    863                 delete this.testEnvironment.afterEach;
    864 
    865                 this.started = now();
    866                 runLoggingCallbacks( "testStart", {
    867                         name: this.testName,
    868                         module: this.module.name,
    869                         testId: this.testId
    870                 });
    871 
    872                 if ( !config.pollution ) {
    873                         saveGlobal();
    874                 }
    875         },
    876 
    877         run: function() {
    878                 var promise;
    879 
    880                 config.current = this;
    881 
    882                 if ( this.async ) {
    883                         QUnit.stop();
    884                 }
    885 
    886                 this.callbackStarted = now();
    887 
    888                 if ( config.notrycatch ) {
    889                         promise = this.callback.call( this.testEnvironment, this.assert );
    890                         this.resolvePromise( promise );
    891                         return;
    892                 }
    893 
    894                 try {
    895                         promise = this.callback.call( this.testEnvironment, this.assert );
    896                         this.resolvePromise( promise );
    897                 } catch ( e ) {
    898                         this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " +
    899                                 this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
    900 
    901                         // else next test will carry the responsibility
    902                         saveGlobal();
    903 
    904                         // Restart the tests if they're blocking
    905                         if ( config.blocking ) {
    906                                 QUnit.start();
    907                         }
    908                 }
    909         },
    910 
    911         after: function() {
    912                 checkPollution();
    913         },
    914 
    915         queueHook: function( hook, hookName ) {
    916                 var promise,
    917                         test = this;
    918                 return function runHook() {
    919                         config.current = test;
    920                         if ( config.notrycatch ) {
    921                                 promise = hook.call( test.testEnvironment, test.assert );
    922                                 test.resolvePromise( promise, hookName );
    923                                 return;
    924                         }
    925                         try {
    926                                 promise = hook.call( test.testEnvironment, test.assert );
    927                                 test.resolvePromise( promise, hookName );
    928                         } catch ( error ) {
    929                                 test.pushFailure( hookName + " failed on " + test.testName + ": " +
    930                                         ( error.message || error ), extractStacktrace( error, 0 ) );
    931                         }
    932                 };
    933         },
    934 
    935         // Currently only used for module level hooks, can be used to add global level ones
    936         hooks: function( handler ) {
    937                 var hooks = [];
    938 
    939                 // Hooks are ignored on skipped tests
    940                 if ( this.skip ) {
    941                         return hooks;
    942                 }
    943 
    944                 if ( this.module.testEnvironment &&
    945                                 QUnit.objectType( this.module.testEnvironment[ handler ] ) === "function" ) {
    946                         hooks.push( this.queueHook( this.module.testEnvironment[ handler ], handler ) );
    947                 }
    948 
    949                 return hooks;
    950         },
    951 
    952         finish: function() {
    953                 config.current = this;
    954                 if ( config.requireExpects && this.expected === null ) {
    955                         this.pushFailure( "Expected number of assertions to be defined, but expect() was " +
    956                                 "not called.", this.stack );
    957                 } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
    958                         this.pushFailure( "Expected " + this.expected + " assertions, but " +
    959                                 this.assertions.length + " were run", this.stack );
    960                 } else if ( this.expected === null && !this.assertions.length ) {
    961                         this.pushFailure( "Expected at least one assertion, but none were run - call " +
    962                                 "expect(0) to accept zero assertions.", this.stack );
    963                 }
    964 
    965                 var i,
    966                         bad = 0;
    967 
    968                 this.runtime = now() - this.started;
    969                 config.stats.all += this.assertions.length;
    970                 config.moduleStats.all += this.assertions.length;
    971 
    972                 for ( i = 0; i < this.assertions.length; i++ ) {
    973                         if ( !this.assertions[ i ].result ) {
    974                                 bad++;
    975                                 config.stats.bad++;
    976                                 config.moduleStats.bad++;
    977                         }
    978                 }
    979 
    980                 runLoggingCallbacks( "testDone", {
    981                         name: this.testName,
    982                         module: this.module.name,
    983                         skipped: !!this.skip,
    984                         failed: bad,
    985                         passed: this.assertions.length - bad,
    986                         total: this.assertions.length,
    987                         runtime: this.runtime,
    988 
    989                         // HTML Reporter use
    990                         assertions: this.assertions,
    991                         testId: this.testId,
    992 
    993                         // DEPRECATED: this property will be removed in 2.0.0, use runtime instead
    994                         duration: this.runtime
    995                 });
    996 
    997                 // QUnit.reset() is deprecated and will be replaced for a new
    998                 // fixture reset function on QUnit 2.0/2.1.
    999                 // It's still called here for backwards compatibility handling
    1000                 QUnit.reset();
    1001 
    1002                 config.current = undefined;
    1003         },
    1004 
    1005         queue: function() {
    1006                 var bad,
    1007                         test = this;
    1008 
    1009                 if ( !this.valid() ) {
    1010                         return;
    1011                 }
    1012 
    1013                 function run() {
    1014 
    1015                         // each of these can by async
    1016                         synchronize([
    1017                                 function() {
    1018                                         test.before();
    1019                                 },
    1020 
    1021                                 test.hooks( "beforeEach" ),
    1022 
    1023                                 function() {
    1024                                         test.run();
    1025                                 },
    1026 
    1027                                 test.hooks( "afterEach" ).reverse(),
    1028 
    1029                                 function() {
    1030                                         test.after();
    1031                                 },
    1032                                 function() {
    1033                                         test.finish();
    1034                                 }
    1035                         ]);
    1036                 }
    1037 
    1038                 // `bad` initialized at top of scope
    1039                 // defer when previous test run passed, if storage is available
    1040                 bad = QUnit.config.reorder && defined.sessionStorage &&
    1041                                 +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName );
    1042 
    1043                 if ( bad ) {
    1044                         run();
    1045                 } else {
    1046                         synchronize( run, true );
    1047                 }
    1048         },
    1049 
    1050         push: function( result, actual, expected, message ) {
    1051                 var source,
    1052                         details = {
    1053                                 module: this.module.name,
    1054                                 name: this.testName,
    1055                                 result: result,
    1056                                 message: message,
    1057                                 actual: actual,
    1058                                 expected: expected,
    1059                                 testId: this.testId,
    1060                                 runtime: now() - this.started
    1061                         };
    1062 
    1063                 if ( !result ) {
    1064                         source = sourceFromStacktrace();
    1065 
    1066                         if ( source ) {
    1067                                 details.source = source;
    1068                         }
    1069                 }
    1070 
    1071                 runLoggingCallbacks( "log", details );
    1072 
    1073                 this.assertions.push({
    1074                         result: !!result,
    1075                         message: message
    1076                 });
    1077         },
    1078 
    1079         pushFailure: function( message, source, actual ) {
    1080                 if ( !this instanceof Test ) {
    1081                         throw new Error( "pushFailure() assertion outside test context, was " +
    1082                                 sourceFromStacktrace( 2 ) );
    1083                 }
    1084 
    1085                 var details = {
    1086                                 module: this.module.name,
    1087                                 name: this.testName,
    1088                                 result: false,
    1089                                 message: message || "error",
    1090                                 actual: actual || null,
    1091                                 testId: this.testId,
    1092                                 runtime: now() - this.started
    1093                         };
    1094 
    1095                 if ( source ) {
    1096                         details.source = source;
    1097                 }
    1098 
    1099                 runLoggingCallbacks( "log", details );
    1100 
    1101                 this.assertions.push({
    1102                         result: false,
    1103                         message: message
    1104                 });
    1105         },
    1106 
    1107         resolvePromise: function( promise, phase ) {
    1108                 var then, message,
    1109                         test = this;
    1110                 if ( promise != null ) {
    1111                         then = promise.then;
    1112                         if ( QUnit.objectType( then ) === "function" ) {
    1113                                 QUnit.stop();
    1114                                 then.call(
    1115                                         promise,
    1116                                         QUnit.start,
    1117                                         function( error ) {
    1118                                                 message = "Promise rejected " +
    1119                                                         ( !phase ? "during" : phase.replace( /Each$/, "" ) ) +
    1120                                                         " " + test.testName + ": " + ( error.message || error );
    1121                                                 test.pushFailure( message, extractStacktrace( error, 0 ) );
    1122 
    1123                                                 // else next test will carry the responsibility
    1124                                                 saveGlobal();
    1125 
    1126                                                 // Unblock
    1127                                                 QUnit.start();
    1128                                         }
    1129                                 );
    1130                         }
    1131                 }
    1132         },
    1133 
    1134         valid: function() {
    1135                 var include,
    1136                         filter = config.filter && config.filter.toLowerCase(),
    1137                         module = QUnit.urlParams.module && QUnit.urlParams.module.toLowerCase(),
    1138                         fullName = ( this.module.name + ": " + this.testName ).toLowerCase();
    1139 
    1140                 // Internally-generated tests are always valid
    1141                 if ( this.callback && this.callback.validTest ) {
    1142                         return true;
    1143                 }
    1144 
    1145                 if ( config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) {
    1146                         return false;
    1147                 }
    1148 
    1149                 if ( module && ( !this.module.name || this.module.name.toLowerCase() !== module ) ) {
    1150                         return false;
    1151                 }
    1152 
    1153                 if ( !filter ) {
    1154                         return true;
    1155                 }
    1156 
    1157                 include = filter.charAt( 0 ) !== "!";
    1158                 if ( !include ) {
    1159                         filter = filter.slice( 1 );
    1160                 }
    1161 
    1162                 // If the filter matches, we need to honour include
    1163                 if ( fullName.indexOf( filter ) !== -1 ) {
    1164                         return include;
    1165                 }
    1166 
    1167                 // Otherwise, do the opposite
    1168                 return !include;
    1169         }
    1170 
    1171 };
    1172 
    1173 // Resets the test setup. Useful for tests that modify the DOM.
    1174 /*
    1175 DEPRECATED: Use multiple tests instead of resetting inside a test.
    1176 Use testStart or testDone for custom cleanup.
    1177 This method will throw an error in 2.0, and will be removed in 2.1
    1178 */
    1179 QUnit.reset = function() {
    1180 
    1181         // Return on non-browser environments
    1182         // This is necessary to not break on node tests
    1183         if ( typeof window === "undefined" ) {
    1184                 return;
    1185         }
    1186 
    1187         var fixture = defined.document && document.getElementById &&
    1188                         document.getElementById( "qunit-fixture" );
    1189 
    1190         if ( fixture ) {
    1191                 fixture.innerHTML = config.fixture;
    1192         }
    1193 };
    1194 
    1195 QUnit.pushFailure = function() {
    1196         if ( !QUnit.config.current ) {
    1197                 throw new Error( "pushFailure() assertion outside test context, in " +
    1198                         sourceFromStacktrace( 2 ) );
    1199         }
    1200 
    1201         // Gets current test obj
    1202         var currentTest = QUnit.config.current;
    1203 
    1204         return currentTest.pushFailure.apply( currentTest, arguments );
    1205 };
    1206 
    1207 // Based on Java's String.hashCode, a simple but not
    1208 // rigorously collision resistant hashing function
    1209 function generateHash( module, testName ) {
    1210         var hex,
    1211                 i = 0,
    1212                 hash = 0,
    1213                 str = module + "\x1C" + testName,
    1214                 len = str.length;
    1215 
    1216         for ( ; i < len; i++ ) {
    1217                 hash  = ( ( hash << 5 ) - hash ) + str.charCodeAt( i );
    1218                 hash |= 0;
    1219         }
    1220 
    1221         // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
    1222         // strictly necessary but increases user understanding that the id is a SHA-like hash
    1223         hex = ( 0x100000000 + hash ).toString( 16 );
    1224         if ( hex.length < 8 ) {
    1225                 hex = "0000000" + hex;
    1226         }
    1227 
    1228         return hex.slice( -8 );
    1229 }
    1230 
    1231 function Assert( testContext ) {
    1232         this.test = testContext;
    1233 }
    1234 
    1235 // Assert helpers
    1236 QUnit.assert = Assert.prototype = {
    1237 
    1238         // Specify the number of expected assertions to guarantee that failed test
    1239         // (no assertions are run at all) don't slip through.
    1240         expect: function( asserts ) {
    1241                 if ( arguments.length === 1 ) {
    1242                         this.test.expected = asserts;
    1243                 } else {
    1244                         return this.test.expected;
    1245                 }
    1246         },
    1247 
    1248         // Increment this Test's semaphore counter, then return a single-use function that
    1249         // decrements that counter a maximum of once.
    1250         async: function() {
    1251                 var test = this.test,
    1252                         popped = false;
    1253 
    1254                 test.semaphore += 1;
    1255                 test.usedAsync = true;
    1256                 pauseProcessing();
    1257 
    1258                 return function done() {
    1259                         if ( !popped ) {
    1260                                 test.semaphore -= 1;
    1261                                 popped = true;
    1262                                 resumeProcessing();
    1263                         } else {
    1264                                 test.pushFailure( "Called the callback returned from `assert.async` more than once",
    1265                                         sourceFromStacktrace( 2 ) );
    1266                         }
    1267                 };
    1268         },
    1269 
    1270         // Exports test.push() to the user API
    1271         push: function( /* result, actual, expected, message */ ) {
    1272                 var assert = this,
    1273                         currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current;
    1274 
    1275                 // Backwards compatibility fix.
    1276                 // Allows the direct use of global exported assertions and QUnit.assert.*
    1277                 // Although, it's use is not recommended as it can leak assertions
    1278                 // to other tests from async tests, because we only get a reference to the current test,
    1279                 // not exactly the test where assertion were intended to be called.
    1280                 if ( !currentTest ) {
    1281                         throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) );
    1282                 }
    1283 
    1284                 if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) {
    1285                         currentTest.pushFailure( "Assertion after the final `assert.async` was resolved",
    1286                                 sourceFromStacktrace( 2 ) );
    1287 
    1288                         // Allow this assertion to continue running anyway...
    1289                 }
    1290 
    1291                 if ( !( assert instanceof Assert ) ) {
    1292                         assert = currentTest.assert;
    1293                 }
    1294                 return assert.test.push.apply( assert.test, arguments );
    1295         },
    1296 
    1297         ok: function( result, message ) {
    1298                 message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " +
    1299                         QUnit.dump.parse( result ) );
    1300                 this.push( !!result, result, true, message );
    1301         },
    1302 
    1303         notOk: function( result, message ) {
    1304                 message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " +
    1305                         QUnit.dump.parse( result ) );
    1306                 this.push( !result, result, false, message );
    1307         },
    1308 
    1309         equal: function( actual, expected, message ) {
    1310                 /*jshint eqeqeq:false */
    1311                 this.push( expected == actual, actual, expected, message );
    1312         },
    1313 
    1314         notEqual: function( actual, expected, message ) {
    1315                 /*jshint eqeqeq:false */
    1316                 this.push( expected != actual, actual, expected, message );
    1317         },
    1318 
    1319         propEqual: function( actual, expected, message ) {
    1320                 actual = objectValues( actual );
    1321                 expected = objectValues( expected );
    1322                 this.push( QUnit.equiv( actual, expected ), actual, expected, message );
    1323         },
    1324 
    1325         notPropEqual: function( actual, expected, message ) {
    1326                 actual = objectValues( actual );
    1327                 expected = objectValues( expected );
    1328                 this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
    1329         },
    1330 
    1331         deepEqual: function( actual, expected, message ) {
    1332                 this.push( QUnit.equiv( actual, expected ), actual, expected, message );
    1333         },
    1334 
    1335         notDeepEqual: function( actual, expected, message ) {
    1336                 this.push( !QUnit.equiv( actual, expected ), actual, expected, message );
    1337         },
    1338 
    1339         strictEqual: function( actual, expected, message ) {
    1340                 this.push( expected === actual, actual, expected, message );
    1341         },
    1342 
    1343         notStrictEqual: function( actual, expected, message ) {
    1344                 this.push( expected !== actual, actual, expected, message );
    1345         },
    1346 
    1347         "throws": function( block, expected, message ) {
    1348                 var actual, expectedType,
    1349                         expectedOutput = expected,
    1350                         ok = false,
    1351                         currentTest = ( this instanceof Assert && this.test ) || QUnit.config.current;
    1352 
    1353                 // 'expected' is optional unless doing string comparison
    1354                 if ( message == null && typeof expected === "string" ) {
    1355                         message = expected;
    1356                         expected = null;
    1357                 }
    1358 
    1359                 currentTest.ignoreGlobalErrors = true;
    1360                 try {
    1361                         block.call( currentTest.testEnvironment );
    1362                 } catch (e) {
    1363                         actual = e;
    1364                 }
    1365                 currentTest.ignoreGlobalErrors = false;
    1366 
    1367                 if ( actual ) {
    1368                         expectedType = QUnit.objectType( expected );
    1369 
    1370                         // we don't want to validate thrown error
    1371                         if ( !expected ) {
    1372                                 ok = true;
    1373                                 expectedOutput = null;
    1374 
    1375                         // expected is a regexp
    1376                         } else if ( expectedType === "regexp" ) {
    1377                                 ok = expected.test( errorString( actual ) );
    1378 
    1379                         // expected is a string
    1380                         } else if ( expectedType === "string" ) {
    1381                                 ok = expected === errorString( actual );
    1382 
    1383                         // expected is a constructor, maybe an Error constructor
    1384                         } else if ( expectedType === "function" && actual instanceof expected ) {
    1385                                 ok = true;
    1386 
    1387                         // expected is an Error object
    1388                         } else if ( expectedType === "object" ) {
    1389                                 ok = actual instanceof expected.constructor &&
    1390                                         actual.name === expected.name &&
    1391                                         actual.message === expected.message;
    1392 
    1393                         // expected is a validation function which returns true if validation passed
    1394                         } else if ( expectedType === "function" && expected.call( {}, actual ) === true ) {
    1395                                 expectedOutput = null;
    1396                                 ok = true;
    1397                         }
    1398                 }
    1399 
    1400                 currentTest.assert.push( ok, actual, expectedOutput, message );
    1401         }
    1402 };
    1403 
    1404 // Provide an alternative to assert.throws(), for enviroments that consider throws a reserved word
    1405 // Known to us are: Closure Compiler, Narwhal
    1406 (function() {
    1407         /*jshint sub:true */
    1408         Assert.prototype.raises = Assert.prototype[ "throws" ];
    1409 }());
    1410 
    1411 // Test for equality any JavaScript type.
    1412 // Author: Philippe Rathé <prathe@gmail.com>
    1413 QUnit.equiv = (function() {
    1414 
    1415         // Call the o related callback with the given arguments.
    1416         function bindCallbacks( o, callbacks, args ) {
    1417                 var prop = QUnit.objectType( o );
    1418                 if ( prop ) {
    1419                         if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
    1420                                 return callbacks[ prop ].apply( callbacks, args );
    1421                         } else {
    1422                                 return callbacks[ prop ]; // or undefined
    1423                         }
    1424                 }
    1425         }
    1426 
    1427         // the real equiv function
    1428         var innerEquiv,
    1429 
    1430                 // stack to decide between skip/abort functions
    1431                 callers = [],
    1432 
    1433                 // stack to avoiding loops from circular referencing
    1434                 parents = [],
    1435                 parentsB = [],
    1436 
    1437                 getProto = Object.getPrototypeOf || function( obj ) {
    1438                         /* jshint camelcase: false, proto: true */
    1439                         return obj.__proto__;
    1440                 },
    1441                 callbacks = (function() {
    1442 
    1443                         // for string, boolean, number and null
    1444                         function useStrictEquality( b, a ) {
    1445 
    1446                                 /*jshint eqeqeq:false */
    1447                                 if ( b instanceof a.constructor || a instanceof b.constructor ) {
    1448 
    1449                                         // to catch short annotation VS 'new' annotation of a
    1450                                         // declaration
    1451                                         // e.g. var i = 1;
    1452                                         // var j = new Number(1);
    1453                                         return a == b;
    1454                                 } else {
    1455                                         return a === b;
    1456                                 }
    1457                         }
    1458 
    1459                         return {
    1460                                 "string": useStrictEquality,
    1461                                 "boolean": useStrictEquality,
    1462                                 "number": useStrictEquality,
    1463                                 "null": useStrictEquality,
    1464                                 "undefined": useStrictEquality,
    1465 
    1466                                 "nan": function( b ) {
    1467                                         return isNaN( b );
    1468                                 },
    1469 
    1470                                 "date": function( b, a ) {
    1471                                         return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
    1472                                 },
    1473 
    1474                                 "regexp": function( b, a ) {
    1475                                         return QUnit.objectType( b ) === "regexp" &&
    1476 
    1477                                                 // the regex itself
    1478                                                 a.source === b.source &&
    1479 
    1480                                                 // and its modifiers
    1481                                                 a.global === b.global &&
    1482 
    1483                                                 // (gmi) ...
    1484                                                 a.ignoreCase === b.ignoreCase &&
    1485                                                 a.multiline === b.multiline &&
    1486                                                 a.sticky === b.sticky;
    1487                                 },
    1488 
    1489                                 // - skip when the property is a method of an instance (OOP)
    1490                                 // - abort otherwise,
    1491                                 // initial === would have catch identical references anyway
    1492                                 "function": function() {
    1493                                         var caller = callers[ callers.length - 1 ];
    1494                                         return caller !== Object && typeof caller !== "undefined";
    1495                                 },
    1496 
    1497                                 "array": function( b, a ) {
    1498                                         var i, j, len, loop, aCircular, bCircular;
    1499 
    1500                                         // b could be an object literal here
    1501                                         if ( QUnit.objectType( b ) !== "array" ) {
    1502                                                 return false;
    1503                                         }
    1504 
    1505                                         len = a.length;
    1506                                         if ( len !== b.length ) {
    1507                                                 // safe and faster
    1508                                                 return false;
    1509                                         }
    1510 
    1511                                         // track reference to avoid circular references
    1512                                         parents.push( a );
    1513                                         parentsB.push( b );
    1514                                         for ( i = 0; i < len; i++ ) {
    1515                                                 loop = false;
    1516                                                 for ( j = 0; j < parents.length; j++ ) {
    1517                                                         aCircular = parents[ j ] === a[ i ];
    1518                                                         bCircular = parentsB[ j ] === b[ i ];
    1519                                                         if ( aCircular || bCircular ) {
    1520                                                                 if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
    1521                                                                         loop = true;
    1522                                                                 } else {
    1523                                                                         parents.pop();
    1524                                                                         parentsB.pop();
    1525                                                                         return false;
    1526                                                                 }
    1527                                                         }
    1528                                                 }
    1529                                                 if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
    1530                                                         parents.pop();
    1531                                                         parentsB.pop();
    1532                                                         return false;
    1533                                                 }
    1534                                         }
    1535                                         parents.pop();
    1536                                         parentsB.pop();
    1537                                         return true;
    1538                                 },
    1539 
    1540                                 "object": function( b, a ) {
    1541 
    1542                                         /*jshint forin:false */
    1543                                         var i, j, loop, aCircular, bCircular,
    1544                                                 // Default to true
    1545                                                 eq = true,
    1546                                                 aProperties = [],
    1547                                                 bProperties = [];
    1548 
    1549                                         // comparing constructors is more strict than using
    1550                                         // instanceof
    1551                                         if ( a.constructor !== b.constructor ) {
    1552 
    1553                                                 // Allow objects with no prototype to be equivalent to
    1554                                                 // objects with Object as their constructor.
    1555                                                 if ( !( ( getProto( a ) === null && getProto( b ) === Object.prototype ) ||
    1556                                                         ( getProto( b ) === null && getProto( a ) === Object.prototype ) ) ) {
    1557                                                         return false;
    1558                                                 }
    1559                                         }
    1560 
    1561                                         // stack constructor before traversing properties
    1562                                         callers.push( a.constructor );
    1563 
    1564                                         // track reference to avoid circular references
    1565                                         parents.push( a );
    1566                                         parentsB.push( b );
    1567 
    1568                                         // be strict: don't ensure hasOwnProperty and go deep
    1569                                         for ( i in a ) {
    1570                                                 loop = false;
    1571                                                 for ( j = 0; j < parents.length; j++ ) {
    1572                                                         aCircular = parents[ j ] === a[ i ];
    1573                                                         bCircular = parentsB[ j ] === b[ i ];
    1574                                                         if ( aCircular || bCircular ) {
    1575                                                                 if ( a[ i ] === b[ i ] || aCircular && bCircular ) {
    1576                                                                         loop = true;
    1577                                                                 } else {
    1578                                                                         eq = false;
    1579                                                                         break;
    1580                                                                 }
    1581                                                         }
    1582                                                 }
    1583                                                 aProperties.push( i );
    1584                                                 if ( !loop && !innerEquiv( a[ i ], b[ i ] ) ) {
    1585                                                         eq = false;
    1586                                                         break;
    1587                                                 }
    1588                                         }
    1589 
    1590                                         parents.pop();
    1591                                         parentsB.pop();
    1592                                         callers.pop(); // unstack, we are done
    1593 
    1594                                         for ( i in b ) {
    1595                                                 bProperties.push( i ); // collect b's properties
    1596                                         }
    1597 
    1598                                         // Ensures identical properties name
    1599                                         return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
    1600                                 }
    1601                         };
    1602                 }());
    1603 
    1604         innerEquiv = function() { // can take multiple arguments
    1605                 var args = [].slice.apply( arguments );
    1606                 if ( args.length < 2 ) {
    1607                         return true; // end transition
    1608                 }
    1609 
    1610                 return ( (function( a, b ) {
    1611                         if ( a === b ) {
    1612                                 return true; // catch the most you can
    1613                         } else if ( a === null || b === null || typeof a === "undefined" ||
    1614                                         typeof b === "undefined" ||
    1615                                         QUnit.objectType( a ) !== QUnit.objectType( b ) ) {
    1616 
    1617                                 // don't lose time with error prone cases
    1618                                 return false;
    1619                         } else {
    1620                                 return bindCallbacks( a, callbacks, [ b, a ] );
    1621                         }
    1622 
    1623                         // apply transition with (1..n) arguments
    1624                 }( args[ 0 ], args[ 1 ] ) ) &&
    1625                         innerEquiv.apply( this, args.splice( 1, args.length - 1 ) ) );
    1626         };
    1627 
    1628         return innerEquiv;
    1629 }());
    1630 
    1631 // Based on jsDump by Ariel Flesler
    1632 // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
    1633 QUnit.dump = (function() {
    1634         function quote( str ) {
    1635                 return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
    1636         }
    1637         function literal( o ) {
    1638                 return o + "";
    1639         }
    1640         function join( pre, arr, post ) {
    1641                 var s = dump.separator(),
    1642                         base = dump.indent(),
    1643                         inner = dump.indent( 1 );
    1644                 if ( arr.join ) {
    1645                         arr = arr.join( "," + s + inner );
    1646                 }
    1647                 if ( !arr ) {
    1648                         return pre + post;
    1649                 }
    1650                 return [ pre, inner + arr, base + post ].join( s );
    1651         }
    1652         function array( arr, stack ) {
    1653                 var i = arr.length,
    1654                         ret = new Array( i );
    1655 
    1656                 if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
    1657                         return "[object Array]";
    1658                 }
    1659 
    1660                 this.up();
    1661                 while ( i-- ) {
    1662                         ret[ i ] = this.parse( arr[ i ], undefined, stack );
    1663                 }
    1664                 this.down();
    1665                 return join( "[", ret, "]" );
    1666         }
    1667 
    1668         var reName = /^function (\w+)/,
    1669                 dump = {
    1670 
    1671                         // objType is used mostly internally, you can fix a (custom) type in advance
    1672                         parse: function( obj, objType, stack ) {
    1673                                 stack = stack || [];
    1674                                 var res, parser, parserType,
    1675                                         inStack = inArray( obj, stack );
    1676 
    1677                                 if ( inStack !== -1 ) {
    1678                                         return "recursion(" + ( inStack - stack.length ) + ")";
    1679                                 }
    1680 
    1681                                 objType = objType || this.typeOf( obj  );
    1682                                 parser = this.parsers[ objType ];
    1683                                 parserType = typeof parser;
    1684 
    1685                                 if ( parserType === "function" ) {
    1686                                         stack.push( obj );
    1687                                         res = parser.call( this, obj, stack );
    1688                                         stack.pop();
    1689                                         return res;
    1690                                 }
    1691                                 return ( parserType === "string" ) ? parser : this.parsers.error;
    1692                         },
    1693                         typeOf: function( obj ) {
    1694                                 var type;
    1695                                 if ( obj === null ) {
    1696                                         type = "null";
    1697                                 } else if ( typeof obj === "undefined" ) {
    1698                                         type = "undefined";
    1699                                 } else if ( QUnit.is( "regexp", obj ) ) {
    1700                                         type = "regexp";
    1701                                 } else if ( QUnit.is( "date", obj ) ) {
    1702                                         type = "date";
    1703                                 } else if ( QUnit.is( "function", obj ) ) {
    1704                                         type = "function";
    1705                                 } else if ( obj.setInterval !== undefined &&
    1706                                                 obj.document !== undefined &&
    1707                                                 obj.nodeType === undefined ) {
    1708                                         type = "window";
    1709                                 } else if ( obj.nodeType === 9 ) {
    1710                                         type = "document";
    1711                                 } else if ( obj.nodeType ) {
    1712                                         type = "node";
    1713                                 } else if (
    1714 
    1715                                         // native arrays
    1716                                         toString.call( obj ) === "[object Array]" ||
    1717 
    1718                                         // NodeList objects
    1719                                         ( typeof obj.length === "number" && obj.item !== undefined &&
    1720                                         ( obj.length ? obj.item( 0 ) === obj[ 0 ] : ( obj.item( 0 ) === null &&
    1721                                         obj[ 0 ] === undefined ) ) )
    1722                                 ) {
    1723                                         type = "array";
    1724                                 } else if ( obj.constructor === Error.prototype.constructor ) {
    1725                                         type = "error";
    1726                                 } else {
    1727                                         type = typeof obj;
    1728                                 }
    1729                                 return type;
    1730                         },
    1731                         separator: function() {
    1732                                 return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&#160;" : " ";
    1733                         },
    1734                         // extra can be a number, shortcut for increasing-calling-decreasing
    1735                         indent: function( extra ) {
    1736                                 if ( !this.multiline ) {
    1737                                         return "";
    1738                                 }
    1739                                 var chr = this.indentChar;
    1740                                 if ( this.HTML ) {
    1741                                         chr = chr.replace( /\t/g, "   " ).replace( / /g, "&#160;" );
    1742                                 }
    1743                                 return new Array( this.depth + ( extra || 0 ) ).join( chr );
    1744                         },
    1745                         up: function( a ) {
    1746                                 this.depth += a || 1;
    1747                         },
    1748                         down: function( a ) {
    1749                                 this.depth -= a || 1;
    1750                         },
    1751                         setParser: function( name, parser ) {
    1752                                 this.parsers[ name ] = parser;
    1753                         },
    1754                         // The next 3 are exposed so you can use them
    1755                         quote: quote,
    1756                         literal: literal,
    1757                         join: join,
    1758                         //
    1759                         depth: 1,
    1760                         maxDepth: QUnit.config.maxDepth,
    1761 
    1762                         // This is the list of parsers, to modify them, use dump.setParser
    1763                         parsers: {
    1764                                 window: "[Window]",
    1765                                 document: "[Document]",
    1766                                 error: function( error ) {
    1767                                         return "Error(\"" + error.message + "\")";
    1768                                 },
    1769                                 unknown: "[Unknown]",
    1770                                 "null": "null",
    1771                                 "undefined": "undefined",
    1772                                 "function": function( fn ) {
    1773                                         var ret = "function",
    1774 
    1775                                                 // functions never have name in IE
    1776                                                 name = "name" in fn ? fn.name : ( reName.exec( fn ) || [] )[ 1 ];
    1777 
    1778                                         if ( name ) {
    1779                                                 ret += " " + name;
    1780                                         }
    1781                                         ret += "( ";
    1782 
    1783                                         ret = [ ret, dump.parse( fn, "functionArgs" ), "){" ].join( "" );
    1784                                         return join( ret, dump.parse( fn, "functionCode" ), "}" );
    1785                                 },
    1786                                 array: array,
    1787                                 nodelist: array,
    1788                                 "arguments": array,
    1789                                 object: function( map, stack ) {
    1790                                         var keys, key, val, i, nonEnumerableProperties,
    1791                                                 ret = [];
    1792 
    1793                                         if ( dump.maxDepth && dump.depth > dump.maxDepth ) {
    1794                                                 return "[object Object]";
    1795                                         }
    1796 
    1797                                         dump.up();
    1798                                         keys = [];
    1799                                         for ( key in map ) {
    1800                                                 keys.push( key );
    1801                                         }
    1802 
    1803                                         // Some properties are not always enumerable on Error objects.
    1804                                         nonEnumerableProperties = [ "message", "name" ];
    1805                                         for ( i in nonEnumerableProperties ) {
    1806                                                 key = nonEnumerableProperties[ i ];
    1807                                                 if ( key in map && inArray( key, keys ) < 0 ) {
    1808                                                         keys.push( key );
    1809                                                 }
    1810                                         }
    1811                                         keys.sort();
    1812                                         for ( i = 0; i < keys.length; i++ ) {
    1813                                                 key = keys[ i ];
    1814                                                 val = map[ key ];
    1815                                                 ret.push( dump.parse( key, "key" ) + ": " +
    1816                                                         dump.parse( val, undefined, stack ) );
    1817                                         }
    1818                                         dump.down();
    1819                                         return join( "{", ret, "}" );
    1820                                 },
    1821                                 node: function( node ) {
    1822                                         var len, i, val,
    1823                                                 open = dump.HTML ? "&lt;" : "<",
    1824                                                 close = dump.HTML ? "&gt;" : ">",
    1825                                                 tag = node.nodeName.toLowerCase(),
    1826                                                 ret = open + tag,
    1827                                                 attrs = node.attributes;
    1828 
    1829                                         if ( attrs ) {
    1830                                                 for ( i = 0, len = attrs.length; i < len; i++ ) {
    1831                                                         val = attrs[ i ].nodeValue;
    1832 
    1833                                                         // IE6 includes all attributes in .attributes, even ones not explicitly
    1834                                                         // set. Those have values like undefined, null, 0, false, "" or
    1835                                                         // "inherit".
    1836                                                         if ( val && val !== "inherit" ) {
    1837                                                                 ret += " " + attrs[ i ].nodeName + "=" +
    1838                                                                         dump.parse( val, "attribute" );
    1839                                                         }
    1840                                                 }
    1841                                         }
    1842                                         ret += close;
    1843 
    1844                                         // Show content of TextNode or CDATASection
    1845                                         if ( node.nodeType === 3 || node.nodeType === 4 ) {
    1846                                                 ret += node.nodeValue;
    1847                                         }
    1848 
    1849                                         return ret + open + "/" + tag + close;
    1850                                 },
    1851 
    1852                                 // function calls it internally, it's the arguments part of the function
    1853                                 functionArgs: function( fn ) {
    1854                                         var args,
    1855                                                 l = fn.length;
    1856 
    1857                                         if ( !l ) {
    1858                                                 return "";
    1859                                         }
    1860 
    1861                                         args = new Array( l );
    1862                                         while ( l-- ) {
    1863 
    1864                                                 // 97 is 'a'
    1865                                                 args[ l ] = String.fromCharCode( 97 + l );
    1866                                         }
    1867                                         return " " + args.join( ", " ) + " ";
    1868                                 },
    1869                                 // object calls it internally, the key part of an item in a map
    1870                                 key: quote,
    1871                                 // function calls it internally, it's the content of the function
    1872                                 functionCode: "[code]",
    1873                                 // node calls it internally, it's an html attribute value
    1874                                 attribute: quote,
    1875                                 string: quote,
    1876                                 date: quote,
    1877                                 regexp: literal,
    1878                                 number: literal,
    1879                                 "boolean": literal
    1880                         },
    1881                         // if true, entities are escaped ( <, >, \t, space and \n )
    1882                         HTML: false,
    1883                         // indentation unit
    1884                         indentChar: "  ",
    1885                         // if true, items in a collection, are separated by a \n, else just a space.
    1886                         multiline: true
    1887                 };
    1888 
    1889         return dump;
    1890 }());
    1891 
    1892 // back compat
    1893 QUnit.jsDump = QUnit.dump;
    1894 
    1895 // For browser, export only select globals
    1896 if ( typeof window !== "undefined" ) {
    1897 
    1898         // Deprecated
    1899         // Extend assert methods to QUnit and Global scope through Backwards compatibility
    1900         (function() {
    1901                 var i,
    1902                         assertions = Assert.prototype;
    1903 
    1904                 function applyCurrent( current ) {
    1905                         return function() {
    1906                                 var assert = new Assert( QUnit.config.current );
    1907                                 current.apply( assert, arguments );
    1908                         };
    1909                 }
    1910 
    1911                 for ( i in assertions ) {
    1912                         QUnit[ i ] = applyCurrent( assertions[ i ] );
    1913                 }
    1914         })();
    1915 
    1916         (function() {
    1917                 var i, l,
    1918                         keys = [
    1919                                 "test",
    1920                                 "module",
    1921                                 "expect",
    1922                                 "asyncTest",
    1923                                 "start",
    1924                                 "stop",
    1925                                 "ok",
    1926                                 "notOk",
    1927                                 "equal",
    1928                                 "notEqual",
    1929                                 "propEqual",
    1930                                 "notPropEqual",
    1931                                 "deepEqual",
    1932                                 "notDeepEqual",
    1933                                 "strictEqual",
    1934                                 "notStrictEqual",
    1935                                 "throws"
    1936                         ];
    1937 
    1938                 for ( i = 0, l = keys.length; i < l; i++ ) {
    1939                         window[ keys[ i ] ] = QUnit[ keys[ i ] ];
    1940                 }
    1941         })();
    1942 
    1943         window.QUnit = QUnit;
    1944 }
    1945 
    1946 // For nodejs
    1947 if ( typeof module !== "undefined" && module && module.exports ) {
    1948         module.exports = QUnit;
    1949 
    1950         // For consistency with CommonJS environments' exports
    1951         module.exports.QUnit = QUnit;
    1952 }
    1953 
    1954 // For CommonJS with exports, but without module.exports, like Rhino
    1955 if ( typeof exports !== "undefined" && exports ) {
    1956         exports.QUnit = QUnit;
    1957 }
    1958 
    1959 if ( typeof define === "function" && define.amd ) {
    1960         define( function() {
    1961                 return QUnit;
    1962         } );
    1963         QUnit.config.autostart = false;
    1964 }
    1965 
    1966 // Get a reference to the global object, like window in browsers
    1967 }( (function() {
    1968         return this;
    1969 })() ));
    1970 
    1971 /*istanbul ignore next */
    1972 // jscs:disable maximumLineLength
    1973 /*
    1974  * This file is a modified version of google-diff-match-patch's JavaScript implementation
    1975  * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
    1976  * modifications are licensed as more fully set forth in LICENSE.txt.
    1977  *
    1978  * The original source of google-diff-match-patch is attributable and licensed as follows:
    1979  *
    1980  * Copyright 2006 Google Inc.
    1981  * http://code.google.com/p/google-diff-match-patch/
    1982  *
    1983  * Licensed under the Apache License, Version 2.0 (the "License");
    1984  * you may not use this file except in compliance with the License.
    1985  * You may obtain a copy of the License at
    1986  *
    1987  * http://www.apache.org/licenses/LICENSE-2.0
    1988  *
    1989  * Unless required by applicable law or agreed to in writing, software
    1990  * distributed under the License is distributed on an "AS IS" BASIS,
    1991  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1992  * See the License for the specific language governing permissions and
    1993  * limitations under the License.
    1994  *
    1995  * More Info:
    1996  *  https://code.google.com/p/google-diff-match-patch/
    1997  *
    1998  * Usage: QUnit.diff(expected, actual)
    1999  *
    2000  * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) === "the  quick <del>brown </del> fox jump<ins>s</ins><del>ed</del over"
    2001  */
    2002 QUnit.diff = (function() {
    2003 
    2004     function DiffMatchPatch() {
    2005 
    2006         // Defaults.
    2007         // Redefine these in your program to override the defaults.
    2008 
    2009         // Number of seconds to map a diff before giving up (0 for infinity).
    2010         this.DiffTimeout = 1.0;
    2011         // Cost of an empty edit operation in terms of edit characters.
    2012         this.DiffEditCost = 4;
    2013     }
    2014 
    2015     //  DIFF FUNCTIONS
    2016 
    2017     /**
    2018      * The data structure representing a diff is an array of tuples:
    2019      * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
    2020      * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
    2021      */
    2022     var DIFF_DELETE = -1,
    2023                 DIFF_INSERT = 1,
    2024                 DIFF_EQUAL = 0;
    2025 
    2026     /**
    2027      * Find the differences between two texts.  Simplifies the problem by stripping
    2028      * any common prefix or suffix off the texts before diffing.
    2029      * @param {string} text1 Old string to be diffed.
    2030      * @param {string} text2 New string to be diffed.
    2031      * @param {boolean=} optChecklines Optional speedup flag. If present and false,
    2032      *     then don't run a line-level diff first to identify the changed areas.
    2033      *     Defaults to true, which does a faster, slightly less optimal diff.
    2034      * @param {number} optDeadline Optional time when the diff should be complete
    2035      *     by.  Used internally for recursive calls.  Users should set DiffTimeout
    2036      *     instead.
    2037      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
    2038      */
    2039     DiffMatchPatch.prototype.DiffMain = function( text1, text2, optChecklines, optDeadline ) {
    2040         var deadline, checklines, commonlength,
    2041                         commonprefix, commonsuffix, diffs;
    2042         // Set a deadline by which time the diff must be complete.
    2043         if ( typeof optDeadline === "undefined" ) {
    2044             if ( this.DiffTimeout <= 0 ) {
    2045                 optDeadline = Number.MAX_VALUE;
    2046             } else {
    2047                 optDeadline = ( new Date() ).getTime() + this.DiffTimeout * 1000;
    2048             }
    2049         }
    2050         deadline = optDeadline;
    2051 
    2052         // Check for null inputs.
    2053         if ( text1 === null || text2 === null ) {
    2054             throw new Error( "Null input. (DiffMain)" );
    2055         }
    2056 
    2057         // Check for equality (speedup).
    2058         if ( text1 === text2 ) {
    2059             if ( text1 ) {
    2060                 return [
    2061                     [ DIFF_EQUAL, text1 ]
    2062                 ];
    2063             }
    2064             return [];
    2065         }
    2066 
    2067         if ( typeof optChecklines === "undefined" ) {
    2068             optChecklines = true;
    2069         }
    2070 
    2071         checklines = optChecklines;
    2072 
    2073         // Trim off common prefix (speedup).
    2074         commonlength = this.diffCommonPrefix( text1, text2 );
    2075         commonprefix = text1.substring( 0, commonlength );
    2076         text1 = text1.substring( commonlength );
    2077         text2 = text2.substring( commonlength );
    2078 
    2079         // Trim off common suffix (speedup).
    2080         /////////
    2081         commonlength = this.diffCommonSuffix( text1, text2 );
    2082         commonsuffix = text1.substring( text1.length - commonlength );
    2083         text1 = text1.substring( 0, text1.length - commonlength );
    2084         text2 = text2.substring( 0, text2.length - commonlength );
    2085 
    2086         // Compute the diff on the middle block.
    2087         diffs = this.diffCompute( text1, text2, checklines, deadline );
    2088 
    2089         // Restore the prefix and suffix.
    2090         if ( commonprefix ) {
    2091             diffs.unshift( [ DIFF_EQUAL, commonprefix ] );
    2092         }
    2093         if ( commonsuffix ) {
    2094             diffs.push( [ DIFF_EQUAL, commonsuffix ] );
    2095         }
    2096         this.diffCleanupMerge( diffs );
    2097         return diffs;
    2098     };
    2099 
    2100     /**
    2101      * Reduce the number of edits by eliminating operationally trivial equalities.
    2102      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
    2103      */
    2104     DiffMatchPatch.prototype.diffCleanupEfficiency = function( diffs ) {
    2105         var changes, equalities, equalitiesLength, lastequality,
    2106                         pointer, preIns, preDel, postIns, postDel;
    2107         changes = false;
    2108         equalities = []; // Stack of indices where equalities are found.
    2109         equalitiesLength = 0; // Keeping our own length var is faster in JS.
    2110         /** @type {?string} */
    2111         lastequality = null;
    2112         // Always equal to diffs[equalities[equalitiesLength - 1]][1]
    2113         pointer = 0; // Index of current position.
    2114         // Is there an insertion operation before the last equality.
    2115         preIns = false;
    2116         // Is there a deletion operation before the last equality.
    2117         preDel = false;
    2118         // Is there an insertion operation after the last equality.
    2119         postIns = false;
    2120         // Is there a deletion operation after the last equality.
    2121         postDel = false;
    2122         while ( pointer < diffs.length ) {
    2123             if ( diffs[ pointer ][ 0 ] === DIFF_EQUAL ) { // Equality found.
    2124                 if ( diffs[ pointer ][ 1 ].length < this.DiffEditCost && ( postIns || postDel ) ) {
    2125                     // Candidate found.
    2126                     equalities[ equalitiesLength++ ] = pointer;
    2127                     preIns = postIns;
    2128                     preDel = postDel;
    2129                     lastequality = diffs[ pointer ][ 1 ];
    2130                 } else {
    2131                     // Not a candidate, and can never become one.
    2132                     equalitiesLength = 0;
    2133                     lastequality = null;
    2134                 }
    2135                 postIns = postDel = false;
    2136             } else { // An insertion or deletion.
    2137                 if ( diffs[ pointer ][ 0 ] === DIFF_DELETE ) {
    2138                     postDel = true;
    2139                 } else {
    2140                     postIns = true;
    2141                 }
    2142                 /*
    2143                  * Five types to be split:
    2144                  * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
    2145                  * <ins>A</ins>X<ins>C</ins><del>D</del>
    2146                  * <ins>A</ins><del>B</del>X<ins>C</ins>
    2147                  * <ins>A</del>X<ins>C</ins><del>D</del>
    2148                  * <ins>A</ins><del>B</del>X<del>C</del>
    2149                  */
    2150                 if ( lastequality && ( ( preIns && preDel && postIns && postDel ) ||
    2151                         ( ( lastequality.length < this.DiffEditCost / 2 ) &&
    2152                             ( preIns + preDel + postIns + postDel ) === 3 ) ) ) {
    2153                     // Duplicate record.
    2154                     diffs.splice( equalities[equalitiesLength - 1], 0, [ DIFF_DELETE, lastequality ] );
    2155                     // Change second copy to insert.
    2156                     diffs[ equalities[ equalitiesLength - 1 ] + 1 ][ 0 ] = DIFF_INSERT;
    2157                     equalitiesLength--; // Throw away the equality we just deleted;
    2158                     lastequality = null;
    2159                     if (preIns && preDel) {
    2160                         // No changes made which could affect previous entry, keep going.
    2161                         postIns = postDel = true;
    2162                         equalitiesLength = 0;
    2163                     } else {
    2164                         equalitiesLength--; // Throw away the previous equality.
    2165                         pointer = equalitiesLength > 0 ? equalities[ equalitiesLength - 1 ] : -1;
    2166                         postIns = postDel = false;
    2167                     }
    2168                     changes = true;
    2169                 }
    2170             }
    2171             pointer++;
    2172         }
    2173 
    2174         if ( changes ) {
    2175             this.diffCleanupMerge( diffs );
    2176         }
    2177     };
    2178 
    2179     /**
    2180      * Convert a diff array into a pretty HTML report.
    2181      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
    2182      * @param {integer} string to be beautified.
    2183      * @return {string} HTML representation.
    2184      */
    2185     DiffMatchPatch.prototype.diffPrettyHtml = function( diffs ) {
    2186         var op, data, x, html = [];
    2187         for ( x = 0; x < diffs.length; x++ ) {
    2188             op = diffs[x][0]; // Operation (insert, delete, equal)
    2189             data = diffs[x][1]; // Text of change.
    2190             switch ( op ) {
    2191                 case DIFF_INSERT:
    2192                     html[x] = "<ins>" + data + "</ins>";
    2193                     break;
    2194                 case DIFF_DELETE:
    2195                     html[x] = "<del>" + data + "</del>";
    2196                     break;
    2197                 case DIFF_EQUAL:
    2198                     html[x] = "<span>" + data + "</span>";
    2199                     break;
    2200             }
    2201         }
    2202         return html.join("");
    2203     };
    2204 
    2205     /**
    2206      * Determine the common prefix of two strings.
    2207      * @param {string} text1 First string.
    2208      * @param {string} text2 Second string.
    2209      * @return {number} The number of characters common to the start of each
    2210      *     string.
    2211      */
    2212     DiffMatchPatch.prototype.diffCommonPrefix = function( text1, text2 ) {
    2213         var pointermid, pointermax, pointermin, pointerstart;
    2214         // Quick check for common null cases.
    2215         if ( !text1 || !text2 || text1.charAt(0) !== text2.charAt(0) ) {
    2216             return 0;
    2217         }
    2218         // Binary search.
    2219         // Performance analysis: http://neil.fraser.name/news/2007/10/09/
    2220         pointermin = 0;
    2221         pointermax = Math.min( text1.length, text2.length );
    2222         pointermid = pointermax;
    2223         pointerstart = 0;
    2224         while ( pointermin < pointermid ) {
    2225             if ( text1.substring( pointerstart, pointermid ) === text2.substring( pointerstart, pointermid ) ) {
    2226                 pointermin = pointermid;
    2227                 pointerstart = pointermin;
    2228             } else {
    2229                 pointermax = pointermid;
    2230             }
    2231             pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
    2232         }
    2233         return pointermid;
    2234     };
    2235 
    2236     /**
    2237      * Determine the common suffix of two strings.
    2238      * @param {string} text1 First string.
    2239      * @param {string} text2 Second string.
    2240      * @return {number} The number of characters common to the end of each string.
    2241      */
    2242     DiffMatchPatch.prototype.diffCommonSuffix = function( text1, text2 ) {
    2243         var pointermid, pointermax, pointermin, pointerend;
    2244         // Quick check for common null cases.
    2245         if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
    2246             return 0;
    2247         }
    2248         // Binary search.
    2249         // Performance analysis: http://neil.fraser.name/news/2007/10/09/
    2250         pointermin = 0;
    2251         pointermax = Math.min(text1.length, text2.length);
    2252         pointermid = pointermax;
    2253         pointerend = 0;
    2254         while ( pointermin < pointermid ) {
    2255             if (text1.substring( text1.length - pointermid, text1.length - pointerend ) ===
    2256                 text2.substring( text2.length - pointermid, text2.length - pointerend ) ) {
    2257                 pointermin = pointermid;
    2258                 pointerend = pointermin;
    2259             } else {
    2260                 pointermax = pointermid;
    2261             }
    2262             pointermid = Math.floor( ( pointermax - pointermin ) / 2 + pointermin );
    2263         }
    2264         return pointermid;
    2265     };
    2266 
    2267     /**
    2268      * Find the differences between two texts.  Assumes that the texts do not
    2269      * have any common prefix or suffix.
    2270      * @param {string} text1 Old string to be diffed.
    2271      * @param {string} text2 New string to be diffed.
    2272      * @param {boolean} checklines Speedup flag.  If false, then don't run a
    2273      *     line-level diff first to identify the changed areas.
    2274      *     If true, then run a faster, slightly less optimal diff.
    2275      * @param {number} deadline Time when the diff should be complete by.
    2276      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
    2277      * @private
    2278      */
    2279     DiffMatchPatch.prototype.diffCompute = function( text1, text2, checklines, deadline ) {
    2280         var diffs, longtext, shorttext, i, hm,
    2281                         text1A, text2A, text1B, text2B,
    2282                         midCommon, diffsA, diffsB;
    2283 
    2284         if ( !text1 ) {
    2285             // Just add some text (speedup).
    2286             return [
    2287                 [ DIFF_INSERT, text2 ]
    2288             ];
    2289         }
    2290 
    2291         if (!text2) {
    2292             // Just delete some text (speedup).
    2293             return [
    2294                 [ DIFF_DELETE, text1 ]
    2295             ];
    2296         }
    2297 
    2298         longtext = text1.length > text2.length ? text1 : text2;
    2299         shorttext = text1.length > text2.length ? text2 : text1;
    2300         i = longtext.indexOf( shorttext );
    2301         if ( i !== -1 ) {
    2302             // Shorter text is inside the longer text (speedup).
    2303             diffs = [
    2304                 [ DIFF_INSERT, longtext.substring( 0, i ) ],
    2305                 [ DIFF_EQUAL, shorttext ],
    2306                 [ DIFF_INSERT, longtext.substring( i + shorttext.length ) ]
    2307             ];
    2308             // Swap insertions for deletions if diff is reversed.
    2309             if ( text1.length > text2.length ) {
    2310                 diffs[0][0] = diffs[2][0] = DIFF_DELETE;
    2311             }
    2312             return diffs;
    2313         }
    2314 
    2315         if ( shorttext.length === 1 ) {
    2316             // Single character string.
    2317             // After the previous speedup, the character can't be an equality.
    2318             return [
    2319                 [ DIFF_DELETE, text1 ],
    2320                 [ DIFF_INSERT, text2 ]
    2321             ];
    2322         }
    2323 
    2324         // Check to see if the problem can be split in two.
    2325         hm = this.diffHalfMatch(text1, text2);
    2326         if (hm) {
    2327             // A half-match was found, sort out the return data.
    2328             text1A = hm[0];
    2329             text1B = hm[1];
    2330             text2A = hm[2];
    2331             text2B = hm[3];
    2332             midCommon = hm[4];
    2333             // Send both pairs off for separate processing.
    2334             diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
    2335             diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
    2336             // Merge the results.
    2337             return diffsA.concat([
    2338                 [ DIFF_EQUAL, midCommon ]
    2339             ], diffsB);
    2340         }
    2341 
    2342         if (checklines && text1.length > 100 && text2.length > 100) {
    2343             return this.diffLineMode(text1, text2, deadline);
    2344         }
    2345 
    2346         return this.diffBisect(text1, text2, deadline);
    2347     };
    2348 
    2349     /**
    2350      * Do the two texts share a substring which is at least half the length of the
    2351      * longer text?
    2352      * This speedup can produce non-minimal diffs.
    2353      * @param {string} text1 First string.
    2354      * @param {string} text2 Second string.
    2355      * @return {Array.<string>} Five element Array, containing the prefix of
    2356      *     text1, the suffix of text1, the prefix of text2, the suffix of
    2357      *     text2 and the common middle.  Or null if there was no match.
    2358      * @private
    2359      */
    2360     DiffMatchPatch.prototype.diffHalfMatch = function(text1, text2) {
    2361         var longtext, shorttext, dmp,
    2362                         text1A, text2B, text2A, text1B, midCommon,
    2363                         hm1, hm2, hm;
    2364         if (this.DiffTimeout <= 0) {
    2365             // Don't risk returning a non-optimal diff if we have unlimited time.
    2366             return null;
    2367         }
    2368         longtext = text1.length > text2.length ? text1 : text2;
    2369         shorttext = text1.length > text2.length ? text2 : text1;
    2370         if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
    2371             return null; // Pointless.
    2372         }
    2373         dmp = this; // 'this' becomes 'window' in a closure.
    2374 
    2375         /**
    2376          * Does a substring of shorttext exist within longtext such that the substring
    2377          * is at least half the length of longtext?
    2378          * Closure, but does not reference any external variables.
    2379          * @param {string} longtext Longer string.
    2380          * @param {string} shorttext Shorter string.
    2381          * @param {number} i Start index of quarter length substring within longtext.
    2382          * @return {Array.<string>} Five element Array, containing the prefix of
    2383          *     longtext, the suffix of longtext, the prefix of shorttext, the suffix
    2384          *     of shorttext and the common middle.  Or null if there was no match.
    2385          * @private
    2386          */
    2387         function diffHalfMatchI(longtext, shorttext, i) {
    2388             var seed, j, bestCommon, prefixLength, suffixLength,
    2389                                 bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
    2390             // Start with a 1/4 length substring at position i as a seed.
    2391             seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
    2392             j = -1;
    2393             bestCommon = "";
    2394             while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
    2395                 prefixLength = dmp.diffCommonPrefix(longtext.substring(i),
    2396                     shorttext.substring(j));
    2397                 suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i),
    2398                     shorttext.substring(0, j));
    2399                 if (bestCommon.length < suffixLength + prefixLength) {
    2400                     bestCommon = shorttext.substring(j - suffixLength, j) +
    2401                         shorttext.substring(j, j + prefixLength);
    2402                     bestLongtextA = longtext.substring(0, i - suffixLength);
    2403                     bestLongtextB = longtext.substring(i + prefixLength);
    2404                     bestShorttextA = shorttext.substring(0, j - suffixLength);
    2405                     bestShorttextB = shorttext.substring(j + prefixLength);
    2406                 }
    2407             }
    2408             if (bestCommon.length * 2 >= longtext.length) {
    2409                 return [ bestLongtextA, bestLongtextB,
    2410                     bestShorttextA, bestShorttextB, bestCommon
    2411                 ];
    2412             } else {
    2413                 return null;
    2414             }
    2415         }
    2416 
    2417         // First check if the second quarter is the seed for a half-match.
    2418         hm1 = diffHalfMatchI(longtext, shorttext,
    2419             Math.ceil(longtext.length / 4));
    2420         // Check again based on the third quarter.
    2421         hm2 = diffHalfMatchI(longtext, shorttext,
    2422             Math.ceil(longtext.length / 2));
    2423         if (!hm1 && !hm2) {
    2424             return null;
    2425         } else if (!hm2) {
    2426             hm = hm1;
    2427         } else if (!hm1) {
    2428             hm = hm2;
    2429         } else {
    2430             // Both matched.  Select the longest.
    2431             hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
    2432         }
    2433 
    2434         // A half-match was found, sort out the return data.
    2435         text1A, text1B, text2A, text2B;
    2436         if (text1.length > text2.length) {
    2437             text1A = hm[0];
    2438             text1B = hm[1];
    2439             text2A = hm[2];
    2440             text2B = hm[3];
    2441         } else {
    2442             text2A = hm[0];
    2443             text2B = hm[1];
    2444             text1A = hm[2];
    2445             text1B = hm[3];
    2446         }
    2447         midCommon = hm[4];
    2448         return [ text1A, text1B, text2A, text2B, midCommon ];
    2449     };
    2450 
    2451     /**
    2452      * Do a quick line-level diff on both strings, then rediff the parts for
    2453      * greater accuracy.
    2454      * This speedup can produce non-minimal diffs.
    2455      * @param {string} text1 Old string to be diffed.
    2456      * @param {string} text2 New string to be diffed.
    2457      * @param {number} deadline Time when the diff should be complete by.
    2458      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
    2459      * @private
    2460      */
    2461     DiffMatchPatch.prototype.diffLineMode = function(text1, text2, deadline) {
    2462         var a, diffs, linearray, pointer, countInsert,
    2463                         countDelete, textInsert, textDelete, j;
    2464         // Scan the text on a line-by-line basis first.
    2465         a = this.diffLinesToChars(text1, text2);
    2466         text1 = a.chars1;
    2467         text2 = a.chars2;
    2468         linearray = a.lineArray;
    2469 
    2470         diffs = this.DiffMain(text1, text2, false, deadline);
    2471 
    2472         // Convert the diff back to original text.
    2473         this.diffCharsToLines(diffs, linearray);
    2474         // Eliminate freak matches (e.g. blank lines)
    2475         this.diffCleanupSemantic(diffs);
    2476 
    2477         // Rediff any replacement blocks, this time character-by-character.
    2478         // Add a dummy entry at the end.
    2479         diffs.push( [ DIFF_EQUAL, "" ] );
    2480         pointer = 0;
    2481         countDelete = 0;
    2482         countInsert = 0;
    2483         textDelete = "";
    2484         textInsert = "";
    2485         while (pointer < diffs.length) {
    2486             switch ( diffs[pointer][0] ) {
    2487                 case DIFF_INSERT:
    2488                     countInsert++;
    2489                     textInsert += diffs[pointer][1];
    2490                     break;
    2491                 case DIFF_DELETE:
    2492                     countDelete++;
    2493                     textDelete += diffs[pointer][1];
    2494                     break;
    2495                 case DIFF_EQUAL:
    2496                     // Upon reaching an equality, check for prior redundancies.
    2497                     if (countDelete >= 1 && countInsert >= 1) {
    2498                         // Delete the offending records and add the merged ones.
    2499                         diffs.splice(pointer - countDelete - countInsert,
    2500                             countDelete + countInsert);
    2501                         pointer = pointer - countDelete - countInsert;
    2502                         a = this.DiffMain(textDelete, textInsert, false, deadline);
    2503                         for (j = a.length - 1; j >= 0; j--) {
    2504                             diffs.splice( pointer, 0, a[j] );
    2505                         }
    2506                         pointer = pointer + a.length;
    2507                     }
    2508                     countInsert = 0;
    2509                     countDelete = 0;
    2510                     textDelete = "";
    2511                     textInsert = "";
    2512                     break;
    2513             }
    2514             pointer++;
    2515         }
    2516         diffs.pop(); // Remove the dummy entry at the end.
    2517 
    2518         return diffs;
    2519     };
    2520 
    2521     /**
    2522      * Find the 'middle snake' of a diff, split the problem in two
    2523      * and return the recursively constructed diff.
    2524      * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
    2525      * @param {string} text1 Old string to be diffed.
    2526      * @param {string} text2 New string to be diffed.
    2527      * @param {number} deadline Time at which to bail if not yet complete.
    2528      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
    2529      * @private
    2530      */
    2531     DiffMatchPatch.prototype.diffBisect = function(text1, text2, deadline) {
    2532         var text1Length, text2Length, maxD, vOffset, vLength,
    2533                         v1, v2, x, delta, front, k1start, k1end, k2start,
    2534                         k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
    2535         // Cache the text lengths to prevent multiple calls.
    2536         text1Length = text1.length;
    2537         text2Length = text2.length;
    2538         maxD = Math.ceil((text1Length + text2Length) / 2);
    2539         vOffset = maxD;
    2540         vLength = 2 * maxD;
    2541         v1 = new Array(vLength);
    2542         v2 = new Array(vLength);
    2543         // Setting all elements to -1 is faster in Chrome & Firefox than mixing
    2544         // integers and undefined.
    2545         for (x = 0; x < vLength; x++) {
    2546             v1[x] = -1;
    2547             v2[x] = -1;
    2548         }
    2549         v1[vOffset + 1] = 0;
    2550         v2[vOffset + 1] = 0;
    2551         delta = text1Length - text2Length;
    2552         // If the total number of characters is odd, then the front path will collide
    2553         // with the reverse path.
    2554         front = (delta % 2 !== 0);
    2555         // Offsets for start and end of k loop.
    2556         // Prevents mapping of space beyond the grid.
    2557         k1start = 0;
    2558         k1end = 0;
    2559         k2start = 0;
    2560         k2end = 0;
    2561         for (d = 0; d < maxD; d++) {
    2562             // Bail out if deadline is reached.
    2563             if ((new Date()).getTime() > deadline) {
    2564                 break;
    2565             }
    2566 
    2567             // Walk the front path one step.
    2568             for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
    2569                 k1Offset = vOffset + k1;
    2570                 if ( k1 === -d || ( k1 !== d && v1[ k1Offset - 1 ] < v1[ k1Offset + 1 ] ) ) {
    2571                     x1 = v1[k1Offset + 1];
    2572                 } else {
    2573                     x1 = v1[k1Offset - 1] + 1;
    2574                 }
    2575                 y1 = x1 - k1;
    2576                 while (x1 < text1Length && y1 < text2Length &&
    2577                     text1.charAt(x1) === text2.charAt(y1)) {
    2578                     x1++;
    2579                     y1++;
    2580                 }
    2581                 v1[k1Offset] = x1;
    2582                 if (x1 > text1Length) {
    2583                     // Ran off the right of the graph.
    2584                     k1end += 2;
    2585                 } else if (y1 > text2Length) {
    2586                     // Ran off the bottom of the graph.
    2587                     k1start += 2;
    2588                 } else if (front) {
    2589                     k2Offset = vOffset + delta - k1;
    2590                     if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
    2591                         // Mirror x2 onto top-left coordinate system.
    2592                         x2 = text1Length - v2[k2Offset];
    2593                         if (x1 >= x2) {
    2594                             // Overlap detected.
    2595                             return this.diffBisectSplit(text1, text2, x1, y1, deadline);
    2596                         }
    2597                     }
    2598                 }
    2599             }
    2600 
    2601             // Walk the reverse path one step.
    2602             for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
    2603                 k2Offset = vOffset + k2;
    2604                 if ( k2 === -d || (k2 !== d && v2[ k2Offset - 1 ] < v2[ k2Offset + 1 ] ) ) {
    2605                     x2 = v2[k2Offset + 1];
    2606                 } else {
    2607                     x2 = v2[k2Offset - 1] + 1;
    2608                 }
    2609                 y2 = x2 - k2;
    2610                 while (x2 < text1Length && y2 < text2Length &&
    2611                     text1.charAt(text1Length - x2 - 1) ===
    2612                     text2.charAt(text2Length - y2 - 1)) {
    2613                     x2++;
    2614                     y2++;
    2615                 }
    2616                 v2[k2Offset] = x2;
    2617                 if (x2 > text1Length) {
    2618                     // Ran off the left of the graph.
    2619                     k2end += 2;
    2620                 } else if (y2 > text2Length) {
    2621                     // Ran off the top of the graph.
    2622                     k2start += 2;
    2623                 } else if (!front) {
    2624                     k1Offset = vOffset + delta - k2;
    2625                     if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
    2626                         x1 = v1[k1Offset];
    2627                         y1 = vOffset + x1 - k1Offset;
    2628                         // Mirror x2 onto top-left coordinate system.
    2629                         x2 = text1Length - x2;
    2630                         if (x1 >= x2) {
    2631                             // Overlap detected.
    2632                             return this.diffBisectSplit(text1, text2, x1, y1, deadline);
    2633                         }
    2634                     }
    2635                 }
    2636             }
    2637         }
    2638         // Diff took too long and hit the deadline or
    2639         // number of diffs equals number of characters, no commonality at all.
    2640         return [
    2641             [ DIFF_DELETE, text1 ],
    2642             [ DIFF_INSERT, text2 ]
    2643         ];
    2644     };
    2645 
    2646     /**
    2647      * Given the location of the 'middle snake', split the diff in two parts
    2648      * and recurse.
    2649      * @param {string} text1 Old string to be diffed.
    2650      * @param {string} text2 New string to be diffed.
    2651      * @param {number} x Index of split point in text1.
    2652      * @param {number} y Index of split point in text2.
    2653      * @param {number} deadline Time at which to bail if not yet complete.
    2654      * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
    2655      * @private
    2656      */
    2657     DiffMatchPatch.prototype.diffBisectSplit = function( text1, text2, x, y, deadline ) {
    2658         var text1a, text1b, text2a, text2b, diffs, diffsb;
    2659         text1a = text1.substring(0, x);
    2660         text2a = text2.substring(0, y);
    2661         text1b = text1.substring(x);
    2662         text2b = text2.substring(y);
    2663 
    2664         // Compute both diffs serially.
    2665         diffs = this.DiffMain(text1a, text2a, false, deadline);
    2666         diffsb = this.DiffMain(text1b, text2b, false, deadline);
    2667 
    2668         return diffs.concat(diffsb);
    2669     };
    2670 
    2671     /**
    2672      * Reduce the number of edits by eliminating semantically trivial equalities.
    2673      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
    2674      */
    2675     DiffMatchPatch.prototype.diffCleanupSemantic = function(diffs) {
    2676         var changes, equalities, equalitiesLength, lastequality,
    2677                         pointer, lengthInsertions2, lengthDeletions2, lengthInsertions1,
    2678                         lengthDeletions1, deletion, insertion, overlapLength1, overlapLength2;
    2679         changes = false;
    2680         equalities = []; // Stack of indices where equalities are found.
    2681         equalitiesLength = 0; // Keeping our own length var is faster in JS.
    2682         /** @type {?string} */
    2683         lastequality = null;
    2684         // Always equal to diffs[equalities[equalitiesLength - 1]][1]
    2685         pointer = 0; // Index of current position.
    2686         // Number of characters that changed prior to the equality.
    2687         lengthInsertions1 = 0;
    2688         lengthDeletions1 = 0;
    2689         // Number of characters that changed after the equality.
    2690         lengthInsertions2 = 0;
    2691         lengthDeletions2 = 0;
    2692         while (pointer < diffs.length) {
    2693             if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found.
    2694                 equalities[equalitiesLength++] = pointer;
    2695                 lengthInsertions1 = lengthInsertions2;
    2696                 lengthDeletions1 = lengthDeletions2;
    2697                 lengthInsertions2 = 0;
    2698                 lengthDeletions2 = 0;
    2699                 lastequality = diffs[pointer][1];
    2700             } else { // An insertion or deletion.
    2701                 if (diffs[pointer][0] === DIFF_INSERT) {
    2702                     lengthInsertions2 += diffs[pointer][1].length;
    2703                 } else {
    2704                     lengthDeletions2 += diffs[pointer][1].length;
    2705                 }
    2706                 // Eliminate an equality that is smaller or equal to the edits on both
    2707                 // sides of it.
    2708                 if (lastequality && (lastequality.length <=
    2709                         Math.max(lengthInsertions1, lengthDeletions1)) &&
    2710                     (lastequality.length <= Math.max(lengthInsertions2,
    2711                         lengthDeletions2))) {
    2712                     // Duplicate record.
    2713                     diffs.splice( equalities[ equalitiesLength - 1 ], 0, [ DIFF_DELETE, lastequality ] );
    2714                     // Change second copy to insert.
    2715                     diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
    2716                     // Throw away the equality we just deleted.
    2717                     equalitiesLength--;
    2718                     // Throw away the previous equality (it needs to be reevaluated).
    2719                     equalitiesLength--;
    2720                     pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
    2721                     lengthInsertions1 = 0; // Reset the counters.
    2722                     lengthDeletions1 = 0;
    2723                     lengthInsertions2 = 0;
    2724                     lengthDeletions2 = 0;
    2725                     lastequality = null;
    2726                     changes = true;
    2727                 }
    2728             }
    2729             pointer++;
    2730         }
    2731 
    2732         // Normalize the diff.
    2733         if (changes) {
    2734             this.diffCleanupMerge(diffs);
    2735         }
    2736 
    2737         // Find any overlaps between deletions and insertions.
    2738         // e.g: <del>abcxxx</del><ins>xxxdef</ins>
    2739         //   -> <del>abc</del>xxx<ins>def</ins>
    2740         // e.g: <del>xxxabc</del><ins>defxxx</ins>
    2741         //   -> <ins>def</ins>xxx<del>abc</del>
    2742         // Only extract an overlap if it is as big as the edit ahead or behind it.
    2743         pointer = 1;
    2744         while (pointer < diffs.length) {
    2745             if (diffs[pointer - 1][0] === DIFF_DELETE &&
    2746                 diffs[pointer][0] === DIFF_INSERT) {
    2747                 deletion = diffs[pointer - 1][1];
    2748                 insertion = diffs[pointer][1];
    2749                 overlapLength1 = this.diffCommonOverlap(deletion, insertion);
    2750                 overlapLength2 = this.diffCommonOverlap(insertion, deletion);
    2751                 if (overlapLength1 >= overlapLength2) {
    2752                     if (overlapLength1 >= deletion.length / 2 ||
    2753                         overlapLength1 >= insertion.length / 2) {
    2754                         // Overlap found.  Insert an equality and trim the surrounding edits.
    2755                         diffs.splice( pointer, 0, [ DIFF_EQUAL, insertion.substring( 0, overlapLength1 ) ] );
    2756                         diffs[pointer - 1][1] =
    2757                             deletion.substring(0, deletion.length - overlapLength1);
    2758                         diffs[pointer + 1][1] = insertion.substring(overlapLength1);
    2759                         pointer++;
    2760                     }
    2761                 } else {
    2762                     if (overlapLength2 >= deletion.length / 2 ||
    2763                         overlapLength2 >= insertion.length / 2) {
    2764                         // Reverse overlap found.
    2765                         // Insert an equality and swap and trim the surrounding edits.
    2766                         diffs.splice( pointer, 0, [ DIFF_EQUAL, deletion.substring( 0, overlapLength2 ) ] );
    2767                         diffs[pointer - 1][0] = DIFF_INSERT;
    2768                         diffs[pointer - 1][1] =
    2769                             insertion.substring(0, insertion.length - overlapLength2);
    2770                         diffs[pointer + 1][0] = DIFF_DELETE;
    2771                         diffs[pointer + 1][1] =
    2772                             deletion.substring(overlapLength2);
    2773                         pointer++;
    2774                     }
    2775                 }
    2776                 pointer++;
    2777             }
    2778             pointer++;
    2779         }
    2780     };
    2781 
    2782     /**
    2783      * Determine if the suffix of one string is the prefix of another.
    2784      * @param {string} text1 First string.
    2785      * @param {string} text2 Second string.
    2786      * @return {number} The number of characters common to the end of the first
    2787      *     string and the start of the second string.
    2788      * @private
    2789      */
    2790     DiffMatchPatch.prototype.diffCommonOverlap = function(text1, text2) {
    2791         var text1Length, text2Length, textLength,
    2792                         best, length, pattern, found;
    2793         // Cache the text lengths to prevent multiple calls.
    2794         text1Length = text1.length;
    2795         text2Length = text2.length;
    2796         // Eliminate the null case.
    2797         if (text1Length === 0 || text2Length === 0) {
    2798             return 0;
    2799         }
    2800         // Truncate the longer string.
    2801         if (text1Length > text2Length) {
    2802             text1 = text1.substring(text1Length - text2Length);
    2803         } else if (text1Length < text2Length) {
    2804             text2 = text2.substring(0, text1Length);
    2805         }
    2806         textLength = Math.min(text1Length, text2Length);
    2807         // Quick check for the worst case.
    2808         if (text1 === text2) {
    2809             return textLength;
    2810         }
    2811 
    2812         // Start by looking for a single character match
    2813         // and increase length until no match is found.
    2814         // Performance analysis: http://neil.fraser.name/news/2010/11/04/
    2815         best = 0;
    2816         length = 1;
    2817         while (true) {
    2818             pattern = text1.substring(textLength - length);
    2819             found = text2.indexOf(pattern);
    2820             if (found === -1) {
    2821                 return best;
    2822             }
    2823             length += found;
    2824             if (found === 0 || text1.substring(textLength - length) ===
    2825                 text2.substring(0, length)) {
    2826                 best = length;
    2827                 length++;
    2828             }
    2829         }
    2830     };
    2831 
    2832     /**
    2833      * Split two texts into an array of strings.  Reduce the texts to a string of
    2834      * hashes where each Unicode character represents one line.
    2835      * @param {string} text1 First string.
    2836      * @param {string} text2 Second string.
    2837      * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
    2838      *     An object containing the encoded text1, the encoded text2 and
    2839      *     the array of unique strings.
    2840      *     The zeroth element of the array of unique strings is intentionally blank.
    2841      * @private
    2842      */
    2843     DiffMatchPatch.prototype.diffLinesToChars = function(text1, text2) {
    2844         var lineArray, lineHash, chars1, chars2;
    2845         lineArray = []; // e.g. lineArray[4] === 'Hello\n'
    2846         lineHash = {}; // e.g. lineHash['Hello\n'] === 4
    2847 
    2848         // '\x00' is a valid character, but various debuggers don't like it.
    2849         // So we'll insert a junk entry to avoid generating a null character.
    2850         lineArray[0] = "";
    2851 
    2852         /**
    2853          * Split a text into an array of strings.  Reduce the texts to a string of
    2854          * hashes where each Unicode character represents one line.
    2855          * Modifies linearray and linehash through being a closure.
    2856          * @param {string} text String to encode.
    2857          * @return {string} Encoded string.
    2858          * @private
    2859          */
    2860         function diffLinesToCharsMunge(text) {
    2861             var chars, lineStart, lineEnd, lineArrayLength, line;
    2862             chars = "";
    2863             // Walk the text, pulling out a substring for each line.
    2864             // text.split('\n') would would temporarily double our memory footprint.
    2865             // Modifying text would create many large strings to garbage collect.
    2866             lineStart = 0;
    2867             lineEnd = -1;
    2868             // Keeping our own length variable is faster than looking it up.
    2869             lineArrayLength = lineArray.length;
    2870             while (lineEnd < text.length - 1) {
    2871                 lineEnd = text.indexOf("\n", lineStart);
    2872                 if (lineEnd === -1) {
    2873                     lineEnd = text.length - 1;
    2874                 }
    2875                 line = text.substring(lineStart, lineEnd + 1);
    2876                 lineStart = lineEnd + 1;
    2877 
    2878                 if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) :
    2879                     (lineHash[line] !== undefined)) {
    2880                     chars += String.fromCharCode( lineHash[ line ] );
    2881                 } else {
    2882                     chars += String.fromCharCode(lineArrayLength);
    2883                     lineHash[line] = lineArrayLength;
    2884                     lineArray[lineArrayLength++] = line;
    2885                 }
    2886             }
    2887             return chars;
    2888         }
    2889 
    2890         chars1 = diffLinesToCharsMunge(text1);
    2891         chars2 = diffLinesToCharsMunge(text2);
    2892         return {
    2893             chars1: chars1,
    2894             chars2: chars2,
    2895             lineArray: lineArray
    2896         };
    2897     };
    2898 
    2899     /**
    2900      * Rehydrate the text in a diff from a string of line hashes to real lines of
    2901      * text.
    2902      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
    2903      * @param {!Array.<string>} lineArray Array of unique strings.
    2904      * @private
    2905      */
    2906     DiffMatchPatch.prototype.diffCharsToLines = function( diffs, lineArray ) {
    2907         var x, chars, text, y;
    2908         for ( x = 0; x < diffs.length; x++ ) {
    2909             chars = diffs[x][1];
    2910             text = [];
    2911             for ( y = 0; y < chars.length; y++ ) {
    2912                 text[y] = lineArray[chars.charCodeAt(y)];
    2913             }
    2914             diffs[x][1] = text.join("");
    2915         }
    2916     };
    2917 
    2918     /**
    2919      * Reorder and merge like edit sections.  Merge equalities.
    2920      * Any edit section can move as long as it doesn't cross an equality.
    2921      * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
    2922      */
    2923     DiffMatchPatch.prototype.diffCleanupMerge = function(diffs) {
    2924         var pointer, countDelete, countInsert, textInsert, textDelete,
    2925                         commonlength, changes;
    2926         diffs.push( [ DIFF_EQUAL, "" ] ); // Add a dummy entry at the end.
    2927         pointer = 0;
    2928         countDelete = 0;
    2929         countInsert = 0;
    2930         textDelete = "";
    2931         textInsert = "";
    2932         commonlength;
    2933         while (pointer < diffs.length) {
    2934             switch ( diffs[ pointer ][ 0 ] ) {
    2935                 case DIFF_INSERT:
    2936                     countInsert++;
    2937                     textInsert += diffs[pointer][1];
    2938                     pointer++;
    2939                     break;
    2940                 case DIFF_DELETE:
    2941                     countDelete++;
    2942                     textDelete += diffs[pointer][1];
    2943                     pointer++;
    2944                     break;
    2945                 case DIFF_EQUAL:
    2946                     // Upon reaching an equality, check for prior redundancies.
    2947                     if (countDelete + countInsert > 1) {
    2948                         if (countDelete !== 0 && countInsert !== 0) {
    2949                             // Factor out any common prefixies.
    2950                             commonlength = this.diffCommonPrefix(textInsert, textDelete);
    2951                             if (commonlength !== 0) {
    2952                                 if ((pointer - countDelete - countInsert) > 0 &&
    2953                                     diffs[pointer - countDelete - countInsert - 1][0] ===
    2954                                     DIFF_EQUAL) {
    2955                                     diffs[pointer - countDelete - countInsert - 1][1] +=
    2956                                         textInsert.substring(0, commonlength);
    2957                                 } else {
    2958                                     diffs.splice( 0, 0, [ DIFF_EQUAL,
    2959                                         textInsert.substring( 0, commonlength )
    2960                                      ] );
    2961                                     pointer++;
    2962                                 }
    2963                                 textInsert = textInsert.substring(commonlength);
    2964                                 textDelete = textDelete.substring(commonlength);
    2965                             }
    2966                             // Factor out any common suffixies.
    2967                             commonlength = this.diffCommonSuffix(textInsert, textDelete);
    2968                             if (commonlength !== 0) {
    2969                                 diffs[pointer][1] = textInsert.substring(textInsert.length -
    2970                                     commonlength) + diffs[pointer][1];
    2971                                 textInsert = textInsert.substring(0, textInsert.length -
    2972                                     commonlength);
    2973                                 textDelete = textDelete.substring(0, textDelete.length -
    2974                                     commonlength);
    2975                             }
    2976                         }
    2977                         // Delete the offending records and add the merged ones.
    2978                         if (countDelete === 0) {
    2979                             diffs.splice( pointer - countInsert,
    2980                                 countDelete + countInsert, [ DIFF_INSERT, textInsert ] );
    2981                         } else if (countInsert === 0) {
    2982                             diffs.splice( pointer - countDelete,
    2983                                 countDelete + countInsert, [ DIFF_DELETE, textDelete ] );
    2984                         } else {
    2985                             diffs.splice( pointer - countDelete - countInsert,
    2986                                 countDelete + countInsert, [ DIFF_DELETE, textDelete ], [ DIFF_INSERT, textInsert ] );
    2987                         }
    2988                         pointer = pointer - countDelete - countInsert +
    2989                             (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
    2990                     } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
    2991                         // Merge this equality with the previous one.
    2992                         diffs[pointer - 1][1] += diffs[pointer][1];
    2993                         diffs.splice(pointer, 1);
    2994                     } else {
    2995                         pointer++;
    2996                     }
    2997                     countInsert = 0;
    2998                     countDelete = 0;
    2999                     textDelete = "";
    3000                     textInsert = "";
    3001                     break;
    3002             }
    3003         }
    3004         if (diffs[diffs.length - 1][1] === "") {
    3005             diffs.pop(); // Remove the dummy entry at the end.
    3006         }
    3007 
    3008         // Second pass: look for single edits surrounded on both sides by equalities
    3009         // which can be shifted sideways to eliminate an equality.
    3010         // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
    3011         changes = false;
    3012         pointer = 1;
    3013         // Intentionally ignore the first and last element (don't need checking).
    3014         while (pointer < diffs.length - 1) {
    3015             if (diffs[pointer - 1][0] === DIFF_EQUAL &&
    3016                 diffs[pointer + 1][0] === DIFF_EQUAL) {
    3017                 // This is a single edit surrounded by equalities.
    3018                 if ( diffs[ pointer ][ 1 ].substring( diffs[ pointer ][ 1 ].length -
    3019                         diffs[ pointer - 1 ][ 1 ].length ) === diffs[ pointer - 1 ][ 1 ] ) {
    3020                     // Shift the edit over the previous equality.
    3021                     diffs[pointer][1] = diffs[pointer - 1][1] +
    3022                         diffs[pointer][1].substring(0, diffs[pointer][1].length -
    3023                             diffs[pointer - 1][1].length);
    3024                     diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
    3025                     diffs.splice(pointer - 1, 1);
    3026                     changes = true;
    3027                 } else if ( diffs[ pointer ][ 1 ].substring( 0, diffs[ pointer + 1 ][ 1 ].length ) ===
    3028                     diffs[ pointer + 1 ][ 1 ] ) {
    3029                     // Shift the edit over the next equality.
    3030                     diffs[pointer - 1][1] += diffs[pointer + 1][1];
    3031                     diffs[pointer][1] =
    3032                         diffs[pointer][1].substring(diffs[pointer + 1][1].length) +
    3033                         diffs[pointer + 1][1];
    3034                     diffs.splice(pointer + 1, 1);
    3035                     changes = true;
    3036                 }
    3037             }
    3038             pointer++;
    3039         }
    3040         // If shifts were made, the diff needs reordering and another shift sweep.
    3041         if (changes) {
    3042             this.diffCleanupMerge(diffs);
    3043         }
    3044     };
    3045 
    3046     return function(o, n) {
    3047                 var diff, output, text;
    3048         diff = new DiffMatchPatch();
    3049         output = diff.DiffMain(o, n);
    3050         //console.log(output);
    3051         diff.diffCleanupEfficiency(output);
    3052         text = diff.diffPrettyHtml(output);
    3053 
    3054         return text;
    3055     };
    3056 }());
    3057 // jscs:enable
    3058 
    3059 (function() {
    3060 
    3061 // Deprecated QUnit.init - Ref #530
    3062 // Re-initialize the configuration options
    3063 QUnit.init = function() {
    3064         var tests, banner, result, qunit,
    3065                 config = QUnit.config;
    3066 
    3067         config.stats = { all: 0, bad: 0 };
    3068         config.moduleStats = { all: 0, bad: 0 };
    3069         config.started = 0;
    3070         config.updateRate = 1000;
    3071         config.blocking = false;
    3072         config.autostart = true;
    3073         config.autorun = false;
    3074         config.filter = "";
    3075         config.queue = [];
    3076 
    3077         // Return on non-browser environments
    3078         // This is necessary to not break on node tests
    3079         if ( typeof window === "undefined" ) {
    3080                 return;
    3081         }
    3082 
    3083         qunit = id( "qunit" );
    3084         if ( qunit ) {
    3085                 qunit.innerHTML =
    3086                         "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
    3087                         "<h2 id='qunit-banner'></h2>" +
    3088                         "<div id='qunit-testrunner-toolbar'></div>" +
    3089                         "<h2 id='qunit-userAgent'></h2>" +
    3090                         "<ol id='qunit-tests'></ol>";
    3091         }
    3092 
    3093         tests = id( "qunit-tests" );
    3094         banner = id( "qunit-banner" );
    3095         result = id( "qunit-testresult" );
    3096 
    3097         if ( tests ) {
    3098                 tests.innerHTML = "";
    3099         }
    3100 
    3101         if ( banner ) {
    3102                 banner.className = "";
    3103         }
    3104 
    3105         if ( result ) {
    3106                 result.parentNode.removeChild( result );
    3107         }
    3108 
    3109         if ( tests ) {
    3110                 result = document.createElement( "p" );
    3111                 result.id = "qunit-testresult";
    3112                 result.className = "result";
    3113                 tests.parentNode.insertBefore( result, tests );
    3114                 result.innerHTML = "Running...<br />&#160;";
    3115         }
    3116 };
    3117 
    3118 // Don't load the HTML Reporter on non-Browser environments
    3119 if ( typeof window === "undefined" ) {
    3120         return;
    3121 }
    3122 
    3123 var config = QUnit.config,
    3124         hasOwn = Object.prototype.hasOwnProperty,
    3125         defined = {
    3126                 document: window.document !== undefined,
    3127                 sessionStorage: (function() {
    3128                         var x = "qunit-test-string";
    3129                         try {
    3130                                 sessionStorage.setItem( x, x );
    3131                                 sessionStorage.removeItem( x );
    3132                                 return true;
    3133                         } catch ( e ) {
    3134                                 return false;
    3135                         }
    3136                 }())
    3137         },
    3138         modulesList = [];
    3139 
    3140 /**
    3141 * Escape text for attribute or text content.
    3142 */
    3143 function escapeText( s ) {
    3144         if ( !s ) {
    3145                 return "";
    3146         }
    3147         s = s + "";
    3148 
    3149         // Both single quotes and double quotes (for attributes)
    3150         return s.replace( /['"<>&]/g, function( s ) {
    3151                 switch ( s ) {
    3152                 case "'":
    3153                         return "&#039;";
    3154                 case "\"":
    3155                         return "&quot;";
    3156                 case "<":
    3157                         return "&lt;";
    3158                 case ">":
    3159                         return "&gt;";
    3160                 case "&":
    3161                         return "&amp;";
    3162                 }
    3163         });
    3164 }
    3165 
    3166 /**
    3167  * @param {HTMLElement} elem
    3168  * @param {string} type
    3169  * @param {Function} fn
    3170  */
    3171 function addEvent( elem, type, fn ) {
    3172         if ( elem.addEventListener ) {
    3173 
    3174                 // Standards-based browsers
    3175                 elem.addEventListener( type, fn, false );
    3176         } else if ( elem.attachEvent ) {
    3177 
    3178                 // support: IE <9
    3179                 elem.attachEvent( "on" + type, function() {
    3180                         var event = window.event;
    3181                         if ( !event.target ) {
    3182                                 event.target = event.srcElement || document;
    3183                         }
    3184 
    3185                         fn.call( elem, event );
    3186                 });
    3187         }
    3188 }
    3189 
    3190 /**
    3191  * @param {Array|NodeList} elems
    3192  * @param {string} type
    3193  * @param {Function} fn
    3194  */
    3195 function addEvents( elems, type, fn ) {
    3196         var i = elems.length;
    3197         while ( i-- ) {
    3198                 addEvent( elems[ i ], type, fn );
    3199         }
    3200 }
    3201 
    3202 function hasClass( elem, name ) {
    3203         return ( " " + elem.className + " " ).indexOf( " " + name + " " ) >= 0;
    3204 }
    3205 
    3206 function addClass( elem, name ) {
    3207         if ( !hasClass( elem, name ) ) {
    3208                 elem.className += ( elem.className ? " " : "" ) + name;
    3209         }
    3210 }
    3211 
    3212 function toggleClass( elem, name ) {
    3213         if ( hasClass( elem, name ) ) {
    3214                 removeClass( elem, name );
    3215         } else {
    3216                 addClass( elem, name );
    3217         }
    3218 }
    3219 
    3220 function removeClass( elem, name ) {
    3221         var set = " " + elem.className + " ";
    3222 
    3223         // Class name may appear multiple times
    3224         while ( set.indexOf( " " + name + " " ) >= 0 ) {
    3225                 set = set.replace( " " + name + " ", " " );
    3226         }
    3227 
    3228         // trim for prettiness
    3229         elem.className = typeof set.trim === "function" ? set.trim() : set.replace( /^\s+|\s+$/g, "" );
    3230 }
    3231 
    3232 function id( name ) {
    3233         return defined.document && document.getElementById && document.getElementById( name );
    3234 }
    3235 
    3236 function getUrlConfigHtml() {
    3237         var i, j, val,
    3238                 escaped, escapedTooltip,
    3239                 selection = false,
    3240                 len = config.urlConfig.length,
    3241                 urlConfigHtml = "";
    3242 
    3243         for ( i = 0; i < len; i++ ) {
    3244                 val = config.urlConfig[ i ];
    3245                 if ( typeof val === "string" ) {
    3246                         val = {
    3247                                 id: val,
    3248                                 label: val
    3249                         };
    3250                 }
    3251 
    3252                 escaped = escapeText( val.id );
    3253                 escapedTooltip = escapeText( val.tooltip );
    3254 
    3255                 if ( config[ val.id ] === undefined ) {
    3256                         config[ val.id ] = QUnit.urlParams[ val.id ];
    3257                 }
    3258 
    3259                 if ( !val.value || typeof val.value === "string" ) {
    3260                         urlConfigHtml += "<input id='qunit-urlconfig-" + escaped +
    3261                                 "' name='" + escaped + "' type='checkbox'" +
    3262                                 ( val.value ? " value='" + escapeText( val.value ) + "'" : "" ) +
    3263                                 ( config[ val.id ] ? " checked='checked'" : "" ) +
    3264                                 " title='" + escapedTooltip + "' /><label for='qunit-urlconfig-" + escaped +
    3265                                 "' title='" + escapedTooltip + "'>" + val.label + "</label>";
    3266                 } else {
    3267                         urlConfigHtml += "<label for='qunit-urlconfig-" + escaped +
    3268                                 "' title='" + escapedTooltip + "'>" + val.label +
    3269                                 ": </label><select id='qunit-urlconfig-" + escaped +
    3270                                 "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
    3271 
    3272                         if ( QUnit.is( "array", val.value ) ) {
    3273                                 for ( j = 0; j < val.value.length; j++ ) {
    3274                                         escaped = escapeText( val.value[ j ] );
    3275                                         urlConfigHtml += "<option value='" + escaped + "'" +
    3276                                                 ( config[ val.id ] === val.value[ j ] ?
    3277                                                         ( selection = true ) && " selected='selected'" : "" ) +
    3278                                                 ">" + escaped + "</option>";
    3279                                 }
    3280                         } else {
    3281                                 for ( j in val.value ) {
    3282                                         if ( hasOwn.call( val.value, j ) ) {
    3283                                                 urlConfigHtml += "<option value='" + escapeText( j ) + "'" +
    3284                                                         ( config[ val.id ] === j ?
    3285                                                                 ( selection = true ) && " selected='selected'" : "" ) +
    3286                                                         ">" + escapeText( val.value[ j ] ) + "</option>";
    3287                                         }
    3288                                 }
    3289                         }
    3290                         if ( config[ val.id ] && !selection ) {
    3291                                 escaped = escapeText( config[ val.id ] );
    3292                                 urlConfigHtml += "<option value='" + escaped +
    3293                                         "' selected='selected' disabled='disabled'>" + escaped + "</option>";
    3294                         }
    3295                         urlConfigHtml += "</select>";
    3296                 }
    3297         }
    3298 
    3299         return urlConfigHtml;
    3300 }
    3301 
    3302 // Handle "click" events on toolbar checkboxes and "change" for select menus.
    3303 // Updates the URL with the new state of `config.urlConfig` values.
    3304 function toolbarChanged() {
    3305         var updatedUrl, value,
    3306                 field = this,
    3307                 params = {};
    3308 
    3309         // Detect if field is a select menu or a checkbox
    3310         if ( "selectedIndex" in field ) {
    3311                 value = field.options[ field.selectedIndex ].value || undefined;
    3312         } else {
    3313                 value = field.checked ? ( field.defaultValue || true ) : undefined;
    3314         }
    3315 
    3316         params[ field.name ] = value;
    3317         updatedUrl = setUrl( params );
    3318 
    3319         if ( "hidepassed" === field.name && "replaceState" in window.history ) {
    3320                 config[ field.name ] = value || false;
    3321                 if ( value ) {
    3322                         addClass( id( "qunit-tests" ), "hidepass" );
    3323                 } else {
    3324                         removeClass( id( "qunit-tests" ), "hidepass" );
    3325                 }
    3326 
    3327                 // It is not necessary to refresh the whole page
    3328                 window.history.replaceState( null, "", updatedUrl );
    3329         } else {
    3330                 window.location = updatedUrl;
    3331         }
    3332 }
    3333 
    3334 function setUrl( params ) {
    3335         var key,
    3336                 querystring = "?";
    3337 
    3338         params = QUnit.extend( QUnit.extend( {}, QUnit.urlParams ), params );
    3339 
    3340         for ( key in params ) {
    3341                 if ( hasOwn.call( params, key ) ) {
    3342                         if ( params[ key ] === undefined ) {
    3343                                 continue;
    3344                         }
    3345                         querystring += encodeURIComponent( key );
    3346                         if ( params[ key ] !== true ) {
    3347                                 querystring += "=" + encodeURIComponent( params[ key ] );
    3348                         }
    3349                         querystring += "&";
    3350                 }
    3351         }
    3352         return location.protocol + "//" + location.host +
    3353                 location.pathname + querystring.slice( 0, -1 );
    3354 }
    3355 
    3356 function applyUrlParams() {
    3357         var selectedModule,
    3358                 modulesList = id( "qunit-modulefilter" ),
    3359                 filter = id( "qunit-filter-input" ).value;
    3360 
    3361         selectedModule = modulesList ?
    3362                 decodeURIComponent( modulesList.options[ modulesList.selectedIndex ].value ) :
    3363                 undefined;
    3364 
    3365         window.location = setUrl({
    3366                 module: ( selectedModule === "" ) ? undefined : selectedModule,
    3367                 filter: ( filter === "" ) ? undefined : filter,
    3368 
    3369                 // Remove testId filter
    3370                 testId: undefined
    3371         });
    3372 }
    3373 
    3374 function toolbarUrlConfigContainer() {
    3375         var urlConfigContainer = document.createElement( "span" );
    3376 
    3377         urlConfigContainer.innerHTML = getUrlConfigHtml();
    3378         addClass( urlConfigContainer, "qunit-url-config" );
    3379 
    3380         // For oldIE support:
    3381         // * Add handlers to the individual elements instead of the container
    3382         // * Use "click" instead of "change" for checkboxes
    3383         addEvents( urlConfigContainer.getElementsByTagName( "input" ), "click", toolbarChanged );
    3384         addEvents( urlConfigContainer.getElementsByTagName( "select" ), "change", toolbarChanged );
    3385 
    3386         return urlConfigContainer;
    3387 }
    3388 
    3389 function toolbarLooseFilter() {
    3390         var filter = document.createElement( "form" ),
    3391                 label = document.createElement( "label" ),
    3392                 input = document.createElement( "input" ),
    3393                 button = document.createElement( "button" );
    3394 
    3395         addClass( filter, "qunit-filter" );
    3396 
    3397         label.innerHTML = "Filter: ";
    3398 
    3399         input.type = "text";
    3400         input.value = config.filter || "";
    3401         input.name = "filter";
    3402         input.id = "qunit-filter-input";
    3403 
    3404         button.innerHTML = "Go";
    3405 
    3406         label.appendChild( input );
    3407 
    3408         filter.appendChild( label );
    3409         filter.appendChild( button );
    3410         addEvent( filter, "submit", function( ev ) {
    3411                 applyUrlParams();
    3412 
    3413                 if ( ev && ev.preventDefault ) {
    3414                         ev.preventDefault();
    3415                 }
    3416 
    3417                 return false;
    3418         });
    3419 
    3420         return filter;
    3421 }
    3422 
    3423 function toolbarModuleFilterHtml() {
    3424         var i,
    3425                 moduleFilterHtml = "";
    3426 
    3427         if ( !modulesList.length ) {
    3428                 return false;
    3429         }
    3430 
    3431         modulesList.sort(function( a, b ) {
    3432                 return a.localeCompare( b );
    3433         });
    3434 
    3435         moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label>" +
    3436                 "<select id='qunit-modulefilter' name='modulefilter'><option value='' " +
    3437                 ( QUnit.urlParams.module === undefined ? "selected='selected'" : "" ) +
    3438                 ">< All Modules ></option>";
    3439 
    3440         for ( i = 0; i < modulesList.length; i++ ) {
    3441                 moduleFilterHtml += "<option value='" +
    3442                         escapeText( encodeURIComponent( modulesList[ i ] ) ) + "' " +
    3443                         ( QUnit.urlParams.module === modulesList[ i ] ? "selected='selected'" : "" ) +
    3444                         ">" + escapeText( modulesList[ i ] ) + "</option>";
    3445         }
    3446         moduleFilterHtml += "</select>";
    3447 
    3448         return moduleFilterHtml;
    3449 }
    3450 
    3451 function toolbarModuleFilter() {
    3452         var toolbar = id( "qunit-testrunner-toolbar" ),
    3453                 moduleFilter = document.createElement( "span" ),
    3454                 moduleFilterHtml = toolbarModuleFilterHtml();
    3455 
    3456         if ( !toolbar || !moduleFilterHtml ) {
    3457                 return false;
    3458         }
    3459 
    3460         moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
    3461         moduleFilter.innerHTML = moduleFilterHtml;
    3462 
    3463         addEvent( moduleFilter.lastChild, "change", applyUrlParams );
    3464 
    3465         toolbar.appendChild( moduleFilter );
    3466 }
    3467 
    3468 function appendToolbar() {
    3469         var toolbar = id( "qunit-testrunner-toolbar" );
    3470 
    3471         if ( toolbar ) {
    3472                 toolbar.appendChild( toolbarUrlConfigContainer() );
    3473                 toolbar.appendChild( toolbarLooseFilter() );
    3474         }
    3475 }
    3476 
    3477 function appendHeader() {
    3478         var header = id( "qunit-header" );
    3479 
    3480         if ( header ) {
    3481                 header.innerHTML = "<a href='" +
    3482                         setUrl({ filter: undefined, module: undefined, testId: undefined }) +
    3483                         "'>" + header.innerHTML + "</a> ";
    3484         }
    3485 }
    3486 
    3487 function appendBanner() {
    3488         var banner = id( "qunit-banner" );
    3489 
    3490         if ( banner ) {
    3491                 banner.className = "";
    3492         }
    3493 }
    3494 
    3495 function appendTestResults() {
    3496         var tests = id( "qunit-tests" ),
    3497                 result = id( "qunit-testresult" );
    3498 
    3499         if ( result ) {
    3500                 result.parentNode.removeChild( result );
    3501         }
    3502 
    3503         if ( tests ) {
    3504                 tests.innerHTML = "";
    3505                 result = document.createElement( "p" );
    3506                 result.id = "qunit-testresult";
    3507                 result.className = "result";
    3508                 tests.parentNode.insertBefore( result, tests );
    3509                 result.innerHTML = "Running...<br />&#160;";
    3510         }
    3511 }
    3512 
    3513 function storeFixture() {
    3514         var fixture = id( "qunit-fixture" );
    3515         if ( fixture ) {
    3516                 config.fixture = fixture.innerHTML;
    3517         }
    3518 }
    3519 
    3520 function appendUserAgent() {
    3521         var userAgent = id( "qunit-userAgent" );
    3522 
    3523         if ( userAgent ) {
    3524                 userAgent.innerHTML = "";
    3525                 userAgent.appendChild(
    3526                         document.createTextNode(
    3527                                 "QUnit " + QUnit.version  + "; " + navigator.userAgent
    3528                         )
    3529                 );
    3530         }
    3531 }
    3532 
    3533 function appendTestsList( modules ) {
    3534         var i, l, x, z, test, moduleObj;
    3535 
    3536         for ( i = 0, l = modules.length; i < l; i++ ) {
    3537                 moduleObj = modules[ i ];
    3538 
    3539                 if ( moduleObj.name ) {
    3540                         modulesList.push( moduleObj.name );
    3541                 }
    3542 
    3543                 for ( x = 0, z = moduleObj.tests.length; x < z; x++ ) {
    3544                         test = moduleObj.tests[ x ];
    3545 
    3546                         appendTest( test.name, test.testId, moduleObj.name );
    3547                 }
    3548         }
    3549 }
    3550 
    3551 function appendTest( name, testId, moduleName ) {
    3552         var title, rerunTrigger, testBlock, assertList,
    3553                 tests = id( "qunit-tests" );
    3554 
    3555         if ( !tests ) {
    3556                 return;
    3557         }
    3558 
    3559         title = document.createElement( "strong" );
    3560         title.innerHTML = getNameHtml( name, moduleName );
    3561 
    3562         rerunTrigger = document.createElement( "a" );
    3563         rerunTrigger.innerHTML = "Rerun";
    3564         rerunTrigger.href = setUrl({ testId: testId });
    3565 
    3566         testBlock = document.createElement( "li" );
    3567         testBlock.appendChild( title );
    3568         testBlock.appendChild( rerunTrigger );
    3569         testBlock.id = "qunit-test-output-" + testId;
    3570 
    3571         assertList = document.createElement( "ol" );
    3572         assertList.className = "qunit-assert-list";
    3573 
    3574         testBlock.appendChild( assertList );
    3575 
    3576         tests.appendChild( testBlock );
    3577 }
    3578 
    3579 // HTML Reporter initialization and load
    3580 QUnit.begin(function( details ) {
    3581         var qunit = id( "qunit" );
    3582 
    3583         // Fixture is the only one necessary to run without the #qunit element
    3584         storeFixture();
    3585 
    3586         if ( qunit ) {
    3587                 qunit.innerHTML =
    3588                         "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
    3589                         "<h2 id='qunit-banner'></h2>" +
    3590                         "<div id='qunit-testrunner-toolbar'></div>" +
    3591                         "<h2 id='qunit-userAgent'></h2>" +
    3592                         "<ol id='qunit-tests'></ol>";
    3593         }
    3594 
    3595         appendHeader();
    3596         appendBanner();
    3597         appendTestResults();
    3598         appendUserAgent();
    3599         appendToolbar();
    3600         appendTestsList( details.modules );
    3601         toolbarModuleFilter();
    3602 
    3603         if ( qunit && config.hidepassed ) {
    3604                 addClass( qunit.lastChild, "hidepass" );
    3605         }
    3606 });
    3607 
    3608 QUnit.done(function( details ) {
    3609         var i, key,
    3610                 banner = id( "qunit-banner" ),
    3611                 tests = id( "qunit-tests" ),
    3612                 html = [
    3613                         "Tests completed in ",
    3614                         details.runtime,
    3615                         " milliseconds.<br />",
    3616                         "<span class='passed'>",
    3617                         details.passed,
    3618                         "</span> assertions of <span class='total'>",
    3619                         details.total,
    3620                         "</span> passed, <span class='failed'>",
    3621                         details.failed,
    3622                         "</span> failed."
    3623                 ].join( "" );
    3624 
    3625         if ( banner ) {
    3626                 banner.className = details.failed ? "qunit-fail" : "qunit-pass";
    3627         }
    3628 
    3629         if ( tests ) {
    3630                 id( "qunit-testresult" ).innerHTML = html;
    3631         }
    3632 
    3633         if ( config.altertitle && defined.document && document.title ) {
    3634 
    3635                 // show ✖ for good, ✔ for bad suite result in title
    3636                 // use escape sequences in case file gets loaded with non-utf-8-charset
    3637                 document.title = [
    3638                         ( details.failed ? "\u2716" : "\u2714" ),
    3639                         document.title.replace( /^[\u2714\u2716] /i, "" )
    3640                 ].join( " " );
    3641         }
    3642 
    3643         // clear own sessionStorage items if all tests passed
    3644         if ( config.reorder && defined.sessionStorage && details.failed === 0 ) {
    3645                 for ( i = 0; i < sessionStorage.length; i++ ) {
    3646                         key = sessionStorage.key( i++ );
    3647                         if ( key.indexOf( "qunit-test-" ) === 0 ) {
    3648                                 sessionStorage.removeItem( key );
    3649                         }
    3650                 }
    3651         }
    3652 
    3653         // scroll back to top to show results
    3654         if ( config.scrolltop && window.scrollTo ) {
    3655                 window.scrollTo( 0, 0 );
    3656         }
    3657 });
    3658 
    3659 function getNameHtml( name, module ) {
    3660         var nameHtml = "";
    3661 
    3662         if ( module ) {
    3663                 nameHtml = "<span class='module-name'>" + escapeText( module ) + "</span>: ";
    3664         }
    3665 
    3666         nameHtml += "<span class='test-name'>" + escapeText( name ) + "</span>";
    3667 
    3668         return nameHtml;
    3669 }
    3670 
    3671 QUnit.testStart(function( details ) {
    3672         var running, testBlock, bad;
    3673 
    3674         testBlock = id( "qunit-test-output-" + details.testId );
    3675         if ( testBlock ) {
    3676                 testBlock.className = "running";
    3677         } else {
    3678 
    3679                 // Report later registered tests
    3680                 appendTest( details.name, details.testId, details.module );
    3681         }
    3682 
    3683         running = id( "qunit-testresult" );
    3684         if ( running ) {
    3685                 bad = QUnit.config.reorder && defined.sessionStorage &&
    3686                         +sessionStorage.getItem( "qunit-test-" + details.module + "-" + details.name );
    3687 
    3688                 running.innerHTML = ( bad ?
    3689                         "Rerunning previously failed test: <br />" :
    3690                         "Running: <br />" ) +
    3691                         getNameHtml( details.name, details.module );
    3692         }
    3693 
    3694 });
    3695 
    3696 QUnit.log(function( details ) {
    3697         var assertList, assertLi,
    3698                 message, expected, actual,
    3699                 testItem = id( "qunit-test-output-" + details.testId );
    3700 
    3701         if ( !testItem ) {
    3702                 return;
    3703         }
    3704 
    3705         message = escapeText( details.message ) || ( details.result ? "okay" : "failed" );
    3706         message = "<span class='test-message'>" + message + "</span>";
    3707         message += "<span class='runtime'>@ " + details.runtime + " ms</span>";
    3708 
    3709         // pushFailure doesn't provide details.expected
    3710         // when it calls, it's implicit to also not show expected and diff stuff
    3711         // Also, we need to check details.expected existence, as it can exist and be undefined
    3712         if ( !details.result && hasOwn.call( details, "expected" ) ) {
    3713                 expected = escapeText( QUnit.dump.parse( details.expected ) );
    3714                 actual = escapeText( QUnit.dump.parse( details.actual ) );
    3715                 message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" +
    3716                         expected +
    3717                         "</pre></td></tr>";
    3718 
    3719                 if ( actual !== expected ) {
    3720                         message += "<tr class='test-actual'><th>Result: </th><td><pre>" +
    3721                                 actual + "</pre></td></tr>" +
    3722                                 "<tr class='test-diff'><th>Diff: </th><td><pre>" +
    3723                                 QUnit.diff( expected, actual ) + "</pre></td></tr>";
    3724                 } else {
    3725                         if ( expected.indexOf( "[object Array]" ) !== -1 ||
    3726                                         expected.indexOf( "[object Object]" ) !== -1 ) {
    3727                                 message += "<tr class='test-message'><th>Message: </th><td>" +
    3728                                         "Diff suppressed as the depth of object is more than current max depth (" +
    3729                                         QUnit.config.maxDepth + ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " +
    3730                                         " run with a higher max depth or <a href='" + setUrl({ maxDepth: -1 }) + "'>" +
    3731                                         "Rerun</a> without max depth.</p></td></tr>";
    3732                         }
    3733                 }
    3734 
    3735                 if ( details.source ) {
    3736                         message += "<tr class='test-source'><th>Source: </th><td><pre>" +
    3737                                 escapeText( details.source ) + "</pre></td></tr>";
    3738                 }
    3739 
    3740                 message += "</table>";
    3741 
    3742         // this occours when pushFailure is set and we have an extracted stack trace
    3743         } else if ( !details.result && details.source ) {
    3744                 message += "<table>" +
    3745                         "<tr class='test-source'><th>Source: </th><td><pre>" +
    3746                         escapeText( details.source ) + "</pre></td></tr>" +
    3747                         "</table>";
    3748         }
    3749 
    3750         assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
    3751 
    3752         assertLi = document.createElement( "li" );
    3753         assertLi.className = details.result ? "pass" : "fail";
    3754         assertLi.innerHTML = message;
    3755         assertList.appendChild( assertLi );
    3756 });
    3757 
    3758 QUnit.testDone(function( details ) {
    3759         var testTitle, time, testItem, assertList,
    3760                 good, bad, testCounts, skipped,
    3761                 tests = id( "qunit-tests" );
    3762 
    3763         if ( !tests ) {
    3764                 return;
    3765         }
    3766 
    3767         testItem = id( "qunit-test-output-" + details.testId );
    3768 
    3769         assertList = testItem.getElementsByTagName( "ol" )[ 0 ];
    3770 
    3771         good = details.passed;
    3772         bad = details.failed;
    3773 
    3774         // store result when possible
    3775         if ( config.reorder && defined.sessionStorage ) {
    3776                 if ( bad ) {
    3777                         sessionStorage.setItem( "qunit-test-" + details.module + "-" + details.name, bad );
    3778                 } else {
    3779                         sessionStorage.removeItem( "qunit-test-" + details.module + "-" + details.name );
    3780                 }
    3781         }
    3782 
    3783         if ( bad === 0 ) {
    3784                 addClass( assertList, "qunit-collapsed" );
    3785         }
    3786 
    3787         // testItem.firstChild is the test name
    3788         testTitle = testItem.firstChild;
    3789 
    3790         testCounts = bad ?
    3791                 "<b class='failed'>" + bad + "</b>, " + "<b class='passed'>" + good + "</b>, " :
    3792                 "";
    3793 
    3794         testTitle.innerHTML += " <b class='counts'>(" + testCounts +
    3795                 details.assertions.length + ")</b>";
    3796 
    3797         if ( details.skipped ) {
    3798                 testItem.className = "skipped";
    3799                 skipped = document.createElement( "em" );
    3800                 skipped.className = "qunit-skipped-label";
    3801                 skipped.innerHTML = "skipped";
    3802                 testItem.insertBefore( skipped, testTitle );
    3803         } else {
    3804                 addEvent( testTitle, "click", function() {
    3805                         toggleClass( assertList, "qunit-collapsed" );
    3806                 });
    3807 
    3808                 testItem.className = bad ? "fail" : "pass";
    3809 
    3810                 time = document.createElement( "span" );
    3811                 time.className = "runtime";
    3812                 time.innerHTML = details.runtime + " ms";
    3813                 testItem.insertBefore( time, assertList );
    3814         }
    3815 });
    3816 
    3817 if ( defined.document ) {
    3818         if ( document.readyState === "complete" ) {
    3819                 QUnit.load();
    3820         } else {
    3821                 addEvent( window, "load", QUnit.load );
    3822         }
    3823 } else {
    3824         config.pageLoaded = true;
    3825         config.autorun = true;
    3826 }
    3827 
    3828 })();
  • tests/qunit/vendor/sinon-qunit.js

    Property changes on: tests/qunit/vendor/qunit.js
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    1 /**
    2  * sinon-qunit 1.0.0, 2010/12/09
    3  *
    4  * @author Christian Johansen (christian@cjohansen.no)
    5  *
    6  * (The BSD License)
    7  *
    8  * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no
    9  * All rights reserved.
    10  *
    11  * Redistribution and use in source and binary forms, with or without modification,
    12  * are permitted provided that the following conditions are met:
    13  *
    14  *     * Redistributions of source code must retain the above copyright notice,
    15  *       this list of conditions and the following disclaimer.
    16  *     * Redistributions in binary form must reproduce the above copyright notice,
    17  *       this list of conditions and the following disclaimer in the documentation
    18  *       and/or other materials provided with the distribution.
    19  *     * Neither the name of Christian Johansen nor the names of his contributors
    20  *       may be used to endorse or promote products derived from this software
    21  *       without specific prior written permission.
    22  *
    23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    25  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    26  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    30  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    32  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    33  */
    34 /*global sinon, QUnit, test*/
    35 sinon.assert.fail = function (msg) {
    36     QUnit.ok(false, msg);
    37 };
    38 
    39 sinon.assert.pass = function (assertion) {
    40     QUnit.ok(true, assertion);
    41 };
    42 
    43 sinon.config = {
    44     injectIntoThis: true,
    45     injectInto: null,
    46     properties: ["spy", "stub", "mock", "clock", "sandbox"],
    47     useFakeTimers: true,
    48     useFakeServer: false
    49 };
    50 
    51 (function (global) {
    52     var qTest = QUnit.test;
    53    
    54     QUnit.test = global.test = function (testName, expected, callback, async) {
    55         if (arguments.length === 2) {
    56             callback = expected;
    57             expected = null;
    58         }
    59 
    60 
    61         return qTest(testName, expected, sinon.test(callback), async);
    62     };
    63 }(this));
  • tests/qunit/vendor/sinon.js

    Property changes on: tests/qunit/vendor/sinon-qunit.js
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    1 /**
    2  * Sinon.JS 1.8.2, 2014/02/11
    3  *
    4  * @author Christian Johansen (christian@cjohansen.no)
    5  * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
    6  *
    7  * (The BSD License)
    8  *
    9  * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no
    10  * All rights reserved.
    11  *
    12  * Redistribution and use in source and binary forms, with or without modification,
    13  * are permitted provided that the following conditions are met:
    14  *
    15  *     * Redistributions of source code must retain the above copyright notice,
    16  *       this list of conditions and the following disclaimer.
    17  *     * Redistributions in binary form must reproduce the above copyright notice,
    18  *       this list of conditions and the following disclaimer in the documentation
    19  *       and/or other materials provided with the distribution.
    20  *     * Neither the name of Christian Johansen nor the names of his contributors
    21  *       may be used to endorse or promote products derived from this software
    22  *       without specific prior written permission.
    23  *
    24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
    25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    26  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    27  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
    28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    30  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    31  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    33  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    34  */
    35 
    36 this.sinon = (function () {
    37 var samsam, formatio;
    38 function define(mod, deps, fn) { if (mod == "samsam") { samsam = deps(); } else { formatio = fn(samsam); } }
    39 define.amd = true;
    40 ((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) ||
    41  (typeof module === "object" &&
    42       function (m) { module.exports = m(); }) || // Node
    43  function (m) { this.samsam = m(); } // Browser globals
    44 )(function () {
    45     var o = Object.prototype;
    46     var div = typeof document !== "undefined" && document.createElement("div");
    47 
    48     function isNaN(value) {
    49         // Unlike global isNaN, this avoids type coercion
    50         // typeof check avoids IE host object issues, hat tip to
    51         // lodash
    52         var val = value; // JsLint thinks value !== value is "weird"
    53         return typeof value === "number" && value !== val;
    54     }
    55 
    56     function getClass(value) {
    57         // Returns the internal [[Class]] by calling Object.prototype.toString
    58         // with the provided value as this. Return value is a string, naming the
    59         // internal class, e.g. "Array"
    60         return o.toString.call(value).split(/[ \]]/)[1];
    61     }
    62 
    63     /**
    64      * @name samsam.isArguments
    65      * @param Object object
    66      *
    67      * Returns ``true`` if ``object`` is an ``arguments`` object,
    68      * ``false`` otherwise.
    69      */
    70     function isArguments(object) {
    71         if (typeof object !== "object" || typeof object.length !== "number" ||
    72                 getClass(object) === "Array") {
    73             return false;
    74         }
    75         if (typeof object.callee == "function") { return true; }
    76         try {
    77             object[object.length] = 6;
    78             delete object[object.length];
    79         } catch (e) {
    80             return true;
    81         }
    82         return false;
    83     }
    84 
    85     /**
    86      * @name samsam.isElement
    87      * @param Object object
    88      *
    89      * Returns ``true`` if ``object`` is a DOM element node. Unlike
    90      * Underscore.js/lodash, this function will return ``false`` if ``object``
    91      * is an *element-like* object, i.e. a regular object with a ``nodeType``
    92      * property that holds the value ``1``.
    93      */
    94     function isElement(object) {
    95         if (!object || object.nodeType !== 1 || !div) { return false; }
    96         try {
    97             object.appendChild(div);
    98             object.removeChild(div);
    99         } catch (e) {
    100             return false;
    101         }
    102         return true;
    103     }
    104 
    105     /**
    106      * @name samsam.keys
    107      * @param Object object
    108      *
    109      * Return an array of own property names.
    110      */
    111     function keys(object) {
    112         var ks = [], prop;
    113         for (prop in object) {
    114             if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); }
    115         }
    116         return ks;
    117     }
    118 
    119     /**
    120      * @name samsam.isDate
    121      * @param Object value
    122      *
    123      * Returns true if the object is a ``Date``, or *date-like*. Duck typing
    124      * of date objects work by checking that the object has a ``getTime``
    125      * function whose return value equals the return value from the object's
    126      * ``valueOf``.
    127      */
    128     function isDate(value) {
    129         return typeof value.getTime == "function" &&
    130             value.getTime() == value.valueOf();
    131     }
    132 
    133     /**
    134      * @name samsam.isNegZero
    135      * @param Object value
    136      *
    137      * Returns ``true`` if ``value`` is ``-0``.
    138      */
    139     function isNegZero(value) {
    140         return value === 0 && 1 / value === -Infinity;
    141     }
    142 
    143     /**
    144      * @name samsam.equal
    145      * @param Object obj1
    146      * @param Object obj2
    147      *
    148      * Returns ``true`` if two objects are strictly equal. Compared to
    149      * ``===`` there are two exceptions:
    150      *
    151      *   - NaN is considered equal to NaN
    152      *   - -0 and +0 are not considered equal
    153      */
    154     function identical(obj1, obj2) {
    155         if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) {
    156             return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2);
    157         }
    158     }
    159 
    160 
    161     /**
    162      * @name samsam.deepEqual
    163      * @param Object obj1
    164      * @param Object obj2
    165      *
    166      * Deep equal comparison. Two values are "deep equal" if:
    167      *
    168      *   - They are equal, according to samsam.identical
    169      *   - They are both date objects representing the same time
    170      *   - They are both arrays containing elements that are all deepEqual
    171      *   - They are objects with the same set of properties, and each property
    172      *     in ``obj1`` is deepEqual to the corresponding property in ``obj2``
    173      *
    174      * Supports cyclic objects.
    175      */
    176     function deepEqualCyclic(obj1, obj2) {
    177 
    178         // used for cyclic comparison
    179         // contain already visited objects
    180         var objects1 = [],
    181             objects2 = [],
    182         // contain pathes (position in the object structure)
    183         // of the already visited objects
    184         // indexes same as in objects arrays
    185             paths1 = [],
    186             paths2 = [],
    187         // contains combinations of already compared objects
    188         // in the manner: { "$1['ref']$2['ref']": true }
    189             compared = {};
    190 
    191         /**
    192          * used to check, if the value of a property is an object
    193          * (cyclic logic is only needed for objects)
    194          * only needed for cyclic logic
    195          */
    196         function isObject(value) {
    197 
    198             if (typeof value === 'object' && value !== null &&
    199                     !(value instanceof Boolean) &&
    200                     !(value instanceof Date)    &&
    201                     !(value instanceof Number)  &&
    202                     !(value instanceof RegExp)  &&
    203                     !(value instanceof String)) {
    204 
    205                 return true;
    206             }
    207 
    208             return false;
    209         }
    210 
    211         /**
    212          * returns the index of the given object in the
    213          * given objects array, -1 if not contained
    214          * only needed for cyclic logic
    215          */
    216         function getIndex(objects, obj) {
    217 
    218             var i;
    219             for (i = 0; i < objects.length; i++) {
    220                 if (objects[i] === obj) {
    221                     return i;
    222                 }
    223             }
    224 
    225             return -1;
    226         }
    227 
    228         // does the recursion for the deep equal check
    229         return (function deepEqual(obj1, obj2, path1, path2) {
    230             var type1 = typeof obj1;
    231             var type2 = typeof obj2;
    232 
    233             // == null also matches undefined
    234             if (obj1 === obj2 ||
    235                     isNaN(obj1) || isNaN(obj2) ||
    236                     obj1 == null || obj2 == null ||
    237                     type1 !== "object" || type2 !== "object") {
    238 
    239                 return identical(obj1, obj2);
    240             }
    241 
    242             // Elements are only equal if identical(expected, actual)
    243             if (isElement(obj1) || isElement(obj2)) { return false; }
    244 
    245             var isDate1 = isDate(obj1), isDate2 = isDate(obj2);
    246             if (isDate1 || isDate2) {
    247                 if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) {
    248                     return false;
    249                 }
    250             }
    251 
    252             if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
    253                 if (obj1.toString() !== obj2.toString()) { return false; }
    254             }
    255 
    256             var class1 = getClass(obj1);
    257             var class2 = getClass(obj2);
    258             var keys1 = keys(obj1);
    259             var keys2 = keys(obj2);
    260 
    261             if (isArguments(obj1) || isArguments(obj2)) {
    262                 if (obj1.length !== obj2.length) { return false; }
    263             } else {
    264                 if (type1 !== type2 || class1 !== class2 ||
    265                         keys1.length !== keys2.length) {
    266                     return false;
    267                 }
    268             }
    269 
    270             var key, i, l,
    271                 // following vars are used for the cyclic logic
    272                 value1, value2,
    273                 isObject1, isObject2,
    274                 index1, index2,
    275                 newPath1, newPath2;
    276 
    277             for (i = 0, l = keys1.length; i < l; i++) {
    278                 key = keys1[i];
    279                 if (!o.hasOwnProperty.call(obj2, key)) {
    280                     return false;
    281                 }
    282 
    283                 // Start of the cyclic logic
    284 
    285                 value1 = obj1[key];
    286                 value2 = obj2[key];
    287 
    288                 isObject1 = isObject(value1);
    289                 isObject2 = isObject(value2);
    290 
    291                 // determine, if the objects were already visited
    292                 // (it's faster to check for isObject first, than to
    293                 // get -1 from getIndex for non objects)
    294                 index1 = isObject1 ? getIndex(objects1, value1) : -1;
    295                 index2 = isObject2 ? getIndex(objects2, value2) : -1;
    296 
    297                 // determine the new pathes of the objects
    298                 // - for non cyclic objects the current path will be extended
    299                 //   by current property name
    300                 // - for cyclic objects the stored path is taken
    301                 newPath1 = index1 !== -1
    302                     ? paths1[index1]
    303                     : path1 + '[' + JSON.stringify(key) + ']';
    304                 newPath2 = index2 !== -1
    305                     ? paths2[index2]
    306                     : path2 + '[' + JSON.stringify(key) + ']';
    307 
    308                 // stop recursion if current objects are already compared
    309                 if (compared[newPath1 + newPath2]) {
    310                     return true;
    311                 }
    312 
    313                 // remember the current objects and their pathes
    314                 if (index1 === -1 && isObject1) {
    315                     objects1.push(value1);
    316                     paths1.push(newPath1);
    317                 }
    318                 if (index2 === -1 && isObject2) {
    319                     objects2.push(value2);
    320                     paths2.push(newPath2);
    321                 }
    322 
    323                 // remember that the current objects are already compared
    324                 if (isObject1 && isObject2) {
    325                     compared[newPath1 + newPath2] = true;
    326                 }
    327 
    328                 // End of cyclic logic
    329 
    330                 // neither value1 nor value2 is a cycle
    331                 // continue with next level
    332                 if (!deepEqual(value1, value2, newPath1, newPath2)) {
    333                     return false;
    334                 }
    335             }
    336 
    337             return true;
    338 
    339         }(obj1, obj2, '$1', '$2'));
    340     }
    341 
    342     var match;
    343 
    344     function arrayContains(array, subset) {
    345         if (subset.length === 0) { return true; }
    346         var i, l, j, k;
    347         for (i = 0, l = array.length; i < l; ++i) {
    348             if (match(array[i], subset[0])) {
    349                 for (j = 0, k = subset.length; j < k; ++j) {
    350                     if (!match(array[i + j], subset[j])) { return false; }
    351                 }
    352                 return true;
    353             }
    354         }
    355         return false;
    356     }
    357 
    358     /**
    359      * @name samsam.match
    360      * @param Object object
    361      * @param Object matcher
    362      *
    363      * Compare arbitrary value ``object`` with matcher.
    364      */
    365     match = function match(object, matcher) {
    366         if (matcher && typeof matcher.test === "function") {
    367             return matcher.test(object);
    368         }
    369 
    370         if (typeof matcher === "function") {
    371             return matcher(object) === true;
    372         }
    373 
    374         if (typeof matcher === "string") {
    375             matcher = matcher.toLowerCase();
    376             var notNull = typeof object === "string" || !!object;
    377             return notNull &&
    378                 (String(object)).toLowerCase().indexOf(matcher) >= 0;
    379         }
    380 
    381         if (typeof matcher === "number") {
    382             return matcher === object;
    383         }
    384 
    385         if (typeof matcher === "boolean") {
    386             return matcher === object;
    387         }
    388 
    389         if (getClass(object) === "Array" && getClass(matcher) === "Array") {
    390             return arrayContains(object, matcher);
    391         }
    392 
    393         if (matcher && typeof matcher === "object") {
    394             var prop;
    395             for (prop in matcher) {
    396                 if (!match(object[prop], matcher[prop])) {
    397                     return false;
    398                 }
    399             }
    400             return true;
    401         }
    402 
    403         throw new Error("Matcher was not a string, a number, a " +
    404                         "function, a boolean or an object");
    405     };
    406 
    407     return {
    408         isArguments: isArguments,
    409         isElement: isElement,
    410         isDate: isDate,
    411         isNegZero: isNegZero,
    412         identical: identical,
    413         deepEqual: deepEqualCyclic,
    414         match: match,
    415         keys: keys
    416     };
    417 });
    418 ((typeof define === "function" && define.amd && function (m) {
    419     define("formatio", ["samsam"], m);
    420 }) || (typeof module === "object" && function (m) {
    421     module.exports = m(require("samsam"));
    422 }) || function (m) { this.formatio = m(this.samsam); }
    423 )(function (samsam) {
    424    
    425     var formatio = {
    426         excludeConstructors: ["Object", /^.$/],
    427         quoteStrings: true
    428     };
    429 
    430     var hasOwn = Object.prototype.hasOwnProperty;
    431 
    432     var specialObjects = [];
    433     if (typeof global !== "undefined") {
    434         specialObjects.push({ object: global, value: "[object global]" });
    435     }
    436     if (typeof document !== "undefined") {
    437         specialObjects.push({
    438             object: document,
    439             value: "[object HTMLDocument]"
    440         });
    441     }
    442     if (typeof window !== "undefined") {
    443         specialObjects.push({ object: window, value: "[object Window]" });
    444     }
    445 
    446     function functionName(func) {
    447         if (!func) { return ""; }
    448         if (func.displayName) { return func.displayName; }
    449         if (func.name) { return func.name; }
    450         var matches = func.toString().match(/function\s+([^\(]+)/m);
    451         return (matches && matches[1]) || "";
    452     }
    453 
    454     function constructorName(f, object) {
    455         var name = functionName(object && object.constructor);
    456         var excludes = f.excludeConstructors ||
    457                 formatio.excludeConstructors || [];
    458 
    459         var i, l;
    460         for (i = 0, l = excludes.length; i < l; ++i) {
    461             if (typeof excludes[i] === "string" && excludes[i] === name) {
    462                 return "";
    463             } else if (excludes[i].test && excludes[i].test(name)) {
    464                 return "";
    465             }
    466         }
    467 
    468         return name;
    469     }
    470 
    471     function isCircular(object, objects) {
    472         if (typeof object !== "object") { return false; }
    473         var i, l;
    474         for (i = 0, l = objects.length; i < l; ++i) {
    475             if (objects[i] === object) { return true; }
    476         }
    477         return false;
    478     }
    479 
    480     function ascii(f, object, processed, indent) {
    481         if (typeof object === "string") {
    482             var qs = f.quoteStrings;
    483             var quote = typeof qs !== "boolean" || qs;
    484             return processed || quote ? '"' + object + '"' : object;
    485         }
    486 
    487         if (typeof object === "function" && !(object instanceof RegExp)) {
    488             return ascii.func(object);
    489         }
    490 
    491         processed = processed || [];
    492 
    493         if (isCircular(object, processed)) { return "[Circular]"; }
    494 
    495         if (Object.prototype.toString.call(object) === "[object Array]") {
    496             return ascii.array.call(f, object, processed);
    497         }
    498 
    499         if (!object) { return String((1/object) === -Infinity ? "-0" : object); }
    500         if (samsam.isElement(object)) { return ascii.element(object); }
    501 
    502         if (typeof object.toString === "function" &&
    503                 object.toString !== Object.prototype.toString) {
    504             return object.toString();
    505         }
    506 
    507         var i, l;
    508         for (i = 0, l = specialObjects.length; i < l; i++) {
    509             if (object === specialObjects[i].object) {
    510                 return specialObjects[i].value;
    511             }
    512         }
    513 
    514         return ascii.object.call(f, object, processed, indent);
    515     }
    516 
    517     ascii.func = function (func) {
    518         return "function " + functionName(func) + "() {}";
    519     };
    520 
    521     ascii.array = function (array, processed) {
    522         processed = processed || [];
    523         processed.push(array);
    524         var i, l, pieces = [];
    525         for (i = 0, l = array.length; i < l; ++i) {
    526             pieces.push(ascii(this, array[i], processed));
    527         }
    528         return "[" + pieces.join(", ") + "]";
    529     };
    530 
    531     ascii.object = function (object, processed, indent) {
    532         processed = processed || [];
    533         processed.push(object);
    534         indent = indent || 0;
    535         var pieces = [], properties = samsam.keys(object).sort();
    536         var length = 3;
    537         var prop, str, obj, i, l;
    538 
    539         for (i = 0, l = properties.length; i < l; ++i) {
    540             prop = properties[i];
    541             obj = object[prop];
    542 
    543             if (isCircular(obj, processed)) {
    544                 str = "[Circular]";
    545             } else {
    546                 str = ascii(this, obj, processed, indent + 2);
    547             }
    548 
    549             str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
    550             length += str.length;
    551             pieces.push(str);
    552         }
    553 
    554         var cons = constructorName(this, object);
    555         var prefix = cons ? "[" + cons + "] " : "";
    556         var is = "";
    557         for (i = 0, l = indent; i < l; ++i) { is += " "; }
    558 
    559         if (length + indent > 80) {
    560             return prefix + "{\n  " + is + pieces.join(",\n  " + is) + "\n" +
    561                 is + "}";
    562         }
    563         return prefix + "{ " + pieces.join(", ") + " }";
    564     };
    565 
    566     ascii.element = function (element) {
    567         var tagName = element.tagName.toLowerCase();
    568         var attrs = element.attributes, attr, pairs = [], attrName, i, l, val;
    569 
    570         for (i = 0, l = attrs.length; i < l; ++i) {
    571             attr = attrs.item(i);
    572             attrName = attr.nodeName.toLowerCase().replace("html:", "");
    573             val = attr.nodeValue;
    574             if (attrName !== "contenteditable" || val !== "inherit") {
    575                 if (!!val) { pairs.push(attrName + "=\"" + val + "\""); }
    576             }
    577         }
    578 
    579         var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
    580         var content = element.innerHTML;
    581 
    582         if (content.length > 20) {
    583             content = content.substr(0, 20) + "[...]";
    584         }
    585 
    586         var res = formatted + pairs.join(" ") + ">" + content +
    587                 "</" + tagName + ">";
    588 
    589         return res.replace(/ contentEditable="inherit"/, "");
    590     };
    591 
    592     function Formatio(options) {
    593         for (var opt in options) {
    594             this[opt] = options[opt];
    595         }
    596     }
    597 
    598     Formatio.prototype = {
    599         functionName: functionName,
    600 
    601         configure: function (options) {
    602             return new Formatio(options);
    603         },
    604 
    605         constructorName: function (object) {
    606             return constructorName(this, object);
    607         },
    608 
    609         ascii: function (object, processed, indent) {
    610             return ascii(this, object, processed, indent);
    611         }
    612     };
    613 
    614     return Formatio.prototype;
    615 });
    616 /*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/
    617 /*global module, require, __dirname, document*/
    618 /**
    619  * Sinon core utilities. For internal use only.
    620  *
    621  * @author Christian Johansen (christian@cjohansen.no)
    622  * @license BSD
    623  *
    624  * Copyright (c) 2010-2013 Christian Johansen
    625  */
    626 
    627 var sinon = (function (formatio) {
    628     var div = typeof document != "undefined" && document.createElement("div");
    629     var hasOwn = Object.prototype.hasOwnProperty;
    630 
    631     function isDOMNode(obj) {
    632         var success = false;
    633 
    634         try {
    635             obj.appendChild(div);
    636             success = div.parentNode == obj;
    637         } catch (e) {
    638             return false;
    639         } finally {
    640             try {
    641                 obj.removeChild(div);
    642             } catch (e) {
    643                 // Remove failed, not much we can do about that
    644             }
    645         }
    646 
    647         return success;
    648     }
    649 
    650     function isElement(obj) {
    651         return div && obj && obj.nodeType === 1 && isDOMNode(obj);
    652     }
    653 
    654     function isFunction(obj) {
    655         return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
    656     }
    657 
    658     function mirrorProperties(target, source) {
    659         for (var prop in source) {
    660             if (!hasOwn.call(target, prop)) {
    661                 target[prop] = source[prop];
    662             }
    663         }
    664     }
    665 
    666     function isRestorable (obj) {
    667         return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
    668     }
    669 
    670     var sinon = {
    671         wrapMethod: function wrapMethod(object, property, method) {
    672             if (!object) {
    673                 throw new TypeError("Should wrap property of object");
    674             }
    675 
    676             if (typeof method != "function") {
    677                 throw new TypeError("Method wrapper should be function");
    678             }
    679 
    680             var wrappedMethod = object[property],
    681                 error;
    682 
    683             if (!isFunction(wrappedMethod)) {
    684                 error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
    685                                     property + " as function");
    686             }
    687 
    688             if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
    689                 error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
    690             }
    691 
    692             if (wrappedMethod.calledBefore) {
    693                 var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
    694                 error = new TypeError("Attempted to wrap " + property + " which is already " + verb);
    695             }
    696 
    697             if (error) {
    698                 if (wrappedMethod._stack) {
    699                     error.stack += '\n--------------\n' + wrappedMethod._stack;
    700                 }
    701                 throw error;
    702             }
    703 
    704             // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem
    705             // when using hasOwn.call on objects from other frames.
    706             var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property);
    707             object[property] = method;
    708             method.displayName = property;
    709             // Set up a stack trace which can be used later to find what line of
    710             // code the original method was created on.
    711             method._stack = (new Error('Stack Trace for original')).stack;
    712 
    713             method.restore = function () {
    714                 // For prototype properties try to reset by delete first.
    715                 // If this fails (ex: localStorage on mobile safari) then force a reset
    716                 // via direct assignment.
    717                 if (!owned) {
    718                     delete object[property];
    719                 }
    720                 if (object[property] === method) {
    721                     object[property] = wrappedMethod;
    722                 }
    723             };
    724 
    725             method.restore.sinon = true;
    726             mirrorProperties(method, wrappedMethod);
    727 
    728             return method;
    729         },
    730 
    731         extend: function extend(target) {
    732             for (var i = 1, l = arguments.length; i < l; i += 1) {
    733                 for (var prop in arguments[i]) {
    734                     if (arguments[i].hasOwnProperty(prop)) {
    735                         target[prop] = arguments[i][prop];
    736                     }
    737 
    738                     // DONT ENUM bug, only care about toString
    739                     if (arguments[i].hasOwnProperty("toString") &&
    740                         arguments[i].toString != target.toString) {
    741                         target.toString = arguments[i].toString;
    742                     }
    743                 }
    744             }
    745 
    746             return target;
    747         },
    748 
    749         create: function create(proto) {
    750             var F = function () {};
    751             F.prototype = proto;
    752             return new F();
    753         },
    754 
    755         deepEqual: function deepEqual(a, b) {
    756             if (sinon.match && sinon.match.isMatcher(a)) {
    757                 return a.test(b);
    758             }
    759             if (typeof a != "object" || typeof b != "object") {
    760                 return a === b;
    761             }
    762 
    763             if (isElement(a) || isElement(b)) {
    764                 return a === b;
    765             }
    766 
    767             if (a === b) {
    768                 return true;
    769             }
    770 
    771             if ((a === null && b !== null) || (a !== null && b === null)) {
    772                 return false;
    773             }
    774 
    775             var aString = Object.prototype.toString.call(a);
    776             if (aString != Object.prototype.toString.call(b)) {
    777                 return false;
    778             }
    779 
    780             if (aString == "[object Date]") {
    781                 return a.valueOf() === b.valueOf();
    782             }
    783 
    784             var prop, aLength = 0, bLength = 0;
    785 
    786             if (aString == "[object Array]" && a.length !== b.length) {
    787                 return false;
    788             }
    789 
    790             for (prop in a) {
    791                 aLength += 1;
    792 
    793                 if (!deepEqual(a[prop], b[prop])) {
    794                     return false;
    795                 }
    796             }
    797 
    798             for (prop in b) {
    799                 bLength += 1;
    800             }
    801 
    802             return aLength == bLength;
    803         },
    804 
    805         functionName: function functionName(func) {
    806             var name = func.displayName || func.name;
    807 
    808             // Use function decomposition as a last resort to get function
    809             // name. Does not rely on function decomposition to work - if it
    810             // doesn't debugging will be slightly less informative
    811             // (i.e. toString will say 'spy' rather than 'myFunc').
    812             if (!name) {
    813                 var matches = func.toString().match(/function ([^\s\(]+)/);
    814                 name = matches && matches[1];
    815             }
    816 
    817             return name;
    818         },
    819 
    820         functionToString: function toString() {
    821             if (this.getCall && this.callCount) {
    822                 var thisValue, prop, i = this.callCount;
    823 
    824                 while (i--) {
    825                     thisValue = this.getCall(i).thisValue;
    826 
    827                     for (prop in thisValue) {
    828                         if (thisValue[prop] === this) {
    829                             return prop;
    830                         }
    831                     }
    832                 }
    833             }
    834 
    835             return this.displayName || "sinon fake";
    836         },
    837 
    838         getConfig: function (custom) {
    839             var config = {};
    840             custom = custom || {};
    841             var defaults = sinon.defaultConfig;
    842 
    843             for (var prop in defaults) {
    844                 if (defaults.hasOwnProperty(prop)) {
    845                     config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
    846                 }
    847             }
    848 
    849             return config;
    850         },
    851 
    852         format: function (val) {
    853             return "" + val;
    854         },
    855 
    856         defaultConfig: {
    857             injectIntoThis: true,
    858             injectInto: null,
    859             properties: ["spy", "stub", "mock", "clock", "server", "requests"],
    860             useFakeTimers: true,
    861             useFakeServer: true
    862         },
    863 
    864         timesInWords: function timesInWords(count) {
    865             return count == 1 && "once" ||
    866                 count == 2 && "twice" ||
    867                 count == 3 && "thrice" ||
    868                 (count || 0) + " times";
    869         },
    870 
    871         calledInOrder: function (spies) {
    872             for (var i = 1, l = spies.length; i < l; i++) {
    873                 if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
    874                     return false;
    875                 }
    876             }
    877 
    878             return true;
    879         },
    880 
    881         orderByFirstCall: function (spies) {
    882             return spies.sort(function (a, b) {
    883                 // uuid, won't ever be equal
    884                 var aCall = a.getCall(0);
    885                 var bCall = b.getCall(0);
    886                 var aId = aCall && aCall.callId || -1;
    887                 var bId = bCall && bCall.callId || -1;
    888 
    889                 return aId < bId ? -1 : 1;
    890             });
    891         },
    892 
    893         log: function () {},
    894 
    895         logError: function (label, err) {
    896             var msg = label + " threw exception: ";
    897             sinon.log(msg + "[" + err.name + "] " + err.message);
    898             if (err.stack) { sinon.log(err.stack); }
    899 
    900             setTimeout(function () {
    901                 err.message = msg + err.message;
    902                 throw err;
    903             }, 0);
    904         },
    905 
    906         typeOf: function (value) {
    907             if (value === null) {
    908                 return "null";
    909             }
    910             else if (value === undefined) {
    911                 return "undefined";
    912             }
    913             var string = Object.prototype.toString.call(value);
    914             return string.substring(8, string.length - 1).toLowerCase();
    915         },
    916 
    917         createStubInstance: function (constructor) {
    918             if (typeof constructor !== "function") {
    919                 throw new TypeError("The constructor should be a function.");
    920             }
    921             return sinon.stub(sinon.create(constructor.prototype));
    922         },
    923 
    924         restore: function (object) {
    925             if (object !== null && typeof object === "object") {
    926                 for (var prop in object) {
    927                     if (isRestorable(object[prop])) {
    928                         object[prop].restore();
    929                     }
    930                 }
    931             }
    932             else if (isRestorable(object)) {
    933                 object.restore();
    934             }
    935         }
    936     };
    937 
    938     var isNode = typeof module !== "undefined" && module.exports;
    939     var isAMD = typeof define === 'function' && typeof define.amd === 'object' && define.amd;
    940 
    941     if (isAMD) {
    942         define(function(){
    943             return sinon;
    944         });
    945     } else if (isNode) {
    946         try {
    947             formatio = require("formatio");
    948         } catch (e) {}
    949         module.exports = sinon;
    950         module.exports.spy = require("./sinon/spy");
    951         module.exports.spyCall = require("./sinon/call");
    952         module.exports.behavior = require("./sinon/behavior");
    953         module.exports.stub = require("./sinon/stub");
    954         module.exports.mock = require("./sinon/mock");
    955         module.exports.collection = require("./sinon/collection");
    956         module.exports.assert = require("./sinon/assert");
    957         module.exports.sandbox = require("./sinon/sandbox");
    958         module.exports.test = require("./sinon/test");
    959         module.exports.testCase = require("./sinon/test_case");
    960         module.exports.assert = require("./sinon/assert");
    961         module.exports.match = require("./sinon/match");
    962     }
    963 
    964     if (formatio) {
    965         var formatter = formatio.configure({ quoteStrings: false });
    966         sinon.format = function () {
    967             return formatter.ascii.apply(formatter, arguments);
    968         };
    969     } else if (isNode) {
    970         try {
    971             var util = require("util");
    972             sinon.format = function (value) {
    973                 return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
    974             };
    975         } catch (e) {
    976             /* Node, but no util module - would be very old, but better safe than
    977              sorry */
    978         }
    979     }
    980 
    981     return sinon;
    982 }(typeof formatio == "object" && formatio));
    983 
    984 /* @depend ../sinon.js */
    985 /*jslint eqeqeq: false, onevar: false, plusplus: false*/
    986 /*global module, require, sinon*/
    987 /**
    988  * Match functions
    989  *
    990  * @author Maximilian Antoni (mail@maxantoni.de)
    991  * @license BSD
    992  *
    993  * Copyright (c) 2012 Maximilian Antoni
    994  */
    995 
    996 (function (sinon) {
    997     var commonJSModule = typeof module !== 'undefined' && module.exports;
    998 
    999     if (!sinon && commonJSModule) {
    1000         sinon = require("../sinon");
    1001     }
    1002 
    1003     if (!sinon) {
    1004         return;
    1005     }
    1006 
    1007     function assertType(value, type, name) {
    1008         var actual = sinon.typeOf(value);
    1009         if (actual !== type) {
    1010             throw new TypeError("Expected type of " + name + " to be " +
    1011                 type + ", but was " + actual);
    1012         }
    1013     }
    1014 
    1015     var matcher = {
    1016         toString: function () {
    1017             return this.message;
    1018         }
    1019     };
    1020 
    1021     function isMatcher(object) {
    1022         return matcher.isPrototypeOf(object);
    1023     }
    1024 
    1025     function matchObject(expectation, actual) {
    1026         if (actual === null || actual === undefined) {
    1027             return false;
    1028         }
    1029         for (var key in expectation) {
    1030             if (expectation.hasOwnProperty(key)) {
    1031                 var exp = expectation[key];
    1032                 var act = actual[key];
    1033                 if (match.isMatcher(exp)) {
    1034                     if (!exp.test(act)) {
    1035                         return false;
    1036                     }
    1037                 } else if (sinon.typeOf(exp) === "object") {
    1038                     if (!matchObject(exp, act)) {
    1039                         return false;
    1040                     }
    1041                 } else if (!sinon.deepEqual(exp, act)) {
    1042                     return false;
    1043                 }
    1044             }
    1045         }
    1046         return true;
    1047     }
    1048 
    1049     matcher.or = function (m2) {
    1050         if (!isMatcher(m2)) {
    1051             throw new TypeError("Matcher expected");
    1052         }
    1053         var m1 = this;
    1054         var or = sinon.create(matcher);
    1055         or.test = function (actual) {
    1056             return m1.test(actual) || m2.test(actual);
    1057         };
    1058         or.message = m1.message + ".or(" + m2.message + ")";
    1059         return or;
    1060     };
    1061 
    1062     matcher.and = function (m2) {
    1063         if (!isMatcher(m2)) {
    1064             throw new TypeError("Matcher expected");
    1065         }
    1066         var m1 = this;
    1067         var and = sinon.create(matcher);
    1068         and.test = function (actual) {
    1069             return m1.test(actual) && m2.test(actual);
    1070         };
    1071         and.message = m1.message + ".and(" + m2.message + ")";
    1072         return and;
    1073     };
    1074 
    1075     var match = function (expectation, message) {
    1076         var m = sinon.create(matcher);
    1077         var type = sinon.typeOf(expectation);
    1078         switch (type) {
    1079         case "object":
    1080             if (typeof expectation.test === "function") {
    1081                 m.test = function (actual) {
    1082                     return expectation.test(actual) === true;
    1083                 };
    1084                 m.message = "match(" + sinon.functionName(expectation.test) + ")";
    1085                 return m;
    1086             }
    1087             var str = [];
    1088             for (var key in expectation) {
    1089                 if (expectation.hasOwnProperty(key)) {
    1090                     str.push(key + ": " + expectation[key]);
    1091                 }
    1092             }
    1093             m.test = function (actual) {
    1094                 return matchObject(expectation, actual);
    1095             };
    1096             m.message = "match(" + str.join(", ") + ")";
    1097             break;
    1098         case "number":
    1099             m.test = function (actual) {
    1100                 return expectation == actual;
    1101             };
    1102             break;
    1103         case "string":
    1104             m.test = function (actual) {
    1105                 if (typeof actual !== "string") {
    1106                     return false;
    1107                 }
    1108                 return actual.indexOf(expectation) !== -1;
    1109             };
    1110             m.message = "match(\"" + expectation + "\")";
    1111             break;
    1112         case "regexp":
    1113             m.test = function (actual) {
    1114                 if (typeof actual !== "string") {
    1115                     return false;
    1116                 }
    1117                 return expectation.test(actual);
    1118             };
    1119             break;
    1120         case "function":
    1121             m.test = expectation;
    1122             if (message) {
    1123                 m.message = message;
    1124             } else {
    1125                 m.message = "match(" + sinon.functionName(expectation) + ")";
    1126             }
    1127             break;
    1128         default:
    1129             m.test = function (actual) {
    1130               return sinon.deepEqual(expectation, actual);
    1131             };
    1132         }
    1133         if (!m.message) {
    1134             m.message = "match(" + expectation + ")";
    1135         }
    1136         return m;
    1137     };
    1138 
    1139     match.isMatcher = isMatcher;
    1140 
    1141     match.any = match(function () {
    1142         return true;
    1143     }, "any");
    1144 
    1145     match.defined = match(function (actual) {
    1146         return actual !== null && actual !== undefined;
    1147     }, "defined");
    1148 
    1149     match.truthy = match(function (actual) {
    1150         return !!actual;
    1151     }, "truthy");
    1152 
    1153     match.falsy = match(function (actual) {
    1154         return !actual;
    1155     }, "falsy");
    1156 
    1157     match.same = function (expectation) {
    1158         return match(function (actual) {
    1159             return expectation === actual;
    1160         }, "same(" + expectation + ")");
    1161     };
    1162 
    1163     match.typeOf = function (type) {
    1164         assertType(type, "string", "type");
    1165         return match(function (actual) {
    1166             return sinon.typeOf(actual) === type;
    1167         }, "typeOf(\"" + type + "\")");
    1168     };
    1169 
    1170     match.instanceOf = function (type) {
    1171         assertType(type, "function", "type");
    1172         return match(function (actual) {
    1173             return actual instanceof type;
    1174         }, "instanceOf(" + sinon.functionName(type) + ")");
    1175     };
    1176 
    1177     function createPropertyMatcher(propertyTest, messagePrefix) {
    1178         return function (property, value) {
    1179             assertType(property, "string", "property");
    1180             var onlyProperty = arguments.length === 1;
    1181             var message = messagePrefix + "(\"" + property + "\"";
    1182             if (!onlyProperty) {
    1183                 message += ", " + value;
    1184             }
    1185             message += ")";
    1186             return match(function (actual) {
    1187                 if (actual === undefined || actual === null ||
    1188                         !propertyTest(actual, property)) {
    1189                     return false;
    1190                 }
    1191                 return onlyProperty || sinon.deepEqual(value, actual[property]);
    1192             }, message);
    1193         };
    1194     }
    1195 
    1196     match.has = createPropertyMatcher(function (actual, property) {
    1197         if (typeof actual === "object") {
    1198             return property in actual;
    1199         }
    1200         return actual[property] !== undefined;
    1201     }, "has");
    1202 
    1203     match.hasOwn = createPropertyMatcher(function (actual, property) {
    1204         return actual.hasOwnProperty(property);
    1205     }, "hasOwn");
    1206 
    1207     match.bool = match.typeOf("boolean");
    1208     match.number = match.typeOf("number");
    1209     match.string = match.typeOf("string");
    1210     match.object = match.typeOf("object");
    1211     match.func = match.typeOf("function");
    1212     match.array = match.typeOf("array");
    1213     match.regexp = match.typeOf("regexp");
    1214     match.date = match.typeOf("date");
    1215 
    1216     if (commonJSModule) {
    1217         module.exports = match;
    1218     } else {
    1219         sinon.match = match;
    1220     }
    1221 }(typeof sinon == "object" && sinon || null));
    1222 
    1223 /**
    1224   * @depend ../sinon.js
    1225   * @depend match.js
    1226   */
    1227 /*jslint eqeqeq: false, onevar: false, plusplus: false*/
    1228 /*global module, require, sinon*/
    1229 /**
    1230   * Spy calls
    1231   *
    1232   * @author Christian Johansen (christian@cjohansen.no)
    1233   * @author Maximilian Antoni (mail@maxantoni.de)
    1234   * @license BSD
    1235   *
    1236   * Copyright (c) 2010-2013 Christian Johansen
    1237   * Copyright (c) 2013 Maximilian Antoni
    1238   */
    1239 
    1240 (function (sinon) {
    1241     var commonJSModule = typeof module !== 'undefined' && module.exports;
    1242     if (!sinon && commonJSModule) {
    1243         sinon = require("../sinon");
    1244     }
    1245 
    1246     if (!sinon) {
    1247         return;
    1248     }
    1249 
    1250     function throwYieldError(proxy, text, args) {
    1251         var msg = sinon.functionName(proxy) + text;
    1252         if (args.length) {
    1253             msg += " Received [" + slice.call(args).join(", ") + "]";
    1254         }
    1255         throw new Error(msg);
    1256     }
    1257 
    1258     var slice = Array.prototype.slice;
    1259 
    1260     var callProto = {
    1261         calledOn: function calledOn(thisValue) {
    1262             if (sinon.match && sinon.match.isMatcher(thisValue)) {
    1263                 return thisValue.test(this.thisValue);
    1264             }
    1265             return this.thisValue === thisValue;
    1266         },
    1267 
    1268         calledWith: function calledWith() {
    1269             for (var i = 0, l = arguments.length; i < l; i += 1) {
    1270                 if (!sinon.deepEqual(arguments[i], this.args[i])) {
    1271                     return false;
    1272                 }
    1273             }
    1274 
    1275             return true;
    1276         },
    1277 
    1278         calledWithMatch: function calledWithMatch() {
    1279             for (var i = 0, l = arguments.length; i < l; i += 1) {
    1280                 var actual = this.args[i];
    1281                 var expectation = arguments[i];
    1282                 if (!sinon.match || !sinon.match(expectation).test(actual)) {
    1283                     return false;
    1284                 }
    1285             }
    1286             return true;
    1287         },
    1288 
    1289         calledWithExactly: function calledWithExactly() {
    1290             return arguments.length == this.args.length &&
    1291                 this.calledWith.apply(this, arguments);
    1292         },
    1293 
    1294         notCalledWith: function notCalledWith() {
    1295             return !this.calledWith.apply(this, arguments);
    1296         },
    1297 
    1298         notCalledWithMatch: function notCalledWithMatch() {
    1299             return !this.calledWithMatch.apply(this, arguments);
    1300         },
    1301 
    1302         returned: function returned(value) {
    1303             return sinon.deepEqual(value, this.returnValue);
    1304         },
    1305 
    1306         threw: function threw(error) {
    1307             if (typeof error === "undefined" || !this.exception) {
    1308                 return !!this.exception;
    1309             }
    1310 
    1311             return this.exception === error || this.exception.name === error;
    1312         },
    1313 
    1314         calledWithNew: function calledWithNew() {
    1315             return this.proxy.prototype && this.thisValue instanceof this.proxy;
    1316         },
    1317 
    1318         calledBefore: function (other) {
    1319             return this.callId < other.callId;
    1320         },
    1321 
    1322         calledAfter: function (other) {
    1323             return this.callId > other.callId;
    1324         },
    1325 
    1326         callArg: function (pos) {
    1327             this.args[pos]();
    1328         },
    1329 
    1330         callArgOn: function (pos, thisValue) {
    1331             this.args[pos].apply(thisValue);
    1332         },
    1333 
    1334         callArgWith: function (pos) {
    1335             this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
    1336         },
    1337 
    1338         callArgOnWith: function (pos, thisValue) {
    1339             var args = slice.call(arguments, 2);
    1340             this.args[pos].apply(thisValue, args);
    1341         },
    1342 
    1343         "yield": function () {
    1344             this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
    1345         },
    1346 
    1347         yieldOn: function (thisValue) {
    1348             var args = this.args;
    1349             for (var i = 0, l = args.length; i < l; ++i) {
    1350                 if (typeof args[i] === "function") {
    1351                     args[i].apply(thisValue, slice.call(arguments, 1));
    1352                     return;
    1353                 }
    1354             }
    1355             throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
    1356         },
    1357 
    1358         yieldTo: function (prop) {
    1359             this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
    1360         },
    1361 
    1362         yieldToOn: function (prop, thisValue) {
    1363             var args = this.args;
    1364             for (var i = 0, l = args.length; i < l; ++i) {
    1365                 if (args[i] && typeof args[i][prop] === "function") {
    1366                     args[i][prop].apply(thisValue, slice.call(arguments, 2));
    1367                     return;
    1368                 }
    1369             }
    1370             throwYieldError(this.proxy, " cannot yield to '" + prop +
    1371                 "' since no callback was passed.", args);
    1372         },
    1373 
    1374         toString: function () {
    1375             var callStr = this.proxy.toString() + "(";
    1376             var args = [];
    1377 
    1378             for (var i = 0, l = this.args.length; i < l; ++i) {
    1379                 args.push(sinon.format(this.args[i]));
    1380             }
    1381 
    1382             callStr = callStr + args.join(", ") + ")";
    1383 
    1384             if (typeof this.returnValue != "undefined") {
    1385                 callStr += " => " + sinon.format(this.returnValue);
    1386             }
    1387 
    1388             if (this.exception) {
    1389                 callStr += " !" + this.exception.name;
    1390 
    1391                 if (this.exception.message) {
    1392                     callStr += "(" + this.exception.message + ")";
    1393                 }
    1394             }
    1395 
    1396             return callStr;
    1397         }
    1398     };
    1399 
    1400     callProto.invokeCallback = callProto.yield;
    1401 
    1402     function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
    1403         if (typeof id !== "number") {
    1404             throw new TypeError("Call id is not a number");
    1405         }
    1406         var proxyCall = sinon.create(callProto);
    1407         proxyCall.proxy = spy;
    1408         proxyCall.thisValue = thisValue;
    1409         proxyCall.args = args;
    1410         proxyCall.returnValue = returnValue;
    1411         proxyCall.exception = exception;
    1412         proxyCall.callId = id;
    1413 
    1414         return proxyCall;
    1415     }
    1416     createSpyCall.toString = callProto.toString; // used by mocks
    1417 
    1418     if (commonJSModule) {
    1419         module.exports = createSpyCall;
    1420     } else {
    1421         sinon.spyCall = createSpyCall;
    1422     }
    1423 }(typeof sinon == "object" && sinon || null));
    1424 
    1425 
    1426 /**
    1427   * @depend ../sinon.js
    1428   * @depend call.js
    1429   */
    1430 /*jslint eqeqeq: false, onevar: false, plusplus: false*/
    1431 /*global module, require, sinon*/
    1432 /**
    1433   * Spy functions
    1434   *
    1435   * @author Christian Johansen (christian@cjohansen.no)
    1436   * @license BSD
    1437   *
    1438   * Copyright (c) 2010-2013 Christian Johansen
    1439   */
    1440 
    1441 (function (sinon) {
    1442     var commonJSModule = typeof module !== 'undefined' && module.exports;
    1443     var push = Array.prototype.push;
    1444     var slice = Array.prototype.slice;
    1445     var callId = 0;
    1446 
    1447     if (!sinon && commonJSModule) {
    1448         sinon = require("../sinon");
    1449     }
    1450 
    1451     if (!sinon) {
    1452         return;
    1453     }
    1454 
    1455     function spy(object, property) {
    1456         if (!property && typeof object == "function") {
    1457             return spy.create(object);
    1458         }
    1459 
    1460         if (!object && !property) {
    1461             return spy.create(function () { });
    1462         }
    1463 
    1464         var method = object[property];
    1465         return sinon.wrapMethod(object, property, spy.create(method));
    1466     }
    1467 
    1468     function matchingFake(fakes, args, strict) {
    1469         if (!fakes) {
    1470             return;
    1471         }
    1472 
    1473         for (var i = 0, l = fakes.length; i < l; i++) {
    1474             if (fakes[i].matches(args, strict)) {
    1475                 return fakes[i];
    1476             }
    1477         }
    1478     }
    1479 
    1480     function incrementCallCount() {
    1481         this.called = true;
    1482         this.callCount += 1;
    1483         this.notCalled = false;
    1484         this.calledOnce = this.callCount == 1;
    1485         this.calledTwice = this.callCount == 2;
    1486         this.calledThrice = this.callCount == 3;
    1487     }
    1488 
    1489     function createCallProperties() {
    1490         this.firstCall = this.getCall(0);
    1491         this.secondCall = this.getCall(1);
    1492         this.thirdCall = this.getCall(2);
    1493         this.lastCall = this.getCall(this.callCount - 1);
    1494     }
    1495 
    1496     var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
    1497     function createProxy(func) {
    1498         // Retain the function length:
    1499         var p;
    1500         if (func.length) {
    1501             eval("p = (function proxy(" + vars.substring(0, func.length * 2 - 1) +
    1502                 ") { return p.invoke(func, this, slice.call(arguments)); });");
    1503         }
    1504         else {
    1505             p = function proxy() {
    1506                 return p.invoke(func, this, slice.call(arguments));
    1507             };
    1508         }
    1509         return p;
    1510     }
    1511 
    1512     var uuid = 0;
    1513 
    1514     // Public API
    1515     var spyApi = {
    1516         reset: function () {
    1517             this.called = false;
    1518             this.notCalled = true;
    1519             this.calledOnce = false;
    1520             this.calledTwice = false;
    1521             this.calledThrice = false;
    1522             this.callCount = 0;
    1523             this.firstCall = null;
    1524             this.secondCall = null;
    1525             this.thirdCall = null;
    1526             this.lastCall = null;
    1527             this.args = [];
    1528             this.returnValues = [];
    1529             this.thisValues = [];
    1530             this.exceptions = [];
    1531             this.callIds = [];
    1532             if (this.fakes) {
    1533                 for (var i = 0; i < this.fakes.length; i++) {
    1534                     this.fakes[i].reset();
    1535                 }
    1536             }
    1537         },
    1538 
    1539         create: function create(func) {
    1540             var name;
    1541 
    1542             if (typeof func != "function") {
    1543                 func = function () { };
    1544             } else {
    1545                 name = sinon.functionName(func);
    1546             }
    1547 
    1548             var proxy = createProxy(func);
    1549 
    1550             sinon.extend(proxy, spy);
    1551             delete proxy.create;
    1552             sinon.extend(proxy, func);
    1553 
    1554             proxy.reset();
    1555             proxy.prototype = func.prototype;
    1556             proxy.displayName = name || "spy";
    1557             proxy.toString = sinon.functionToString;
    1558             proxy._create = sinon.spy.create;
    1559             proxy.id = "spy#" + uuid++;
    1560 
    1561             return proxy;
    1562         },
    1563 
    1564         invoke: function invoke(func, thisValue, args) {
    1565             var matching = matchingFake(this.fakes, args);
    1566             var exception, returnValue;
    1567 
    1568             incrementCallCount.call(this);
    1569             push.call(this.thisValues, thisValue);
    1570             push.call(this.args, args);
    1571             push.call(this.callIds, callId++);
    1572 
    1573             try {
    1574                 if (matching) {
    1575                     returnValue = matching.invoke(func, thisValue, args);
    1576                 } else {
    1577                     returnValue = (this.func || func).apply(thisValue, args);
    1578                 }
    1579 
    1580                 var thisCall = this.getCall(this.callCount - 1);
    1581                 if (thisCall.calledWithNew() && typeof returnValue !== 'object') {
    1582                     returnValue = thisValue;
    1583                 }
    1584             } catch (e) {
    1585                 exception = e;
    1586             }
    1587 
    1588             push.call(this.exceptions, exception);
    1589             push.call(this.returnValues, returnValue);
    1590 
    1591             createCallProperties.call(this);
    1592 
    1593             if (exception !== undefined) {
    1594                 throw exception;
    1595             }
    1596 
    1597             return returnValue;
    1598         },
    1599 
    1600         getCall: function getCall(i) {
    1601             if (i < 0 || i >= this.callCount) {
    1602                 return null;
    1603             }
    1604 
    1605             return sinon.spyCall(this, this.thisValues[i], this.args[i],
    1606                                     this.returnValues[i], this.exceptions[i],
    1607                                     this.callIds[i]);
    1608         },
    1609 
    1610         getCalls: function () {
    1611             var calls = [];
    1612             var i;
    1613 
    1614             for (i = 0; i < this.callCount; i++) {
    1615                 calls.push(this.getCall(i));
    1616             }
    1617 
    1618             return calls;
    1619         },
    1620 
    1621         calledBefore: function calledBefore(spyFn) {
    1622             if (!this.called) {
    1623                 return false;
    1624             }
    1625 
    1626             if (!spyFn.called) {
    1627                 return true;
    1628             }
    1629 
    1630             return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
    1631         },
    1632 
    1633         calledAfter: function calledAfter(spyFn) {
    1634             if (!this.called || !spyFn.called) {
    1635                 return false;
    1636             }
    1637 
    1638             return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
    1639         },
    1640 
    1641         withArgs: function () {
    1642             var args = slice.call(arguments);
    1643 
    1644             if (this.fakes) {
    1645                 var match = matchingFake(this.fakes, args, true);
    1646 
    1647                 if (match) {
    1648                     return match;
    1649                 }
    1650             } else {
    1651                 this.fakes = [];
    1652             }
    1653 
    1654             var original = this;
    1655             var fake = this._create();
    1656             fake.matchingAguments = args;
    1657             fake.parent = this;
    1658             push.call(this.fakes, fake);
    1659 
    1660             fake.withArgs = function () {
    1661                 return original.withArgs.apply(original, arguments);
    1662             };
    1663 
    1664             for (var i = 0; i < this.args.length; i++) {
    1665                 if (fake.matches(this.args[i])) {
    1666                     incrementCallCount.call(fake);
    1667                     push.call(fake.thisValues, this.thisValues[i]);
    1668                     push.call(fake.args, this.args[i]);
    1669                     push.call(fake.returnValues, this.returnValues[i]);
    1670                     push.call(fake.exceptions, this.exceptions[i]);
    1671                     push.call(fake.callIds, this.callIds[i]);
    1672                 }
    1673             }
    1674             createCallProperties.call(fake);
    1675 
    1676             return fake;
    1677         },
    1678 
    1679         matches: function (args, strict) {
    1680             var margs = this.matchingAguments;
    1681 
    1682             if (margs.length <= args.length &&
    1683                 sinon.deepEqual(margs, args.slice(0, margs.length))) {
    1684                 return !strict || margs.length == args.length;
    1685             }
    1686         },
    1687 
    1688         printf: function (format) {
    1689             var spy = this;
    1690             var args = slice.call(arguments, 1);
    1691             var formatter;
    1692 
    1693             return (format || "").replace(/%(.)/g, function (match, specifyer) {
    1694                 formatter = spyApi.formatters[specifyer];
    1695 
    1696                 if (typeof formatter == "function") {
    1697                     return formatter.call(null, spy, args);
    1698                 } else if (!isNaN(parseInt(specifyer, 10))) {
    1699                     return sinon.format(args[specifyer - 1]);
    1700                 }
    1701 
    1702                 return "%" + specifyer;
    1703             });
    1704         }
    1705     };
    1706 
    1707     function delegateToCalls(method, matchAny, actual, notCalled) {
    1708         spyApi[method] = function () {
    1709             if (!this.called) {
    1710                 if (notCalled) {
    1711                     return notCalled.apply(this, arguments);
    1712                 }
    1713                 return false;
    1714             }
    1715 
    1716             var currentCall;
    1717             var matches = 0;
    1718 
    1719             for (var i = 0, l = this.callCount; i < l; i += 1) {
    1720                 currentCall = this.getCall(i);
    1721 
    1722                 if (currentCall[actual || method].apply(currentCall, arguments)) {
    1723                     matches += 1;
    1724 
    1725                     if (matchAny) {
    1726                         return true;
    1727                     }
    1728                 }
    1729             }
    1730 
    1731             return matches === this.callCount;
    1732         };
    1733     }
    1734 
    1735     delegateToCalls("calledOn", true);
    1736     delegateToCalls("alwaysCalledOn", false, "calledOn");
    1737     delegateToCalls("calledWith", true);
    1738     delegateToCalls("calledWithMatch", true);
    1739     delegateToCalls("alwaysCalledWith", false, "calledWith");
    1740     delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
    1741     delegateToCalls("calledWithExactly", true);
    1742     delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
    1743     delegateToCalls("neverCalledWith", false, "notCalledWith",
    1744         function () { return true; });
    1745     delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch",
    1746         function () { return true; });
    1747     delegateToCalls("threw", true);
    1748     delegateToCalls("alwaysThrew", false, "threw");
    1749     delegateToCalls("returned", true);
    1750     delegateToCalls("alwaysReturned", false, "returned");
    1751     delegateToCalls("calledWithNew", true);
    1752     delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
    1753     delegateToCalls("callArg", false, "callArgWith", function () {
    1754         throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
    1755     });
    1756     spyApi.callArgWith = spyApi.callArg;
    1757     delegateToCalls("callArgOn", false, "callArgOnWith", function () {
    1758         throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
    1759     });
    1760     spyApi.callArgOnWith = spyApi.callArgOn;
    1761     delegateToCalls("yield", false, "yield", function () {
    1762         throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
    1763     });
    1764     // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
    1765     spyApi.invokeCallback = spyApi.yield;
    1766     delegateToCalls("yieldOn", false, "yieldOn", function () {
    1767         throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
    1768     });
    1769     delegateToCalls("yieldTo", false, "yieldTo", function (property) {
    1770         throw new Error(this.toString() + " cannot yield to '" + property +
    1771             "' since it was not yet invoked.");
    1772     });
    1773     delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
    1774         throw new Error(this.toString() + " cannot yield to '" + property +
    1775             "' since it was not yet invoked.");
    1776     });
    1777 
    1778     spyApi.formatters = {
    1779         "c": function (spy) {
    1780             return sinon.timesInWords(spy.callCount);
    1781         },
    1782 
    1783         "n": function (spy) {
    1784             return spy.toString();
    1785         },
    1786 
    1787         "C": function (spy) {
    1788             var calls = [];
    1789 
    1790             for (var i = 0, l = spy.callCount; i < l; ++i) {
    1791                 var stringifiedCall = "    " + spy.getCall(i).toString();
    1792                 if (/\n/.test(calls[i - 1])) {
    1793                     stringifiedCall = "\n" + stringifiedCall;
    1794                 }
    1795                 push.call(calls, stringifiedCall);
    1796             }
    1797 
    1798             return calls.length > 0 ? "\n" + calls.join("\n") : "";
    1799         },
    1800 
    1801         "t": function (spy) {
    1802             var objects = [];
    1803 
    1804             for (var i = 0, l = spy.callCount; i < l; ++i) {
    1805                 push.call(objects, sinon.format(spy.thisValues[i]));
    1806             }
    1807 
    1808             return objects.join(", ");
    1809         },
    1810 
    1811         "*": function (spy, args) {
    1812             var formatted = [];
    1813 
    1814             for (var i = 0, l = args.length; i < l; ++i) {
    1815                 push.call(formatted, sinon.format(args[i]));
    1816             }
    1817 
    1818             return formatted.join(", ");
    1819         }
    1820     };
    1821 
    1822     sinon.extend(spy, spyApi);
    1823 
    1824     spy.spyCall = sinon.spyCall;
    1825 
    1826     if (commonJSModule) {
    1827         module.exports = spy;
    1828     } else {
    1829         sinon.spy = spy;
    1830     }
    1831 }(typeof sinon == "object" && sinon || null));
    1832 
    1833 /**
    1834  * @depend ../sinon.js
    1835  */
    1836 /*jslint eqeqeq: false, onevar: false*/
    1837 /*global module, require, sinon, process, setImmediate, setTimeout*/
    1838 /**
    1839  * Stub behavior
    1840  *
    1841  * @author Christian Johansen (christian@cjohansen.no)
    1842  * @author Tim Fischbach (mail@timfischbach.de)
    1843  * @license BSD
    1844  *
    1845  * Copyright (c) 2010-2013 Christian Johansen
    1846  */
    1847 
    1848 (function (sinon) {
    1849     var commonJSModule = typeof module !== 'undefined' && module.exports;
    1850 
    1851     if (!sinon && commonJSModule) {
    1852         sinon = require("../sinon");
    1853     }
    1854 
    1855     if (!sinon) {
    1856         return;
    1857     }
    1858 
    1859     var slice = Array.prototype.slice;
    1860     var join = Array.prototype.join;
    1861     var proto;
    1862 
    1863     var nextTick = (function () {
    1864         if (typeof process === "object" && typeof process.nextTick === "function") {
    1865             return process.nextTick;
    1866         } else if (typeof setImmediate === "function") {
    1867             return setImmediate;
    1868         } else {
    1869             return function (callback) {
    1870                 setTimeout(callback, 0);
    1871             };
    1872         }
    1873     })();
    1874 
    1875     function throwsException(error, message) {
    1876         if (typeof error == "string") {
    1877             this.exception = new Error(message || "");
    1878             this.exception.name = error;
    1879         } else if (!error) {
    1880             this.exception = new Error("Error");
    1881         } else {
    1882             this.exception = error;
    1883         }
    1884 
    1885         return this;
    1886     }
    1887 
    1888     function getCallback(behavior, args) {
    1889         var callArgAt = behavior.callArgAt;
    1890 
    1891         if (callArgAt < 0) {
    1892             var callArgProp = behavior.callArgProp;
    1893 
    1894             for (var i = 0, l = args.length; i < l; ++i) {
    1895                 if (!callArgProp && typeof args[i] == "function") {
    1896                     return args[i];
    1897                 }
    1898 
    1899                 if (callArgProp && args[i] &&
    1900                     typeof args[i][callArgProp] == "function") {
    1901                     return args[i][callArgProp];
    1902                 }
    1903             }
    1904 
    1905             return null;
    1906         }
    1907 
    1908         return args[callArgAt];
    1909     }
    1910 
    1911     function getCallbackError(behavior, func, args) {
    1912         if (behavior.callArgAt < 0) {
    1913             var msg;
    1914 
    1915             if (behavior.callArgProp) {
    1916                 msg = sinon.functionName(behavior.stub) +
    1917                     " expected to yield to '" + behavior.callArgProp +
    1918                     "', but no object with such a property was passed.";
    1919             } else {
    1920                 msg = sinon.functionName(behavior.stub) +
    1921                     " expected to yield, but no callback was passed.";
    1922             }
    1923 
    1924             if (args.length > 0) {
    1925                 msg += " Received [" + join.call(args, ", ") + "]";
    1926             }
    1927 
    1928             return msg;
    1929         }
    1930 
    1931         return "argument at index " + behavior.callArgAt + " is not a function: " + func;
    1932     }
    1933 
    1934     function callCallback(behavior, args) {
    1935         if (typeof behavior.callArgAt == "number") {
    1936             var func = getCallback(behavior, args);
    1937 
    1938             if (typeof func != "function") {
    1939                 throw new TypeError(getCallbackError(behavior, func, args));
    1940             }
    1941 
    1942             if (behavior.callbackAsync) {
    1943                 nextTick(function() {
    1944                     func.apply(behavior.callbackContext, behavior.callbackArguments);
    1945                 });
    1946             } else {
    1947                 func.apply(behavior.callbackContext, behavior.callbackArguments);
    1948             }
    1949         }
    1950     }
    1951 
    1952     proto = {
    1953         create: function(stub) {
    1954             var behavior = sinon.extend({}, sinon.behavior);
    1955             delete behavior.create;
    1956             behavior.stub = stub;
    1957 
    1958             return behavior;
    1959         },
    1960 
    1961         isPresent: function() {
    1962             return (typeof this.callArgAt == 'number' ||
    1963                     this.exception ||
    1964                     typeof this.returnArgAt == 'number' ||
    1965                     this.returnThis ||
    1966                     this.returnValueDefined);
    1967         },
    1968 
    1969         invoke: function(context, args) {
    1970             callCallback(this, args);
    1971 
    1972             if (this.exception) {
    1973                 throw this.exception;
    1974             } else if (typeof this.returnArgAt == 'number') {
    1975                 return args[this.returnArgAt];
    1976             } else if (this.returnThis) {
    1977                 return context;
    1978             }
    1979 
    1980             return this.returnValue;
    1981         },
    1982 
    1983         onCall: function(index) {
    1984             return this.stub.onCall(index);
    1985         },
    1986 
    1987         onFirstCall: function() {
    1988             return this.stub.onFirstCall();
    1989         },
    1990 
    1991         onSecondCall: function() {
    1992             return this.stub.onSecondCall();
    1993         },
    1994 
    1995         onThirdCall: function() {
    1996             return this.stub.onThirdCall();
    1997         },
    1998 
    1999         withArgs: function(/* arguments */) {
    2000             throw new Error('Defining a stub by invoking "stub.onCall(...).withArgs(...)" is not supported. ' +
    2001                             'Use "stub.withArgs(...).onCall(...)" to define sequential behavior for calls with certain arguments.');
    2002         },
    2003 
    2004         callsArg: function callsArg(pos) {
    2005             if (typeof pos != "number") {
    2006                 throw new TypeError("argument index is not number");
    2007             }
    2008 
    2009             this.callArgAt = pos;
    2010             this.callbackArguments = [];
    2011             this.callbackContext = undefined;
    2012             this.callArgProp = undefined;
    2013             this.callbackAsync = false;
    2014 
    2015             return this;
    2016         },
    2017 
    2018         callsArgOn: function callsArgOn(pos, context) {
    2019             if (typeof pos != "number") {
    2020                 throw new TypeError("argument index is not number");
    2021             }
    2022             if (typeof context != "object") {
    2023                 throw new TypeError("argument context is not an object");
    2024             }
    2025 
    2026             this.callArgAt = pos;
    2027             this.callbackArguments = [];
    2028             this.callbackContext = context;
    2029             this.callArgProp = undefined;
    2030             this.callbackAsync = false;
    2031 
    2032             return this;
    2033         },
    2034 
    2035         callsArgWith: function callsArgWith(pos) {
    2036             if (typeof pos != "number") {
    2037                 throw new TypeError("argument index is not number");
    2038             }
    2039 
    2040             this.callArgAt = pos;
    2041             this.callbackArguments = slice.call(arguments, 1);
    2042             this.callbackContext = undefined;
    2043             this.callArgProp = undefined;
    2044             this.callbackAsync = false;
    2045 
    2046             return this;
    2047         },
    2048 
    2049         callsArgOnWith: function callsArgWith(pos, context) {
    2050             if (typeof pos != "number") {
    2051                 throw new TypeError("argument index is not number");
    2052             }
    2053             if (typeof context != "object") {
    2054                 throw new TypeError("argument context is not an object");
    2055             }
    2056 
    2057             this.callArgAt = pos;
    2058             this.callbackArguments = slice.call(arguments, 2);
    2059             this.callbackContext = context;
    2060             this.callArgProp = undefined;
    2061             this.callbackAsync = false;
    2062 
    2063             return this;
    2064         },
    2065 
    2066         yields: function () {
    2067             this.callArgAt = -1;
    2068             this.callbackArguments = slice.call(arguments, 0);
    2069             this.callbackContext = undefined;
    2070             this.callArgProp = undefined;
    2071             this.callbackAsync = false;
    2072 
    2073             return this;
    2074         },
    2075 
    2076         yieldsOn: function (context) {
    2077             if (typeof context != "object") {
    2078                 throw new TypeError("argument context is not an object");
    2079             }
    2080 
    2081             this.callArgAt = -1;
    2082             this.callbackArguments = slice.call(arguments, 1);
    2083             this.callbackContext = context;
    2084             this.callArgProp = undefined;
    2085             this.callbackAsync = false;
    2086 
    2087             return this;
    2088         },
    2089 
    2090         yieldsTo: function (prop) {
    2091             this.callArgAt = -1;
    2092             this.callbackArguments = slice.call(arguments, 1);
    2093             this.callbackContext = undefined;
    2094             this.callArgProp = prop;
    2095             this.callbackAsync = false;
    2096 
    2097             return this;
    2098         },
    2099 
    2100         yieldsToOn: function (prop, context) {
    2101             if (typeof context != "object") {
    2102                 throw new TypeError("argument context is not an object");
    2103             }
    2104 
    2105             this.callArgAt = -1;
    2106             this.callbackArguments = slice.call(arguments, 2);
    2107             this.callbackContext = context;
    2108             this.callArgProp = prop;
    2109             this.callbackAsync = false;
    2110 
    2111             return this;
    2112         },
    2113 
    2114 
    2115         "throws": throwsException,
    2116         throwsException: throwsException,
    2117 
    2118         returns: function returns(value) {
    2119             this.returnValue = value;
    2120             this.returnValueDefined = true;
    2121 
    2122             return this;
    2123         },
    2124 
    2125         returnsArg: function returnsArg(pos) {
    2126             if (typeof pos != "number") {
    2127                 throw new TypeError("argument index is not number");
    2128             }
    2129 
    2130             this.returnArgAt = pos;
    2131 
    2132             return this;
    2133         },
    2134 
    2135         returnsThis: function returnsThis() {
    2136             this.returnThis = true;
    2137 
    2138             return this;
    2139         }
    2140     };
    2141 
    2142     // create asynchronous versions of callsArg* and yields* methods
    2143     for (var method in proto) {
    2144         // need to avoid creating anotherasync versions of the newly added async methods
    2145         if (proto.hasOwnProperty(method) &&
    2146             method.match(/^(callsArg|yields)/) &&
    2147             !method.match(/Async/)) {
    2148             proto[method + 'Async'] = (function (syncFnName) {
    2149                 return function () {
    2150                     var result = this[syncFnName].apply(this, arguments);
    2151                     this.callbackAsync = true;
    2152                     return result;
    2153                 };
    2154             })(method);
    2155         }
    2156     }
    2157 
    2158     if (commonJSModule) {
    2159         module.exports = proto;
    2160     } else {
    2161         sinon.behavior = proto;
    2162     }
    2163 }(typeof sinon == "object" && sinon || null));
    2164 /**
    2165  * @depend ../sinon.js
    2166  * @depend spy.js
    2167  * @depend behavior.js
    2168  */
    2169 /*jslint eqeqeq: false, onevar: false*/
    2170 /*global module, require, sinon*/
    2171 /**
    2172  * Stub functions
    2173  *
    2174  * @author Christian Johansen (christian@cjohansen.no)
    2175  * @license BSD
    2176  *
    2177  * Copyright (c) 2010-2013 Christian Johansen
    2178  */
    2179 
    2180 (function (sinon) {
    2181     var commonJSModule = typeof module !== 'undefined' && module.exports;
    2182 
    2183     if (!sinon && commonJSModule) {
    2184         sinon = require("../sinon");
    2185     }
    2186 
    2187     if (!sinon) {
    2188         return;
    2189     }
    2190 
    2191     function stub(object, property, func) {
    2192         if (!!func && typeof func != "function") {
    2193             throw new TypeError("Custom stub should be function");
    2194         }
    2195 
    2196         var wrapper;
    2197 
    2198         if (func) {
    2199             wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func;
    2200         } else {
    2201             wrapper = stub.create();
    2202         }
    2203 
    2204         if (!object && typeof property === "undefined") {
    2205             return sinon.stub.create();
    2206         }
    2207 
    2208         if (typeof property === "undefined" && typeof object == "object") {
    2209             for (var prop in object) {
    2210                 if (typeof object[prop] === "function") {
    2211                     stub(object, prop);
    2212                 }
    2213             }
    2214 
    2215             return object;
    2216         }
    2217 
    2218         return sinon.wrapMethod(object, property, wrapper);
    2219     }
    2220 
    2221     function getDefaultBehavior(stub) {
    2222         return stub.defaultBehavior || getParentBehaviour(stub) || sinon.behavior.create(stub);
    2223     }
    2224 
    2225     function getParentBehaviour(stub) {
    2226         return (stub.parent && getCurrentBehavior(stub.parent));
    2227     }
    2228 
    2229     function getCurrentBehavior(stub) {
    2230         var behavior = stub.behaviors[stub.callCount - 1];
    2231         return behavior && behavior.isPresent() ? behavior : getDefaultBehavior(stub);
    2232     }
    2233 
    2234     var uuid = 0;
    2235 
    2236     sinon.extend(stub, (function () {
    2237         var proto = {
    2238             create: function create() {
    2239                 var functionStub = function () {
    2240                     return getCurrentBehavior(functionStub).invoke(this, arguments);
    2241                 };
    2242 
    2243                 functionStub.id = "stub#" + uuid++;
    2244                 var orig = functionStub;
    2245                 functionStub = sinon.spy.create(functionStub);
    2246                 functionStub.func = orig;
    2247 
    2248                 sinon.extend(functionStub, stub);
    2249                 functionStub._create = sinon.stub.create;
    2250                 functionStub.displayName = "stub";
    2251                 functionStub.toString = sinon.functionToString;
    2252 
    2253                 functionStub.defaultBehavior = null;
    2254                 functionStub.behaviors = [];
    2255 
    2256                 return functionStub;
    2257             },
    2258 
    2259             resetBehavior: function () {
    2260                 var i;
    2261 
    2262                 this.defaultBehavior = null;
    2263                 this.behaviors = [];
    2264 
    2265                 delete this.returnValue;
    2266                 delete this.returnArgAt;
    2267                 this.returnThis = false;
    2268 
    2269                 if (this.fakes) {
    2270                     for (i = 0; i < this.fakes.length; i++) {
    2271                         this.fakes[i].resetBehavior();
    2272                     }
    2273                 }
    2274             },
    2275 
    2276             onCall: function(index) {
    2277                 if (!this.behaviors[index]) {
    2278                     this.behaviors[index] = sinon.behavior.create(this);
    2279                 }
    2280 
    2281                 return this.behaviors[index];
    2282             },
    2283 
    2284             onFirstCall: function() {
    2285                 return this.onCall(0);
    2286             },
    2287 
    2288             onSecondCall: function() {
    2289                 return this.onCall(1);
    2290             },
    2291 
    2292             onThirdCall: function() {
    2293                 return this.onCall(2);
    2294             }
    2295         };
    2296 
    2297         for (var method in sinon.behavior) {
    2298             if (sinon.behavior.hasOwnProperty(method) &&
    2299                 !proto.hasOwnProperty(method) &&
    2300                 method != 'create' &&
    2301                 method != 'withArgs' &&
    2302                 method != 'invoke') {
    2303                 proto[method] = (function(behaviorMethod) {
    2304                     return function() {
    2305                         this.defaultBehavior = this.defaultBehavior || sinon.behavior.create(this);
    2306                         this.defaultBehavior[behaviorMethod].apply(this.defaultBehavior, arguments);
    2307                         return this;
    2308                     };
    2309                 }(method));
    2310             }
    2311         }
    2312 
    2313         return proto;
    2314     }()));
    2315 
    2316     if (commonJSModule) {
    2317         module.exports = stub;
    2318     } else {
    2319         sinon.stub = stub;
    2320     }
    2321 }(typeof sinon == "object" && sinon || null));
    2322 
    2323 /**
    2324  * @depend ../sinon.js
    2325  * @depend stub.js
    2326  */
    2327 /*jslint eqeqeq: false, onevar: false, nomen: false*/
    2328 /*global module, require, sinon*/
    2329 /**
    2330  * Mock functions.
    2331  *
    2332  * @author Christian Johansen (christian@cjohansen.no)
    2333  * @license BSD
    2334  *
    2335  * Copyright (c) 2010-2013 Christian Johansen
    2336  */
    2337 
    2338 (function (sinon) {
    2339     var commonJSModule = typeof module !== 'undefined' && module.exports;
    2340     var push = [].push;
    2341     var match;
    2342 
    2343     if (!sinon && commonJSModule) {
    2344         sinon = require("../sinon");
    2345     }
    2346 
    2347     if (!sinon) {
    2348         return;
    2349     }
    2350 
    2351     match = sinon.match;
    2352 
    2353     if (!match && commonJSModule) {
    2354         match = require("./match");
    2355     }
    2356 
    2357     function mock(object) {
    2358         if (!object) {
    2359             return sinon.expectation.create("Anonymous mock");
    2360         }
    2361 
    2362         return mock.create(object);
    2363     }
    2364 
    2365     sinon.mock = mock;
    2366 
    2367     sinon.extend(mock, (function () {
    2368         function each(collection, callback) {
    2369             if (!collection) {
    2370                 return;
    2371             }
    2372 
    2373             for (var i = 0, l = collection.length; i < l; i += 1) {
    2374                 callback(collection[i]);
    2375             }
    2376         }
    2377 
    2378         return {
    2379             create: function create(object) {
    2380                 if (!object) {
    2381                     throw new TypeError("object is null");
    2382                 }
    2383 
    2384                 var mockObject = sinon.extend({}, mock);
    2385                 mockObject.object = object;
    2386                 delete mockObject.create;
    2387 
    2388                 return mockObject;
    2389             },
    2390 
    2391             expects: function expects(method) {
    2392                 if (!method) {
    2393                     throw new TypeError("method is falsy");
    2394                 }
    2395 
    2396                 if (!this.expectations) {
    2397                     this.expectations = {};
    2398                     this.proxies = [];
    2399                 }
    2400 
    2401                 if (!this.expectations[method]) {
    2402                     this.expectations[method] = [];
    2403                     var mockObject = this;
    2404 
    2405                     sinon.wrapMethod(this.object, method, function () {
    2406                         return mockObject.invokeMethod(method, this, arguments);
    2407                     });
    2408 
    2409                     push.call(this.proxies, method);
    2410                 }
    2411 
    2412                 var expectation = sinon.expectation.create(method);
    2413                 push.call(this.expectations[method], expectation);
    2414 
    2415                 return expectation;
    2416             },
    2417 
    2418             restore: function restore() {
    2419                 var object = this.object;
    2420 
    2421                 each(this.proxies, function (proxy) {
    2422                     if (typeof object[proxy].restore == "function") {
    2423                         object[proxy].restore();
    2424                     }
    2425                 });
    2426             },
    2427 
    2428             verify: function verify() {
    2429                 var expectations = this.expectations || {};
    2430                 var messages = [], met = [];
    2431 
    2432                 each(this.proxies, function (proxy) {
    2433                     each(expectations[proxy], function (expectation) {
    2434                         if (!expectation.met()) {
    2435                             push.call(messages, expectation.toString());
    2436                         } else {
    2437                             push.call(met, expectation.toString());
    2438                         }
    2439                     });
    2440                 });
    2441 
    2442                 this.restore();
    2443 
    2444                 if (messages.length > 0) {
    2445                     sinon.expectation.fail(messages.concat(met).join("\n"));
    2446                 } else {
    2447                     sinon.expectation.pass(messages.concat(met).join("\n"));
    2448                 }
    2449 
    2450                 return true;
    2451             },
    2452 
    2453             invokeMethod: function invokeMethod(method, thisValue, args) {
    2454                 var expectations = this.expectations && this.expectations[method];
    2455                 var length = expectations && expectations.length || 0, i;
    2456 
    2457                 for (i = 0; i < length; i += 1) {
    2458                     if (!expectations[i].met() &&
    2459                         expectations[i].allowsCall(thisValue, args)) {
    2460                         return expectations[i].apply(thisValue, args);
    2461                     }
    2462                 }
    2463 
    2464                 var messages = [], available, exhausted = 0;
    2465 
    2466                 for (i = 0; i < length; i += 1) {
    2467                     if (expectations[i].allowsCall(thisValue, args)) {
    2468                         available = available || expectations[i];
    2469                     } else {
    2470                         exhausted += 1;
    2471                     }
    2472                     push.call(messages, "    " + expectations[i].toString());
    2473                 }
    2474 
    2475                 if (exhausted === 0) {
    2476                     return available.apply(thisValue, args);
    2477                 }
    2478 
    2479                 messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({
    2480                     proxy: method,
    2481                     args: args
    2482                 }));
    2483 
    2484                 sinon.expectation.fail(messages.join("\n"));
    2485             }
    2486         };
    2487     }()));
    2488 
    2489     var times = sinon.timesInWords;
    2490 
    2491     sinon.expectation = (function () {
    2492         var slice = Array.prototype.slice;
    2493         var _invoke = sinon.spy.invoke;
    2494 
    2495         function callCountInWords(callCount) {
    2496             if (callCount == 0) {
    2497                 return "never called";
    2498             } else {
    2499                 return "called " + times(callCount);
    2500             }
    2501         }
    2502 
    2503         function expectedCallCountInWords(expectation) {
    2504             var min = expectation.minCalls;
    2505             var max = expectation.maxCalls;
    2506 
    2507             if (typeof min == "number" && typeof max == "number") {
    2508                 var str = times(min);
    2509 
    2510                 if (min != max) {
    2511                     str = "at least " + str + " and at most " + times(max);
    2512                 }
    2513 
    2514                 return str;
    2515             }
    2516 
    2517             if (typeof min == "number") {
    2518                 return "at least " + times(min);
    2519             }
    2520 
    2521             return "at most " + times(max);
    2522         }
    2523 
    2524         function receivedMinCalls(expectation) {
    2525             var hasMinLimit = typeof expectation.minCalls == "number";
    2526             return !hasMinLimit || expectation.callCount >= expectation.minCalls;
    2527         }
    2528 
    2529         function receivedMaxCalls(expectation) {
    2530             if (typeof expectation.maxCalls != "number") {
    2531                 return false;
    2532             }
    2533 
    2534             return expectation.callCount == expectation.maxCalls;
    2535         }
    2536 
    2537         function verifyMatcher(possibleMatcher, arg){
    2538             if (match && match.isMatcher(possibleMatcher)) {
    2539                 return possibleMatcher.test(arg);
    2540             } else {
    2541                 return true;
    2542             }
    2543         }
    2544 
    2545         return {
    2546             minCalls: 1,
    2547             maxCalls: 1,
    2548 
    2549             create: function create(methodName) {
    2550                 var expectation = sinon.extend(sinon.stub.create(), sinon.expectation);
    2551                 delete expectation.create;
    2552                 expectation.method = methodName;
    2553 
    2554                 return expectation;
    2555             },
    2556 
    2557             invoke: function invoke(func, thisValue, args) {
    2558                 this.verifyCallAllowed(thisValue, args);
    2559 
    2560                 return _invoke.apply(this, arguments);
    2561             },
    2562 
    2563             atLeast: function atLeast(num) {
    2564                 if (typeof num != "number") {
    2565                     throw new TypeError("'" + num + "' is not number");
    2566                 }
    2567 
    2568                 if (!this.limitsSet) {
    2569                     this.maxCalls = null;
    2570                     this.limitsSet = true;
    2571                 }
    2572 
    2573                 this.minCalls = num;
    2574 
    2575                 return this;
    2576             },
    2577 
    2578             atMost: function atMost(num) {
    2579                 if (typeof num != "number") {
    2580                     throw new TypeError("'" + num + "' is not number");
    2581                 }
    2582 
    2583                 if (!this.limitsSet) {
    2584                     this.minCalls = null;
    2585                     this.limitsSet = true;
    2586                 }
    2587 
    2588                 this.maxCalls = num;
    2589 
    2590                 return this;
    2591             },
    2592 
    2593             never: function never() {
    2594                 return this.exactly(0);
    2595             },
    2596 
    2597             once: function once() {
    2598                 return this.exactly(1);
    2599             },
    2600 
    2601             twice: function twice() {
    2602                 return this.exactly(2);
    2603             },
    2604 
    2605             thrice: function thrice() {
    2606                 return this.exactly(3);
    2607             },
    2608 
    2609             exactly: function exactly(num) {
    2610                 if (typeof num != "number") {
    2611                     throw new TypeError("'" + num + "' is not a number");
    2612                 }
    2613 
    2614                 this.atLeast(num);
    2615                 return this.atMost(num);
    2616             },
    2617 
    2618             met: function met() {
    2619                 return !this.failed && receivedMinCalls(this);
    2620             },
    2621 
    2622             verifyCallAllowed: function verifyCallAllowed(thisValue, args) {
    2623                 if (receivedMaxCalls(this)) {
    2624                     this.failed = true;
    2625                     sinon.expectation.fail(this.method + " already called " + times(this.maxCalls));
    2626                 }
    2627 
    2628                 if ("expectedThis" in this && this.expectedThis !== thisValue) {
    2629                     sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " +
    2630                         this.expectedThis);
    2631                 }
    2632 
    2633                 if (!("expectedArguments" in this)) {
    2634                     return;
    2635                 }
    2636 
    2637                 if (!args) {
    2638                     sinon.expectation.fail(this.method + " received no arguments, expected " +
    2639                         sinon.format(this.expectedArguments));
    2640                 }
    2641 
    2642                 if (args.length < this.expectedArguments.length) {
    2643                     sinon.expectation.fail(this.method + " received too few arguments (" + sinon.format(args) +
    2644                         "), expected " + sinon.format(this.expectedArguments));
    2645                 }
    2646 
    2647                 if (this.expectsExactArgCount &&
    2648                     args.length != this.expectedArguments.length) {
    2649                     sinon.expectation.fail(this.method + " received too many arguments (" + sinon.format(args) +
    2650                         "), expected " + sinon.format(this.expectedArguments));
    2651                 }
    2652 
    2653                 for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
    2654 
    2655                     if (!verifyMatcher(this.expectedArguments[i],args[i])) {
    2656                         sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
    2657                             ", didn't match " + this.expectedArguments.toString());
    2658                     }
    2659 
    2660                     if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
    2661                         sinon.expectation.fail(this.method + " received wrong arguments " + sinon.format(args) +
    2662                             ", expected " + sinon.format(this.expectedArguments));
    2663                     }
    2664                 }
    2665             },
    2666 
    2667             allowsCall: function allowsCall(thisValue, args) {
    2668                 if (this.met() && receivedMaxCalls(this)) {
    2669                     return false;
    2670                 }
    2671 
    2672                 if ("expectedThis" in this && this.expectedThis !== thisValue) {
    2673                     return false;
    2674                 }
    2675 
    2676                 if (!("expectedArguments" in this)) {
    2677                     return true;
    2678                 }
    2679 
    2680                 args = args || [];
    2681 
    2682                 if (args.length < this.expectedArguments.length) {
    2683                     return false;
    2684                 }
    2685 
    2686                 if (this.expectsExactArgCount &&
    2687                     args.length != this.expectedArguments.length) {
    2688                     return false;
    2689                 }
    2690 
    2691                 for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) {
    2692                     if (!verifyMatcher(this.expectedArguments[i],args[i])) {
    2693                         return false;
    2694                     }
    2695 
    2696                     if (!sinon.deepEqual(this.expectedArguments[i], args[i])) {
    2697                         return false;
    2698                     }
    2699                 }
    2700 
    2701                 return true;
    2702             },
    2703 
    2704             withArgs: function withArgs() {
    2705                 this.expectedArguments = slice.call(arguments);
    2706                 return this;
    2707             },
    2708 
    2709             withExactArgs: function withExactArgs() {
    2710                 this.withArgs.apply(this, arguments);
    2711                 this.expectsExactArgCount = true;
    2712                 return this;
    2713             },
    2714 
    2715             on: function on(thisValue) {
    2716                 this.expectedThis = thisValue;
    2717                 return this;
    2718             },
    2719 
    2720             toString: function () {
    2721                 var args = (this.expectedArguments || []).slice();
    2722 
    2723                 if (!this.expectsExactArgCount) {
    2724                     push.call(args, "[...]");
    2725                 }
    2726 
    2727                 var callStr = sinon.spyCall.toString.call({
    2728                     proxy: this.method || "anonymous mock expectation",
    2729                     args: args
    2730                 });
    2731 
    2732                 var message = callStr.replace(", [...", "[, ...") + " " +
    2733                     expectedCallCountInWords(this);
    2734 
    2735                 if (this.met()) {
    2736                     return "Expectation met: " + message;
    2737                 }
    2738 
    2739                 return "Expected " + message + " (" +
    2740                     callCountInWords(this.callCount) + ")";
    2741             },
    2742 
    2743             verify: function verify() {
    2744                 if (!this.met()) {
    2745                     sinon.expectation.fail(this.toString());
    2746                 } else {
    2747                     sinon.expectation.pass(this.toString());
    2748                 }
    2749 
    2750                 return true;
    2751             },
    2752 
    2753             pass: function(message) {
    2754               sinon.assert.pass(message);
    2755             },
    2756             fail: function (message) {
    2757                 var exception = new Error(message);
    2758                 exception.name = "ExpectationError";
    2759 
    2760                 throw exception;
    2761             }
    2762         };
    2763     }());
    2764 
    2765     if (commonJSModule) {
    2766         module.exports = mock;
    2767     } else {
    2768         sinon.mock = mock;
    2769     }
    2770 }(typeof sinon == "object" && sinon || null));
    2771 
    2772 /**
    2773  * @depend ../sinon.js
    2774  * @depend stub.js
    2775  * @depend mock.js
    2776  */
    2777 /*jslint eqeqeq: false, onevar: false, forin: true*/
    2778 /*global module, require, sinon*/
    2779 /**
    2780  * Collections of stubs, spies and mocks.
    2781  *
    2782  * @author Christian Johansen (christian@cjohansen.no)
    2783  * @license BSD
    2784  *
    2785  * Copyright (c) 2010-2013 Christian Johansen
    2786  */
    2787 
    2788 (function (sinon) {
    2789     var commonJSModule = typeof module !== 'undefined' && module.exports;
    2790     var push = [].push;
    2791     var hasOwnProperty = Object.prototype.hasOwnProperty;
    2792 
    2793     if (!sinon && commonJSModule) {
    2794         sinon = require("../sinon");
    2795     }
    2796 
    2797     if (!sinon) {
    2798         return;
    2799     }
    2800 
    2801     function getFakes(fakeCollection) {
    2802         if (!fakeCollection.fakes) {
    2803             fakeCollection.fakes = [];
    2804         }
    2805 
    2806         return fakeCollection.fakes;
    2807     }
    2808 
    2809     function each(fakeCollection, method) {
    2810         var fakes = getFakes(fakeCollection);
    2811 
    2812         for (var i = 0, l = fakes.length; i < l; i += 1) {
    2813             if (typeof fakes[i][method] == "function") {
    2814                 fakes[i][method]();
    2815             }
    2816         }
    2817     }
    2818 
    2819     function compact(fakeCollection) {
    2820         var fakes = getFakes(fakeCollection);
    2821         var i = 0;
    2822         while (i < fakes.length) {
    2823           fakes.splice(i, 1);
    2824         }
    2825     }
    2826 
    2827     var collection = {
    2828         verify: function resolve() {
    2829             each(this, "verify");
    2830         },
    2831 
    2832         restore: function restore() {
    2833             each(this, "restore");
    2834             compact(this);
    2835         },
    2836 
    2837         verifyAndRestore: function verifyAndRestore() {
    2838             var exception;
    2839 
    2840             try {
    2841                 this.verify();
    2842             } catch (e) {
    2843                 exception = e;
    2844             }
    2845 
    2846             this.restore();
    2847 
    2848             if (exception) {
    2849                 throw exception;
    2850             }
    2851         },
    2852 
    2853         add: function add(fake) {
    2854             push.call(getFakes(this), fake);
    2855             return fake;
    2856         },
    2857 
    2858         spy: function spy() {
    2859             return this.add(sinon.spy.apply(sinon, arguments));
    2860         },
    2861 
    2862         stub: function stub(object, property, value) {
    2863             if (property) {
    2864                 var original = object[property];
    2865 
    2866                 if (typeof original != "function") {
    2867                     if (!hasOwnProperty.call(object, property)) {
    2868                         throw new TypeError("Cannot stub non-existent own property " + property);
    2869                     }
    2870 
    2871                     object[property] = value;
    2872 
    2873                     return this.add({
    2874                         restore: function () {
    2875                             object[property] = original;
    2876                         }
    2877                     });
    2878                 }
    2879             }
    2880             if (!property && !!object && typeof object == "object") {
    2881                 var stubbedObj = sinon.stub.apply(sinon, arguments);
    2882 
    2883                 for (var prop in stubbedObj) {
    2884                     if (typeof stubbedObj[prop] === "function") {
    2885                         this.add(stubbedObj[prop]);
    2886                     }
    2887                 }
    2888 
    2889                 return stubbedObj;
    2890             }
    2891 
    2892             return this.add(sinon.stub.apply(sinon, arguments));
    2893         },
    2894 
    2895         mock: function mock() {
    2896             return this.add(sinon.mock.apply(sinon, arguments));
    2897         },
    2898 
    2899         inject: function inject(obj) {
    2900             var col = this;
    2901 
    2902             obj.spy = function () {
    2903                 return col.spy.apply(col, arguments);
    2904             };
    2905 
    2906             obj.stub = function () {
    2907                 return col.stub.apply(col, arguments);
    2908             };
    2909 
    2910             obj.mock = function () {
    2911                 return col.mock.apply(col, arguments);
    2912             };
    2913 
    2914             return obj;
    2915         }
    2916     };
    2917 
    2918     if (commonJSModule) {
    2919         module.exports = collection;
    2920     } else {
    2921         sinon.collection = collection;
    2922     }
    2923 }(typeof sinon == "object" && sinon || null));
    2924 
    2925 /*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
    2926 /*global module, require, window*/
    2927 /**
    2928  * Fake timer API
    2929  * setTimeout
    2930  * setInterval
    2931  * clearTimeout
    2932  * clearInterval
    2933  * tick
    2934  * reset
    2935  * Date
    2936  *
    2937  * Inspired by jsUnitMockTimeOut from JsUnit
    2938  *
    2939  * @author Christian Johansen (christian@cjohansen.no)
    2940  * @license BSD
    2941  *
    2942  * Copyright (c) 2010-2013 Christian Johansen
    2943  */
    2944 
    2945 if (typeof sinon == "undefined") {
    2946     var sinon = {};
    2947 }
    2948 
    2949 (function (global) {
    2950     var id = 1;
    2951 
    2952     function addTimer(args, recurring) {
    2953         if (args.length === 0) {
    2954             throw new Error("Function requires at least 1 parameter");
    2955         }
    2956 
    2957         if (typeof args[0] === "undefined") {
    2958             throw new Error("Callback must be provided to timer calls");
    2959         }
    2960 
    2961         var toId = id++;
    2962         var delay = args[1] || 0;
    2963 
    2964         if (!this.timeouts) {
    2965             this.timeouts = {};
    2966         }
    2967 
    2968         this.timeouts[toId] = {
    2969             id: toId,
    2970             func: args[0],
    2971             callAt: this.now + delay,
    2972             invokeArgs: Array.prototype.slice.call(args, 2)
    2973         };
    2974 
    2975         if (recurring === true) {
    2976             this.timeouts[toId].interval = delay;
    2977         }
    2978 
    2979         return toId;
    2980     }
    2981 
    2982     function parseTime(str) {
    2983         if (!str) {
    2984             return 0;
    2985         }
    2986 
    2987         var strings = str.split(":");
    2988         var l = strings.length, i = l;
    2989         var ms = 0, parsed;
    2990 
    2991         if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
    2992             throw new Error("tick only understands numbers and 'h:m:s'");
    2993         }
    2994 
    2995         while (i--) {
    2996             parsed = parseInt(strings[i], 10);
    2997 
    2998             if (parsed >= 60) {
    2999                 throw new Error("Invalid time " + str);
    3000             }
    3001 
    3002             ms += parsed * Math.pow(60, (l - i - 1));
    3003         }
    3004 
    3005         return ms * 1000;
    3006     }
    3007 
    3008     function createObject(object) {
    3009         var newObject;
    3010 
    3011         if (Object.create) {
    3012             newObject = Object.create(object);
    3013         } else {
    3014             var F = function () {};
    3015             F.prototype = object;
    3016             newObject = new F();
    3017         }
    3018 
    3019         newObject.Date.clock = newObject;
    3020         return newObject;
    3021     }
    3022 
    3023     sinon.clock = {
    3024         now: 0,
    3025 
    3026         create: function create(now) {
    3027             var clock = createObject(this);
    3028 
    3029             if (typeof now == "number") {
    3030                 clock.now = now;
    3031             }
    3032 
    3033             if (!!now && typeof now == "object") {
    3034                 throw new TypeError("now should be milliseconds since UNIX epoch");
    3035             }
    3036 
    3037             return clock;
    3038         },
    3039 
    3040         setTimeout: function setTimeout(callback, timeout) {
    3041             return addTimer.call(this, arguments, false);
    3042         },
    3043 
    3044         clearTimeout: function clearTimeout(timerId) {
    3045             if (!this.timeouts) {
    3046                 this.timeouts = [];
    3047             }
    3048 
    3049             if (timerId in this.timeouts) {
    3050                 delete this.timeouts[timerId];
    3051             }
    3052         },
    3053 
    3054         setInterval: function setInterval(callback, timeout) {
    3055             return addTimer.call(this, arguments, true);
    3056         },
    3057 
    3058         clearInterval: function clearInterval(timerId) {
    3059             this.clearTimeout(timerId);
    3060         },
    3061 
    3062         setImmediate: function setImmediate(callback) {
    3063             var passThruArgs = Array.prototype.slice.call(arguments, 1);
    3064 
    3065             return addTimer.call(this, [callback, 0].concat(passThruArgs), false);
    3066         },
    3067 
    3068         clearImmediate: function clearImmediate(timerId) {
    3069             this.clearTimeout(timerId);
    3070         },
    3071 
    3072         tick: function tick(ms) {
    3073             ms = typeof ms == "number" ? ms : parseTime(ms);
    3074             var tickFrom = this.now, tickTo = this.now + ms, previous = this.now;
    3075             var timer = this.firstTimerInRange(tickFrom, tickTo);
    3076 
    3077             var firstException;
    3078             while (timer && tickFrom <= tickTo) {
    3079                 if (this.timeouts[timer.id]) {
    3080                     tickFrom = this.now = timer.callAt;
    3081                     try {
    3082                       this.callTimer(timer);
    3083                     } catch (e) {
    3084                       firstException = firstException || e;
    3085                     }
    3086                 }
    3087 
    3088                 timer = this.firstTimerInRange(previous, tickTo);
    3089                 previous = tickFrom;
    3090             }
    3091 
    3092             this.now = tickTo;
    3093 
    3094             if (firstException) {
    3095               throw firstException;
    3096             }
    3097 
    3098             return this.now;
    3099         },
    3100 
    3101         firstTimerInRange: function (from, to) {
    3102             var timer, smallest = null, originalTimer;
    3103 
    3104             for (var id in this.timeouts) {
    3105                 if (this.timeouts.hasOwnProperty(id)) {
    3106                     if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) {
    3107                         continue;
    3108                     }
    3109 
    3110                     if (smallest === null || this.timeouts[id].callAt < smallest) {
    3111                         originalTimer = this.timeouts[id];
    3112                         smallest = this.timeouts[id].callAt;
    3113 
    3114                         timer = {
    3115                             func: this.timeouts[id].func,
    3116                             callAt: this.timeouts[id].callAt,
    3117                             interval: this.timeouts[id].interval,
    3118                             id: this.timeouts[id].id,
    3119                             invokeArgs: this.timeouts[id].invokeArgs
    3120                         };
    3121                     }
    3122                 }
    3123             }
    3124 
    3125             return timer || null;
    3126         },
    3127 
    3128         callTimer: function (timer) {
    3129             if (typeof timer.interval == "number") {
    3130                 this.timeouts[timer.id].callAt += timer.interval;
    3131             } else {
    3132                 delete this.timeouts[timer.id];
    3133             }
    3134 
    3135             try {
    3136                 if (typeof timer.func == "function") {
    3137                     timer.func.apply(null, timer.invokeArgs);
    3138                 } else {
    3139                     eval(timer.func);
    3140                 }
    3141             } catch (e) {
    3142               var exception = e;
    3143             }
    3144 
    3145             if (!this.timeouts[timer.id]) {
    3146                 if (exception) {
    3147                   throw exception;
    3148                 }
    3149                 return;
    3150             }
    3151 
    3152             if (exception) {
    3153               throw exception;
    3154             }
    3155         },
    3156 
    3157         reset: function reset() {
    3158             this.timeouts = {};
    3159         },
    3160 
    3161         Date: (function () {
    3162             var NativeDate = Date;
    3163 
    3164             function ClockDate(year, month, date, hour, minute, second, ms) {
    3165                 // Defensive and verbose to avoid potential harm in passing
    3166                 // explicit undefined when user does not pass argument
    3167                 switch (arguments.length) {
    3168                 case 0:
    3169                     return new NativeDate(ClockDate.clock.now);
    3170                 case 1:
    3171                     return new NativeDate(year);
    3172                 case 2:
    3173                     return new NativeDate(year, month);
    3174                 case 3:
    3175                     return new NativeDate(year, month, date);
    3176                 case 4:
    3177                     return new NativeDate(year, month, date, hour);
    3178                 case 5:
    3179                     return new NativeDate(year, month, date, hour, minute);
    3180                 case 6:
    3181                     return new NativeDate(year, month, date, hour, minute, second);
    3182                 default:
    3183                     return new NativeDate(year, month, date, hour, minute, second, ms);
    3184                 }
    3185             }
    3186 
    3187             return mirrorDateProperties(ClockDate, NativeDate);
    3188         }())
    3189     };
    3190 
    3191     function mirrorDateProperties(target, source) {
    3192         if (source.now) {
    3193             target.now = function now() {
    3194                 return target.clock.now;
    3195             };
    3196         } else {
    3197             delete target.now;
    3198         }
    3199 
    3200         if (source.toSource) {
    3201             target.toSource = function toSource() {
    3202                 return source.toSource();
    3203             };
    3204         } else {
    3205             delete target.toSource;
    3206         }
    3207 
    3208         target.toString = function toString() {
    3209             return source.toString();
    3210         };
    3211 
    3212         target.prototype = source.prototype;
    3213         target.parse = source.parse;
    3214         target.UTC = source.UTC;
    3215         target.prototype.toUTCString = source.prototype.toUTCString;
    3216 
    3217         for (var prop in source) {
    3218             if (source.hasOwnProperty(prop)) {
    3219                 target[prop] = source[prop];
    3220             }
    3221         }
    3222 
    3223         return target;
    3224     }
    3225 
    3226     var methods = ["Date", "setTimeout", "setInterval",
    3227                    "clearTimeout", "clearInterval"];
    3228 
    3229     if (typeof global.setImmediate !== "undefined") {
    3230         methods.push("setImmediate");
    3231     }
    3232 
    3233     if (typeof global.clearImmediate !== "undefined") {
    3234         methods.push("clearImmediate");
    3235     }
    3236 
    3237     function restore() {
    3238         var method;
    3239 
    3240         for (var i = 0, l = this.methods.length; i < l; i++) {
    3241             method = this.methods[i];
    3242 
    3243             if (global[method].hadOwnProperty) {
    3244                 global[method] = this["_" + method];
    3245             } else {
    3246                 try {
    3247                     delete global[method];
    3248                 } catch (e) {}
    3249             }
    3250         }
    3251 
    3252         // Prevent multiple executions which will completely remove these props
    3253         this.methods = [];
    3254     }
    3255 
    3256     function stubGlobal(method, clock) {
    3257         clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(global, method);
    3258         clock["_" + method] = global[method];
    3259 
    3260         if (method == "Date") {
    3261             var date = mirrorDateProperties(clock[method], global[method]);
    3262             global[method] = date;
    3263         } else {
    3264             global[method] = function () {
    3265                 return clock[method].apply(clock, arguments);
    3266             };
    3267 
    3268             for (var prop in clock[method]) {
    3269                 if (clock[method].hasOwnProperty(prop)) {
    3270                     global[method][prop] = clock[method][prop];
    3271                 }
    3272             }
    3273         }
    3274 
    3275         global[method].clock = clock;
    3276     }
    3277 
    3278     sinon.useFakeTimers = function useFakeTimers(now) {
    3279         var clock = sinon.clock.create(now);
    3280         clock.restore = restore;
    3281         clock.methods = Array.prototype.slice.call(arguments,
    3282                                                    typeof now == "number" ? 1 : 0);
    3283 
    3284         if (clock.methods.length === 0) {
    3285             clock.methods = methods;
    3286         }
    3287 
    3288         for (var i = 0, l = clock.methods.length; i < l; i++) {
    3289             stubGlobal(clock.methods[i], clock);
    3290         }
    3291 
    3292         return clock;
    3293     };
    3294 }(typeof global != "undefined" && typeof global !== "function" ? global : this));
    3295 
    3296 sinon.timers = {
    3297     setTimeout: setTimeout,
    3298     clearTimeout: clearTimeout,
    3299     setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
    3300     clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined),
    3301     setInterval: setInterval,
    3302     clearInterval: clearInterval,
    3303     Date: Date
    3304 };
    3305 
    3306 if (typeof module !== 'undefined' && module.exports) {
    3307     module.exports = sinon;
    3308 }
    3309 
    3310 /*jslint eqeqeq: false, onevar: false*/
    3311 /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
    3312 /**
    3313  * Minimal Event interface implementation
    3314  *
    3315  * Original implementation by Sven Fuchs: https://gist.github.com/995028
    3316  * Modifications and tests by Christian Johansen.
    3317  *
    3318  * @author Sven Fuchs (svenfuchs@artweb-design.de)
    3319  * @author Christian Johansen (christian@cjohansen.no)
    3320  * @license BSD
    3321  *
    3322  * Copyright (c) 2011 Sven Fuchs, Christian Johansen
    3323  */
    3324 
    3325 if (typeof sinon == "undefined") {
    3326     this.sinon = {};
    3327 }
    3328 
    3329 (function () {
    3330     var push = [].push;
    3331 
    3332     sinon.Event = function Event(type, bubbles, cancelable, target) {
    3333         this.initEvent(type, bubbles, cancelable, target);
    3334     };
    3335 
    3336     sinon.Event.prototype = {
    3337         initEvent: function(type, bubbles, cancelable, target) {
    3338             this.type = type;
    3339             this.bubbles = bubbles;
    3340             this.cancelable = cancelable;
    3341             this.target = target;
    3342         },
    3343 
    3344         stopPropagation: function () {},
    3345 
    3346         preventDefault: function () {
    3347             this.defaultPrevented = true;
    3348         }
    3349     };
    3350 
    3351     sinon.EventTarget = {
    3352         addEventListener: function addEventListener(event, listener) {
    3353             this.eventListeners = this.eventListeners || {};
    3354             this.eventListeners[event] = this.eventListeners[event] || [];
    3355             push.call(this.eventListeners[event], listener);
    3356         },
    3357 
    3358         removeEventListener: function removeEventListener(event, listener) {
    3359             var listeners = this.eventListeners && this.eventListeners[event] || [];
    3360 
    3361             for (var i = 0, l = listeners.length; i < l; ++i) {
    3362                 if (listeners[i] == listener) {
    3363                     return listeners.splice(i, 1);
    3364                 }
    3365             }
    3366         },
    3367 
    3368         dispatchEvent: function dispatchEvent(event) {
    3369             var type = event.type;
    3370             var listeners = this.eventListeners && this.eventListeners[type] || [];
    3371 
    3372             for (var i = 0; i < listeners.length; i++) {
    3373                 if (typeof listeners[i] == "function") {
    3374                     listeners[i].call(this, event);
    3375                 } else {
    3376                     listeners[i].handleEvent(event);
    3377                 }
    3378             }
    3379 
    3380             return !!event.defaultPrevented;
    3381         }
    3382     };
    3383 }());
    3384 
    3385 /**
    3386  * @depend ../../sinon.js
    3387  * @depend event.js
    3388  */
    3389 /*jslint eqeqeq: false, onevar: false*/
    3390 /*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/
    3391 /**
    3392  * Fake XMLHttpRequest object
    3393  *
    3394  * @author Christian Johansen (christian@cjohansen.no)
    3395  * @license BSD
    3396  *
    3397  * Copyright (c) 2010-2013 Christian Johansen
    3398  */
    3399 
    3400 // wrapper for global
    3401 (function(global) {
    3402     if (typeof sinon === "undefined") {
    3403         global.sinon = {};
    3404     }
    3405 
    3406     var supportsProgress = typeof ProgressEvent !== "undefined";
    3407     var supportsCustomEvent = typeof CustomEvent !== "undefined";
    3408     sinon.xhr = { XMLHttpRequest: global.XMLHttpRequest };
    3409     var xhr = sinon.xhr;
    3410     xhr.GlobalXMLHttpRequest = global.XMLHttpRequest;
    3411     xhr.GlobalActiveXObject = global.ActiveXObject;
    3412     xhr.supportsActiveX = typeof xhr.GlobalActiveXObject != "undefined";
    3413     xhr.supportsXHR = typeof xhr.GlobalXMLHttpRequest != "undefined";
    3414     xhr.workingXHR = xhr.supportsXHR ? xhr.GlobalXMLHttpRequest : xhr.supportsActiveX
    3415                                      ? function() { return new xhr.GlobalActiveXObject("MSXML2.XMLHTTP.3.0") } : false;
    3416     xhr.supportsCORS = 'withCredentials' in (new sinon.xhr.GlobalXMLHttpRequest());
    3417 
    3418     /*jsl:ignore*/
    3419     var unsafeHeaders = {
    3420         "Accept-Charset": true,
    3421         "Accept-Encoding": true,
    3422         "Connection": true,
    3423         "Content-Length": true,
    3424         "Cookie": true,
    3425         "Cookie2": true,
    3426         "Content-Transfer-Encoding": true,
    3427         "Date": true,
    3428         "Expect": true,
    3429         "Host": true,
    3430         "Keep-Alive": true,
    3431         "Referer": true,
    3432         "TE": true,
    3433         "Trailer": true,
    3434         "Transfer-Encoding": true,
    3435         "Upgrade": true,
    3436         "User-Agent": true,
    3437         "Via": true
    3438     };
    3439     /*jsl:end*/
    3440 
    3441     function FakeXMLHttpRequest() {
    3442         this.readyState = FakeXMLHttpRequest.UNSENT;
    3443         this.requestHeaders = {};
    3444         this.requestBody = null;
    3445         this.status = 0;
    3446         this.statusText = "";
    3447         this.upload = new UploadProgress();
    3448         if (sinon.xhr.supportsCORS) {
    3449             this.withCredentials = false;
    3450         }
    3451 
    3452 
    3453         var xhr = this;
    3454         var events = ["loadstart", "load", "abort", "loadend"];
    3455 
    3456         function addEventListener(eventName) {
    3457             xhr.addEventListener(eventName, function (event) {
    3458                 var listener = xhr["on" + eventName];
    3459 
    3460                 if (listener && typeof listener == "function") {
    3461                     listener(event);
    3462                 }
    3463             });
    3464         }
    3465 
    3466         for (var i = events.length - 1; i >= 0; i--) {
    3467             addEventListener(events[i]);
    3468         }
    3469 
    3470         if (typeof FakeXMLHttpRequest.onCreate == "function") {
    3471             FakeXMLHttpRequest.onCreate(this);
    3472         }
    3473     }
    3474 
    3475     // An upload object is created for each
    3476     // FakeXMLHttpRequest and allows upload
    3477     // events to be simulated using uploadProgress
    3478     // and uploadError.
    3479     function UploadProgress() {
    3480         this.eventListeners = {
    3481             "progress": [],
    3482             "load": [],
    3483             "abort": [],
    3484             "error": []
    3485         }
    3486     }
    3487 
    3488     UploadProgress.prototype.addEventListener = function(event, listener) {
    3489         this.eventListeners[event].push(listener);
    3490     };
    3491 
    3492     UploadProgress.prototype.removeEventListener = function(event, listener) {
    3493         var listeners = this.eventListeners[event] || [];
    3494 
    3495         for (var i = 0, l = listeners.length; i < l; ++i) {
    3496             if (listeners[i] == listener) {
    3497                 return listeners.splice(i, 1);
    3498             }
    3499         }
    3500     };
    3501 
    3502     UploadProgress.prototype.dispatchEvent = function(event) {
    3503         var listeners = this.eventListeners[event.type] || [];
    3504 
    3505         for (var i = 0, listener; (listener = listeners[i]) != null; i++) {
    3506             listener(event);
    3507         }
    3508     };
    3509 
    3510     function verifyState(xhr) {
    3511         if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
    3512             throw new Error("INVALID_STATE_ERR");
    3513         }
    3514 
    3515         if (xhr.sendFlag) {
    3516             throw new Error("INVALID_STATE_ERR");
    3517         }
    3518     }
    3519 
    3520     // filtering to enable a white-list version of Sinon FakeXhr,
    3521     // where whitelisted requests are passed through to real XHR
    3522     function each(collection, callback) {
    3523         if (!collection) return;
    3524         for (var i = 0, l = collection.length; i < l; i += 1) {
    3525             callback(collection[i]);
    3526         }
    3527     }
    3528     function some(collection, callback) {
    3529         for (var index = 0; index < collection.length; index++) {
    3530             if(callback(collection[index]) === true) return true;
    3531         }
    3532         return false;
    3533     }
    3534     // largest arity in XHR is 5 - XHR#open
    3535     var apply = function(obj,method,args) {
    3536         switch(args.length) {
    3537         case 0: return obj[method]();
    3538         case 1: return obj[method](args[0]);
    3539         case 2: return obj[method](args[0],args[1]);
    3540         case 3: return obj[method](args[0],args[1],args[2]);
    3541         case 4: return obj[method](args[0],args[1],args[2],args[3]);
    3542         case 5: return obj[method](args[0],args[1],args[2],args[3],args[4]);
    3543         }
    3544     };
    3545 
    3546     FakeXMLHttpRequest.filters = [];
    3547     FakeXMLHttpRequest.addFilter = function(fn) {
    3548         this.filters.push(fn)
    3549     };
    3550     var IE6Re = /MSIE 6/;
    3551     FakeXMLHttpRequest.defake = function(fakeXhr,xhrArgs) {
    3552         var xhr = new sinon.xhr.workingXHR();
    3553         each(["open","setRequestHeader","send","abort","getResponseHeader",
    3554               "getAllResponseHeaders","addEventListener","overrideMimeType","removeEventListener"],
    3555              function(method) {
    3556                  fakeXhr[method] = function() {
    3557                    return apply(xhr,method,arguments);
    3558                  };
    3559              });
    3560 
    3561         var copyAttrs = function(args) {
    3562             each(args, function(attr) {
    3563               try {
    3564                 fakeXhr[attr] = xhr[attr]
    3565               } catch(e) {
    3566                 if(!IE6Re.test(navigator.userAgent)) throw e;
    3567               }
    3568             });
    3569         };
    3570 
    3571         var stateChange = function() {
    3572             fakeXhr.readyState = xhr.readyState;
    3573             if(xhr.readyState >= FakeXMLHttpRequest.HEADERS_RECEIVED) {
    3574                 copyAttrs(["status","statusText"]);
    3575             }
    3576             if(xhr.readyState >= FakeXMLHttpRequest.LOADING) {
    3577                 copyAttrs(["responseText"]);
    3578             }
    3579             if(xhr.readyState === FakeXMLHttpRequest.DONE) {
    3580                 copyAttrs(["responseXML"]);
    3581             }
    3582             if(fakeXhr.onreadystatechange) fakeXhr.onreadystatechange.call(fakeXhr, { target: fakeXhr });
    3583         };
    3584         if(xhr.addEventListener) {
    3585           for(var event in fakeXhr.eventListeners) {
    3586               if(fakeXhr.eventListeners.hasOwnProperty(event)) {
    3587                   each(fakeXhr.eventListeners[event],function(handler) {
    3588                       xhr.addEventListener(event, handler);
    3589                   });
    3590               }
    3591           }
    3592           xhr.addEventListener("readystatechange",stateChange);
    3593         } else {
    3594           xhr.onreadystatechange = stateChange;
    3595         }
    3596         apply(xhr,"open",xhrArgs);
    3597     };
    3598     FakeXMLHttpRequest.useFilters = false;
    3599 
    3600     function verifyRequestSent(xhr) {
    3601         if (xhr.readyState == FakeXMLHttpRequest.DONE) {
    3602             throw new Error("Request done");
    3603         }
    3604     }
    3605 
    3606     function verifyHeadersReceived(xhr) {
    3607         if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
    3608             throw new Error("No headers received");
    3609         }
    3610     }
    3611 
    3612     function verifyResponseBodyType(body) {
    3613         if (typeof body != "string") {
    3614             var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
    3615                                  body + ", which is not a string.");
    3616             error.name = "InvalidBodyException";
    3617             throw error;
    3618         }
    3619     }
    3620 
    3621     sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, {
    3622         async: true,
    3623 
    3624         open: function open(method, url, async, username, password) {
    3625             this.method = method;
    3626             this.url = url;
    3627             this.async = typeof async == "boolean" ? async : true;
    3628             this.username = username;
    3629             this.password = password;
    3630             this.responseText = null;
    3631             this.responseXML = null;
    3632             this.requestHeaders = {};
    3633             this.sendFlag = false;
    3634             if(sinon.FakeXMLHttpRequest.useFilters === true) {
    3635                 var xhrArgs = arguments;
    3636                 var defake = some(FakeXMLHttpRequest.filters,function(filter) {
    3637                     return filter.apply(this,xhrArgs)
    3638                 });
    3639                 if (defake) {
    3640                   return sinon.FakeXMLHttpRequest.defake(this,arguments);
    3641                 }
    3642             }
    3643             this.readyStateChange(FakeXMLHttpRequest.OPENED);
    3644         },
    3645 
    3646         readyStateChange: function readyStateChange(state) {
    3647             this.readyState = state;
    3648 
    3649             if (typeof this.onreadystatechange == "function") {
    3650                 try {
    3651                     this.onreadystatechange();
    3652                 } catch (e) {
    3653                     sinon.logError("Fake XHR onreadystatechange handler", e);
    3654                 }
    3655             }
    3656 
    3657             this.dispatchEvent(new sinon.Event("readystatechange"));
    3658 
    3659             switch (this.readyState) {
    3660                 case FakeXMLHttpRequest.DONE:
    3661                     this.dispatchEvent(new sinon.Event("load", false, false, this));
    3662                     this.dispatchEvent(new sinon.Event("loadend", false, false, this));
    3663                     this.upload.dispatchEvent(new sinon.Event("load", false, false, this));
    3664                     if (supportsProgress) {
    3665                         this.upload.dispatchEvent(new ProgressEvent("progress", {loaded: 100, total: 100}));
    3666                     }
    3667                     break;
    3668             }
    3669         },
    3670 
    3671         setRequestHeader: function setRequestHeader(header, value) {
    3672             verifyState(this);
    3673 
    3674             if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
    3675                 throw new Error("Refused to set unsafe header \"" + header + "\"");
    3676             }
    3677 
    3678             if (this.requestHeaders[header]) {
    3679                 this.requestHeaders[header] += "," + value;
    3680             } else {
    3681                 this.requestHeaders[header] = value;
    3682             }
    3683         },
    3684 
    3685         // Helps testing
    3686         setResponseHeaders: function setResponseHeaders(headers) {
    3687             this.responseHeaders = {};
    3688 
    3689             for (var header in headers) {
    3690                 if (headers.hasOwnProperty(header)) {
    3691                     this.responseHeaders[header] = headers[header];
    3692                 }
    3693             }
    3694 
    3695             if (this.async) {
    3696                 this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
    3697             } else {
    3698                 this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
    3699             }
    3700         },
    3701 
    3702         // Currently treats ALL data as a DOMString (i.e. no Document)
    3703         send: function send(data) {
    3704             verifyState(this);
    3705 
    3706             if (!/^(get|head)$/i.test(this.method)) {
    3707                 if (this.requestHeaders["Content-Type"]) {
    3708                     var value = this.requestHeaders["Content-Type"].split(";");
    3709                     this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8";
    3710                 } else {
    3711                     this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
    3712                 }
    3713 
    3714                 this.requestBody = data;
    3715             }
    3716 
    3717             this.errorFlag = false;
    3718             this.sendFlag = this.async;
    3719             this.readyStateChange(FakeXMLHttpRequest.OPENED);
    3720 
    3721             if (typeof this.onSend == "function") {
    3722                 this.onSend(this);
    3723             }
    3724 
    3725             this.dispatchEvent(new sinon.Event("loadstart", false, false, this));
    3726         },
    3727 
    3728         abort: function abort() {
    3729             this.aborted = true;
    3730             this.responseText = null;
    3731             this.errorFlag = true;
    3732             this.requestHeaders = {};
    3733 
    3734             if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) {
    3735                 this.readyStateChange(sinon.FakeXMLHttpRequest.DONE);
    3736                 this.sendFlag = false;
    3737             }
    3738 
    3739             this.readyState = sinon.FakeXMLHttpRequest.UNSENT;
    3740 
    3741             this.dispatchEvent(new sinon.Event("abort", false, false, this));
    3742 
    3743             this.upload.dispatchEvent(new sinon.Event("abort", false, false, this));
    3744 
    3745             if (typeof this.onerror === "function") {
    3746                 this.onerror();
    3747             }
    3748         },
    3749 
    3750         getResponseHeader: function getResponseHeader(header) {
    3751             if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
    3752                 return null;
    3753             }
    3754 
    3755             if (/^Set-Cookie2?$/i.test(header)) {
    3756                 return null;
    3757             }
    3758 
    3759             header = header.toLowerCase();
    3760 
    3761             for (var h in this.responseHeaders) {
    3762                 if (h.toLowerCase() == header) {
    3763                     return this.responseHeaders[h];
    3764                 }
    3765             }
    3766 
    3767             return null;
    3768         },
    3769 
    3770         getAllResponseHeaders: function getAllResponseHeaders() {
    3771             if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
    3772                 return "";
    3773             }
    3774 
    3775             var headers = "";
    3776 
    3777             for (var header in this.responseHeaders) {
    3778                 if (this.responseHeaders.hasOwnProperty(header) &&
    3779                     !/^Set-Cookie2?$/i.test(header)) {
    3780                     headers += header + ": " + this.responseHeaders[header] + "\r\n";
    3781                 }
    3782             }
    3783 
    3784             return headers;
    3785         },
    3786 
    3787         setResponseBody: function setResponseBody(body) {
    3788             verifyRequestSent(this);
    3789             verifyHeadersReceived(this);
    3790             verifyResponseBodyType(body);
    3791 
    3792             var chunkSize = this.chunkSize || 10;
    3793             var index = 0;
    3794             this.responseText = "";
    3795 
    3796             do {
    3797                 if (this.async) {
    3798                     this.readyStateChange(FakeXMLHttpRequest.LOADING);
    3799                 }
    3800 
    3801                 this.responseText += body.substring(index, index + chunkSize);
    3802                 index += chunkSize;
    3803             } while (index < body.length);
    3804 
    3805             var type = this.getResponseHeader("Content-Type");
    3806 
    3807             if (this.responseText &&
    3808                 (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
    3809                 try {
    3810                     this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText);
    3811                 } catch (e) {
    3812                     // Unable to parse XML - no biggie
    3813                 }
    3814             }
    3815 
    3816             if (this.async) {
    3817                 this.readyStateChange(FakeXMLHttpRequest.DONE);
    3818             } else {
    3819                 this.readyState = FakeXMLHttpRequest.DONE;
    3820             }
    3821         },
    3822 
    3823         respond: function respond(status, headers, body) {
    3824             this.status = typeof status == "number" ? status : 200;
    3825             this.statusText = FakeXMLHttpRequest.statusCodes[this.status];
    3826             this.setResponseHeaders(headers || {});
    3827             this.setResponseBody(body || "");
    3828         },
    3829 
    3830         uploadProgress: function uploadProgress(progressEventRaw) {
    3831             if (supportsProgress) {
    3832                 this.upload.dispatchEvent(new ProgressEvent("progress", progressEventRaw));
    3833             }
    3834         },
    3835 
    3836         uploadError: function uploadError(error) {
    3837             if (supportsCustomEvent) {
    3838                 this.upload.dispatchEvent(new CustomEvent("error", {"detail": error}));
    3839             }
    3840         }
    3841     });
    3842 
    3843     sinon.extend(FakeXMLHttpRequest, {
    3844         UNSENT: 0,
    3845         OPENED: 1,
    3846         HEADERS_RECEIVED: 2,
    3847         LOADING: 3,
    3848         DONE: 4
    3849     });
    3850 
    3851     // Borrowed from JSpec
    3852     FakeXMLHttpRequest.parseXML = function parseXML(text) {
    3853         var xmlDoc;
    3854 
    3855         if (typeof DOMParser != "undefined") {
    3856             var parser = new DOMParser();
    3857             xmlDoc = parser.parseFromString(text, "text/xml");
    3858         } else {
    3859             xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    3860             xmlDoc.async = "false";
    3861             xmlDoc.loadXML(text);
    3862         }
    3863 
    3864         return xmlDoc;
    3865     };
    3866 
    3867     FakeXMLHttpRequest.statusCodes = {
    3868         100: "Continue",
    3869         101: "Switching Protocols",
    3870         200: "OK",
    3871         201: "Created",
    3872         202: "Accepted",
    3873         203: "Non-Authoritative Information",
    3874         204: "No Content",
    3875         205: "Reset Content",
    3876         206: "Partial Content",
    3877         300: "Multiple Choice",
    3878         301: "Moved Permanently",
    3879         302: "Found",
    3880         303: "See Other",
    3881         304: "Not Modified",
    3882         305: "Use Proxy",
    3883         307: "Temporary Redirect",
    3884         400: "Bad Request",
    3885         401: "Unauthorized",
    3886         402: "Payment Required",
    3887         403: "Forbidden",
    3888         404: "Not Found",
    3889         405: "Method Not Allowed",
    3890         406: "Not Acceptable",
    3891         407: "Proxy Authentication Required",
    3892         408: "Request Timeout",
    3893         409: "Conflict",
    3894         410: "Gone",
    3895         411: "Length Required",
    3896         412: "Precondition Failed",
    3897         413: "Request Entity Too Large",
    3898         414: "Request-URI Too Long",
    3899         415: "Unsupported Media Type",
    3900         416: "Requested Range Not Satisfiable",
    3901         417: "Expectation Failed",
    3902         422: "Unprocessable Entity",
    3903         500: "Internal Server Error",
    3904         501: "Not Implemented",
    3905         502: "Bad Gateway",
    3906         503: "Service Unavailable",
    3907         504: "Gateway Timeout",
    3908         505: "HTTP Version Not Supported"
    3909     };
    3910 
    3911     sinon.useFakeXMLHttpRequest = function () {
    3912         sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) {
    3913             if (xhr.supportsXHR) {
    3914                 global.XMLHttpRequest = xhr.GlobalXMLHttpRequest;
    3915             }
    3916 
    3917             if (xhr.supportsActiveX) {
    3918                 global.ActiveXObject = xhr.GlobalActiveXObject;
    3919             }
    3920 
    3921             delete sinon.FakeXMLHttpRequest.restore;
    3922 
    3923             if (keepOnCreate !== true) {
    3924                 delete sinon.FakeXMLHttpRequest.onCreate;
    3925             }
    3926         };
    3927         if (xhr.supportsXHR) {
    3928             global.XMLHttpRequest = sinon.FakeXMLHttpRequest;
    3929         }
    3930 
    3931         if (xhr.supportsActiveX) {
    3932             global.ActiveXObject = function ActiveXObject(objId) {
    3933                 if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) {
    3934 
    3935                     return new sinon.FakeXMLHttpRequest();
    3936                 }
    3937 
    3938                 return new xhr.GlobalActiveXObject(objId);
    3939             };
    3940         }
    3941 
    3942         return sinon.FakeXMLHttpRequest;
    3943     };
    3944 
    3945     sinon.FakeXMLHttpRequest = FakeXMLHttpRequest;
    3946 
    3947 })(typeof global === "object" ? global : this);
    3948 
    3949 if (typeof module !== 'undefined' && module.exports) {
    3950     module.exports = sinon;
    3951 }
    3952 
    3953 /**
    3954  * @depend fake_xml_http_request.js
    3955  */
    3956 /*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/
    3957 /*global module, require, window*/
    3958 /**
    3959  * The Sinon "server" mimics a web server that receives requests from
    3960  * sinon.FakeXMLHttpRequest and provides an API to respond to those requests,
    3961  * both synchronously and asynchronously. To respond synchronuously, canned
    3962  * answers have to be provided upfront.
    3963  *
    3964  * @author Christian Johansen (christian@cjohansen.no)
    3965  * @license BSD
    3966  *
    3967  * Copyright (c) 2010-2013 Christian Johansen
    3968  */
    3969 
    3970 if (typeof sinon == "undefined") {
    3971     var sinon = {};
    3972 }
    3973 
    3974 sinon.fakeServer = (function () {
    3975     var push = [].push;
    3976     function F() {}
    3977 
    3978     function create(proto) {
    3979         F.prototype = proto;
    3980         return new F();
    3981     }
    3982 
    3983     function responseArray(handler) {
    3984         var response = handler;
    3985 
    3986         if (Object.prototype.toString.call(handler) != "[object Array]") {
    3987             response = [200, {}, handler];
    3988         }
    3989 
    3990         if (typeof response[2] != "string") {
    3991             throw new TypeError("Fake server response body should be string, but was " +
    3992                                 typeof response[2]);
    3993         }
    3994 
    3995         return response;
    3996     }
    3997 
    3998     var wloc = typeof window !== "undefined" ? window.location : {};
    3999     var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host);
    4000 
    4001     function matchOne(response, reqMethod, reqUrl) {
    4002         var rmeth = response.method;
    4003         var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase();
    4004         var url = response.url;
    4005         var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl));
    4006 
    4007         return matchMethod && matchUrl;
    4008     }
    4009 
    4010     function match(response, request) {
    4011         var requestUrl = request.url;
    4012 
    4013         if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) {
    4014             requestUrl = requestUrl.replace(rCurrLoc, "");
    4015         }
    4016 
    4017         if (matchOne(response, this.getHTTPMethod(request), requestUrl)) {
    4018             if (typeof response.response == "function") {
    4019                 var ru = response.url;
    4020                 var args = [request].concat(ru && typeof ru.exec == "function" ? ru.exec(requestUrl).slice(1) : []);
    4021                 return response.response.apply(response, args);
    4022             }
    4023 
    4024             return true;
    4025         }
    4026 
    4027         return false;
    4028     }
    4029 
    4030     function log(response, request) {
    4031         var str;
    4032 
    4033         str =  "Request:\n"  + sinon.format(request)  + "\n\n";
    4034         str += "Response:\n" + sinon.format(response) + "\n\n";
    4035 
    4036         sinon.log(str);
    4037     }
    4038 
    4039     return {
    4040         create: function () {
    4041             var server = create(this);
    4042             this.xhr = sinon.useFakeXMLHttpRequest();
    4043             server.requests = [];
    4044 
    4045             this.xhr.onCreate = function (xhrObj) {
    4046                 server.addRequest(xhrObj);
    4047             };
    4048 
    4049             return server;
    4050         },
    4051 
    4052         addRequest: function addRequest(xhrObj) {
    4053             var server = this;
    4054             push.call(this.requests, xhrObj);
    4055 
    4056             xhrObj.onSend = function () {
    4057                 server.handleRequest(this);
    4058 
    4059                 if (server.autoRespond && !server.responding) {
    4060                     setTimeout(function () {
    4061                         server.responding = false;
    4062                         server.respond();
    4063                     }, server.autoRespondAfter || 10);
    4064 
    4065                     server.responding = true;
    4066                 }
    4067             };
    4068         },
    4069 
    4070         getHTTPMethod: function getHTTPMethod(request) {
    4071             if (this.fakeHTTPMethods && /post/i.test(request.method)) {
    4072                 var matches = (request.requestBody || "").match(/_method=([^\b;]+)/);
    4073                 return !!matches ? matches[1] : request.method;
    4074             }
    4075 
    4076             return request.method;
    4077         },
    4078 
    4079         handleRequest: function handleRequest(xhr) {
    4080             if (xhr.async) {
    4081                 if (!this.queue) {
    4082                     this.queue = [];
    4083                 }
    4084 
    4085                 push.call(this.queue, xhr);
    4086             } else {
    4087                 this.processRequest(xhr);
    4088             }
    4089         },
    4090 
    4091         respondWith: function respondWith(method, url, body) {
    4092             if (arguments.length == 1 && typeof method != "function") {
    4093                 this.response = responseArray(method);
    4094                 return;
    4095             }
    4096 
    4097             if (!this.responses) { this.responses = []; }
    4098 
    4099             if (arguments.length == 1) {
    4100                 body = method;
    4101                 url = method = null;
    4102             }
    4103 
    4104             if (arguments.length == 2) {
    4105                 body = url;
    4106                 url = method;
    4107                 method = null;
    4108             }
    4109 
    4110             push.call(this.responses, {
    4111                 method: method,
    4112                 url: url,
    4113                 response: typeof body == "function" ? body : responseArray(body)
    4114             });
    4115         },
    4116 
    4117         respond: function respond() {
    4118             if (arguments.length > 0) this.respondWith.apply(this, arguments);
    4119             var queue = this.queue || [];
    4120             var requests = queue.splice(0);
    4121             var request;
    4122 
    4123             while(request = requests.shift()) {
    4124                 this.processRequest(request);
    4125             }
    4126         },
    4127 
    4128         processRequest: function processRequest(request) {
    4129             try {
    4130                 if (request.aborted) {
    4131                     return;
    4132                 }
    4133 
    4134                 var response = this.response || [404, {}, ""];
    4135 
    4136                 if (this.responses) {
    4137                     for (var l = this.responses.length, i = l - 1; i >= 0; i--) {
    4138                         if (match.call(this, this.responses[i], request)) {
    4139                             response = this.responses[i].response;
    4140                             break;
    4141                         }
    4142                     }
    4143                 }
    4144 
    4145                 if (request.readyState != 4) {
    4146                     log(response, request);
    4147 
    4148                     request.respond(response[0], response[1], response[2]);
    4149                 }
    4150             } catch (e) {
    4151                 sinon.logError("Fake server request processing", e);
    4152             }
    4153         },
    4154 
    4155         restore: function restore() {
    4156             return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments);
    4157         }
    4158     };
    4159 }());
    4160 
    4161 if (typeof module !== 'undefined' && module.exports) {
    4162     module.exports = sinon;
    4163 }
    4164 
    4165 /**
    4166  * @depend fake_server.js
    4167  * @depend fake_timers.js
    4168  */
    4169 /*jslint browser: true, eqeqeq: false, onevar: false*/
    4170 /*global sinon*/
    4171 /**
    4172  * Add-on for sinon.fakeServer that automatically handles a fake timer along with
    4173  * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery
    4174  * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead,
    4175  * it polls the object for completion with setInterval. Dispite the direct
    4176  * motivation, there is nothing jQuery-specific in this file, so it can be used
    4177  * in any environment where the ajax implementation depends on setInterval or
    4178  * setTimeout.
    4179  *
    4180  * @author Christian Johansen (christian@cjohansen.no)
    4181  * @license BSD
    4182  *
    4183  * Copyright (c) 2010-2013 Christian Johansen
    4184  */
    4185 
    4186 (function () {
    4187     function Server() {}
    4188     Server.prototype = sinon.fakeServer;
    4189 
    4190     sinon.fakeServerWithClock = new Server();
    4191 
    4192     sinon.fakeServerWithClock.addRequest = function addRequest(xhr) {
    4193         if (xhr.async) {
    4194             if (typeof setTimeout.clock == "object") {
    4195                 this.clock = setTimeout.clock;
    4196             } else {
    4197                 this.clock = sinon.useFakeTimers();
    4198                 this.resetClock = true;
    4199             }
    4200 
    4201             if (!this.longestTimeout) {
    4202                 var clockSetTimeout = this.clock.setTimeout;
    4203                 var clockSetInterval = this.clock.setInterval;
    4204                 var server = this;
    4205 
    4206                 this.clock.setTimeout = function (fn, timeout) {
    4207                     server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
    4208 
    4209                     return clockSetTimeout.apply(this, arguments);
    4210                 };
    4211 
    4212                 this.clock.setInterval = function (fn, timeout) {
    4213                     server.longestTimeout = Math.max(timeout, server.longestTimeout || 0);
    4214 
    4215                     return clockSetInterval.apply(this, arguments);
    4216                 };
    4217             }
    4218         }
    4219 
    4220         return sinon.fakeServer.addRequest.call(this, xhr);
    4221     };
    4222 
    4223     sinon.fakeServerWithClock.respond = function respond() {
    4224         var returnVal = sinon.fakeServer.respond.apply(this, arguments);
    4225 
    4226         if (this.clock) {
    4227             this.clock.tick(this.longestTimeout || 0);
    4228             this.longestTimeout = 0;
    4229 
    4230             if (this.resetClock) {
    4231                 this.clock.restore();
    4232                 this.resetClock = false;
    4233             }
    4234         }
    4235 
    4236         return returnVal;
    4237     };
    4238 
    4239     sinon.fakeServerWithClock.restore = function restore() {
    4240         if (this.clock) {
    4241             this.clock.restore();
    4242         }
    4243 
    4244         return sinon.fakeServer.restore.apply(this, arguments);
    4245     };
    4246 }());
    4247 
    4248 /**
    4249  * @depend ../sinon.js
    4250  * @depend collection.js
    4251  * @depend util/fake_timers.js
    4252  * @depend util/fake_server_with_clock.js
    4253  */
    4254 /*jslint eqeqeq: false, onevar: false, plusplus: false*/
    4255 /*global require, module*/
    4256 /**
    4257  * Manages fake collections as well as fake utilities such as Sinon's
    4258  * timers and fake XHR implementation in one convenient object.
    4259  *
    4260  * @author Christian Johansen (christian@cjohansen.no)
    4261  * @license BSD
    4262  *
    4263  * Copyright (c) 2010-2013 Christian Johansen
    4264  */
    4265 
    4266 if (typeof module !== 'undefined' && module.exports) {
    4267     var sinon = require("../sinon");
    4268     sinon.extend(sinon, require("./util/fake_timers"));
    4269 }
    4270 
    4271 (function () {
    4272     var push = [].push;
    4273 
    4274     function exposeValue(sandbox, config, key, value) {
    4275         if (!value) {
    4276             return;
    4277         }
    4278 
    4279         if (config.injectInto && !(key in config.injectInto) ) {
    4280             config.injectInto[key] = value;
    4281         } else {
    4282             push.call(sandbox.args, value);
    4283         }
    4284     }
    4285 
    4286     function prepareSandboxFromConfig(config) {
    4287         var sandbox = sinon.create(sinon.sandbox);
    4288 
    4289         if (config.useFakeServer) {
    4290             if (typeof config.useFakeServer == "object") {
    4291                 sandbox.serverPrototype = config.useFakeServer;
    4292             }
    4293 
    4294             sandbox.useFakeServer();
    4295         }
    4296 
    4297         if (config.useFakeTimers) {
    4298             if (typeof config.useFakeTimers == "object") {
    4299                 sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers);
    4300             } else {
    4301                 sandbox.useFakeTimers();
    4302             }
    4303         }
    4304 
    4305         return sandbox;
    4306     }
    4307 
    4308     sinon.sandbox = sinon.extend(sinon.create(sinon.collection), {
    4309         useFakeTimers: function useFakeTimers() {
    4310             this.clock = sinon.useFakeTimers.apply(sinon, arguments);
    4311 
    4312             return this.add(this.clock);
    4313         },
    4314 
    4315         serverPrototype: sinon.fakeServer,
    4316 
    4317         useFakeServer: function useFakeServer() {
    4318             var proto = this.serverPrototype || sinon.fakeServer;
    4319 
    4320             if (!proto || !proto.create) {
    4321                 return null;
    4322             }
    4323 
    4324             this.server = proto.create();
    4325             return this.add(this.server);
    4326         },
    4327 
    4328         inject: function (obj) {
    4329             sinon.collection.inject.call(this, obj);
    4330 
    4331             if (this.clock) {
    4332                 obj.clock = this.clock;
    4333             }
    4334 
    4335             if (this.server) {
    4336                 obj.server = this.server;
    4337                 obj.requests = this.server.requests;
    4338             }
    4339 
    4340             return obj;
    4341         },
    4342 
    4343         create: function (config) {
    4344             if (!config) {
    4345                 return sinon.create(sinon.sandbox);
    4346             }
    4347 
    4348             var sandbox = prepareSandboxFromConfig(config);
    4349             sandbox.args = sandbox.args || [];
    4350             var prop, value, exposed = sandbox.inject({});
    4351 
    4352             if (config.properties) {
    4353                 for (var i = 0, l = config.properties.length; i < l; i++) {
    4354                     prop = config.properties[i];
    4355                     value = exposed[prop] || prop == "sandbox" && sandbox;
    4356                     exposeValue(sandbox, config, prop, value);
    4357                 }
    4358             } else {
    4359                 exposeValue(sandbox, config, "sandbox", value);
    4360             }
    4361 
    4362             return sandbox;
    4363         }
    4364     });
    4365 
    4366     sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer;
    4367 
    4368     if (typeof module !== 'undefined' && module.exports) {
    4369         module.exports = sinon.sandbox;
    4370     }
    4371 }());
    4372 
    4373 /**
    4374  * @depend ../sinon.js
    4375  * @depend stub.js
    4376  * @depend mock.js
    4377  * @depend sandbox.js
    4378  */
    4379 /*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/
    4380 /*global module, require, sinon*/
    4381 /**
    4382  * Test function, sandboxes fakes
    4383  *
    4384  * @author Christian Johansen (christian@cjohansen.no)
    4385  * @license BSD
    4386  *
    4387  * Copyright (c) 2010-2013 Christian Johansen
    4388  */
    4389 
    4390 (function (sinon) {
    4391     var commonJSModule = typeof module !== 'undefined' && module.exports;
    4392 
    4393     if (!sinon && commonJSModule) {
    4394         sinon = require("../sinon");
    4395     }
    4396 
    4397     if (!sinon) {
    4398         return;
    4399     }
    4400 
    4401     function test(callback) {
    4402         var type = typeof callback;
    4403 
    4404         if (type != "function") {
    4405             throw new TypeError("sinon.test needs to wrap a test function, got " + type);
    4406         }
    4407 
    4408         return function () {
    4409             var config = sinon.getConfig(sinon.config);
    4410             config.injectInto = config.injectIntoThis && this || config.injectInto;
    4411             var sandbox = sinon.sandbox.create(config);
    4412             var exception, result;
    4413             var args = Array.prototype.slice.call(arguments).concat(sandbox.args);
    4414 
    4415             try {
    4416                 result = callback.apply(this, args);
    4417             } catch (e) {
    4418                 exception = e;
    4419             }
    4420 
    4421             if (typeof exception !== "undefined") {
    4422                 sandbox.restore();
    4423                 throw exception;
    4424             }
    4425             else {
    4426                 sandbox.verifyAndRestore();
    4427             }
    4428 
    4429             return result;
    4430         };
    4431     }
    4432 
    4433     test.config = {
    4434         injectIntoThis: true,
    4435         injectInto: null,
    4436         properties: ["spy", "stub", "mock", "clock", "server", "requests"],
    4437         useFakeTimers: true,
    4438         useFakeServer: true
    4439     };
    4440 
    4441     if (commonJSModule) {
    4442         module.exports = test;
    4443     } else {
    4444         sinon.test = test;
    4445     }
    4446 }(typeof sinon == "object" && sinon || null));
    4447 
    4448 /**
    4449  * @depend ../sinon.js
    4450  * @depend test.js
    4451  */
    4452 /*jslint eqeqeq: false, onevar: false, eqeqeq: false*/
    4453 /*global module, require, sinon*/
    4454 /**
    4455  * Test case, sandboxes all test functions
    4456  *
    4457  * @author Christian Johansen (christian@cjohansen.no)
    4458  * @license BSD
    4459  *
    4460  * Copyright (c) 2010-2013 Christian Johansen
    4461  */
    4462 
    4463 (function (sinon) {
    4464     var commonJSModule = typeof module !== 'undefined' && module.exports;
    4465 
    4466     if (!sinon && commonJSModule) {
    4467         sinon = require("../sinon");
    4468     }
    4469 
    4470     if (!sinon || !Object.prototype.hasOwnProperty) {
    4471         return;
    4472     }
    4473 
    4474     function createTest(property, setUp, tearDown) {
    4475         return function () {
    4476             if (setUp) {
    4477                 setUp.apply(this, arguments);
    4478             }
    4479 
    4480             var exception, result;
    4481 
    4482             try {
    4483                 result = property.apply(this, arguments);
    4484             } catch (e) {
    4485                 exception = e;
    4486             }
    4487 
    4488             if (tearDown) {
    4489                 tearDown.apply(this, arguments);
    4490             }
    4491 
    4492             if (exception) {
    4493                 throw exception;
    4494             }
    4495 
    4496             return result;
    4497         };
    4498     }
    4499 
    4500     function testCase(tests, prefix) {
    4501         /*jsl:ignore*/
    4502         if (!tests || typeof tests != "object") {
    4503             throw new TypeError("sinon.testCase needs an object with test functions");
    4504         }
    4505         /*jsl:end*/
    4506 
    4507         prefix = prefix || "test";
    4508         var rPrefix = new RegExp("^" + prefix);
    4509         var methods = {}, testName, property, method;
    4510         var setUp = tests.setUp;
    4511         var tearDown = tests.tearDown;
    4512 
    4513         for (testName in tests) {
    4514             if (tests.hasOwnProperty(testName)) {
    4515                 property = tests[testName];
    4516 
    4517                 if (/^(setUp|tearDown)$/.test(testName)) {
    4518                     continue;
    4519                 }
    4520 
    4521                 if (typeof property == "function" && rPrefix.test(testName)) {
    4522                     method = property;
    4523 
    4524                     if (setUp || tearDown) {
    4525                         method = createTest(property, setUp, tearDown);
    4526                     }
    4527 
    4528                     methods[testName] = sinon.test(method);
    4529                 } else {
    4530                     methods[testName] = tests[testName];
    4531                 }
    4532             }
    4533         }
    4534 
    4535         return methods;
    4536     }
    4537 
    4538     if (commonJSModule) {
    4539         module.exports = testCase;
    4540     } else {
    4541         sinon.testCase = testCase;
    4542     }
    4543 }(typeof sinon == "object" && sinon || null));
    4544 
    4545 /**
    4546  * @depend ../sinon.js
    4547  * @depend stub.js
    4548  */
    4549 /*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/
    4550 /*global module, require, sinon*/
    4551 /**
    4552  * Assertions matching the test spy retrieval interface.
    4553  *
    4554  * @author Christian Johansen (christian@cjohansen.no)
    4555  * @license BSD
    4556  *
    4557  * Copyright (c) 2010-2013 Christian Johansen
    4558  */
    4559 
    4560 (function (sinon, global) {
    4561     var commonJSModule = typeof module !== "undefined" && module.exports;
    4562     var slice = Array.prototype.slice;
    4563     var assert;
    4564 
    4565     if (!sinon && commonJSModule) {
    4566         sinon = require("../sinon");
    4567     }
    4568 
    4569     if (!sinon) {
    4570         return;
    4571     }
    4572 
    4573     function verifyIsStub() {
    4574         var method;
    4575 
    4576         for (var i = 0, l = arguments.length; i < l; ++i) {
    4577             method = arguments[i];
    4578 
    4579             if (!method) {
    4580                 assert.fail("fake is not a spy");
    4581             }
    4582 
    4583             if (typeof method != "function") {
    4584                 assert.fail(method + " is not a function");
    4585             }
    4586 
    4587             if (typeof method.getCall != "function") {
    4588                 assert.fail(method + " is not stubbed");
    4589             }
    4590         }
    4591     }
    4592 
    4593     function failAssertion(object, msg) {
    4594         object = object || global;
    4595         var failMethod = object.fail || assert.fail;
    4596         failMethod.call(object, msg);
    4597     }
    4598 
    4599     function mirrorPropAsAssertion(name, method, message) {
    4600         if (arguments.length == 2) {
    4601             message = method;
    4602             method = name;
    4603         }
    4604 
    4605         assert[name] = function (fake) {
    4606             verifyIsStub(fake);
    4607 
    4608             var args = slice.call(arguments, 1);
    4609             var failed = false;
    4610 
    4611             if (typeof method == "function") {
    4612                 failed = !method(fake);
    4613             } else {
    4614                 failed = typeof fake[method] == "function" ?
    4615                     !fake[method].apply(fake, args) : !fake[method];
    4616             }
    4617 
    4618             if (failed) {
    4619                 failAssertion(this, fake.printf.apply(fake, [message].concat(args)));
    4620             } else {
    4621                 assert.pass(name);
    4622             }
    4623         };
    4624     }
    4625 
    4626     function exposedName(prefix, prop) {
    4627         return !prefix || /^fail/.test(prop) ? prop :
    4628             prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1);
    4629     }
    4630 
    4631     assert = {
    4632         failException: "AssertError",
    4633 
    4634         fail: function fail(message) {
    4635             var error = new Error(message);
    4636             error.name = this.failException || assert.failException;
    4637 
    4638             throw error;
    4639         },
    4640 
    4641         pass: function pass(assertion) {},
    4642 
    4643         callOrder: function assertCallOrder() {
    4644             verifyIsStub.apply(null, arguments);
    4645             var expected = "", actual = "";
    4646 
    4647             if (!sinon.calledInOrder(arguments)) {
    4648                 try {
    4649                     expected = [].join.call(arguments, ", ");
    4650                     var calls = slice.call(arguments);
    4651                     var i = calls.length;
    4652                     while (i) {
    4653                         if (!calls[--i].called) {
    4654                             calls.splice(i, 1);
    4655                         }
    4656                     }
    4657                     actual = sinon.orderByFirstCall(calls).join(", ");
    4658                 } catch (e) {
    4659                     // If this fails, we'll just fall back to the blank string
    4660                 }
    4661 
    4662                 failAssertion(this, "expected " + expected + " to be " +
    4663                               "called in order but were called as " + actual);
    4664             } else {
    4665                 assert.pass("callOrder");
    4666             }
    4667         },
    4668 
    4669         callCount: function assertCallCount(method, count) {
    4670             verifyIsStub(method);
    4671 
    4672             if (method.callCount != count) {
    4673                 var msg = "expected %n to be called " + sinon.timesInWords(count) +
    4674                     " but was called %c%C";
    4675                 failAssertion(this, method.printf(msg));
    4676             } else {
    4677                 assert.pass("callCount");
    4678             }
    4679         },
    4680 
    4681         expose: function expose(target, options) {
    4682             if (!target) {
    4683                 throw new TypeError("target is null or undefined");
    4684             }
    4685 
    4686             var o = options || {};
    4687             var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix;
    4688             var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail;
    4689 
    4690             for (var method in this) {
    4691                 if (method != "export" && (includeFail || !/^(fail)/.test(method))) {
    4692                     target[exposedName(prefix, method)] = this[method];
    4693                 }
    4694             }
    4695 
    4696             return target;
    4697         }
    4698     };
    4699 
    4700     mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called");
    4701     mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; },
    4702                           "expected %n to not have been called but was called %c%C");
    4703     mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C");
    4704     mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C");
    4705     mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C");
    4706     mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t");
    4707     mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t");
    4708     mirrorPropAsAssertion("calledWithNew", "expected %n to be called with new");
    4709     mirrorPropAsAssertion("alwaysCalledWithNew", "expected %n to always be called with new");
    4710     mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C");
    4711     mirrorPropAsAssertion("calledWithMatch", "expected %n to be called with match %*%C");
    4712     mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C");
    4713     mirrorPropAsAssertion("alwaysCalledWithMatch", "expected %n to always be called with match %*%C");
    4714     mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C");
    4715     mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C");
    4716     mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C");
    4717     mirrorPropAsAssertion("neverCalledWithMatch", "expected %n to never be called with match %*%C");
    4718     mirrorPropAsAssertion("threw", "%n did not throw exception%C");
    4719     mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C");
    4720 
    4721     if (commonJSModule) {
    4722         module.exports = assert;
    4723     } else {
    4724         sinon.assert = assert;
    4725     }
    4726 }(typeof sinon == "object" && sinon || null, typeof window != "undefined" ? window : (typeof self != "undefined") ? self : global));
    4727 
    4728 return sinon;}.call(typeof window != 'undefined' && window || {}));
  • tests/qunit/wp-admin/js/customize-base.js

    Property changes on: tests/qunit/vendor/sinon.js
    ___________________________________________________________________
    Deleted: svn:eol-style
    ## -1 +0,0 ##
    -native
    \ No newline at end of property
     
    1 /* global wp, test, ok, equal, module */
     1/* global wp */
    22
    33jQuery( function( $ ) {
    44        var FooSuperClass, BarSubClass, foo, bar, ConstructorTestClass, newConstructor, constructorTest, $mockElement, mockString,
    55        firstInitialValue, firstValueInstance, valuesInstance, wasCallbackFired, mockValueCallback;
    66
    7         module( 'Customize Base: Class' );
     7        QUnit.module( 'Customize Base: Class' );
    88
    99        FooSuperClass = wp.customize.Class.extend(
    1010                {
    11                         initialize: function ( instanceProps ) {
     11                        initialize: function( instanceProps ) {
    1212                                $.extend( this, instanceProps || {} );
    1313                        },
    1414                        protoProp: 'protoPropValue'
     
    1717                        staticProp: 'staticPropValue'
    1818                }
    1919        );
    20         test( 'FooSuperClass is a function ', function () {
    21                 equal( typeof FooSuperClass, 'function' );
     20        QUnit.test( 'FooSuperClass is a function', function( assert ) {
     21                assert.equal( typeof FooSuperClass, 'function' );
    2222        });
    23         test( 'FooSuperClass prototype has protoProp', function () {
    24                 equal( FooSuperClass.prototype.protoProp, 'protoPropValue' );
     23        QUnit.test( 'FooSuperClass prototype has protoProp', function( assert ) {
     24                assert.equal( FooSuperClass.prototype.protoProp, 'protoPropValue' );
    2525        });
    26         test( 'FooSuperClass does not have protoProp', function () {
    27                 equal( typeof FooSuperClass.protoProp, 'undefined' );
     26        QUnit.test( 'FooSuperClass does not have protoProp', function( assert ) {
     27                assert.equal( typeof FooSuperClass.protoProp, 'undefined' );
    2828        });
    29         test( 'FooSuperClass has staticProp', function () {
    30                 equal( FooSuperClass.staticProp, 'staticPropValue' );
     29        QUnit.test( 'FooSuperClass has staticProp', function( assert ) {
     30                assert.equal( FooSuperClass.staticProp, 'staticPropValue' );
    3131        });
    32         test( 'FooSuperClass prototype does not have staticProp', function () {
    33                 equal( typeof FooSuperClass.prototype.staticProp, 'undefined' );
     32        QUnit.test( 'FooSuperClass prototype does not have staticProp', function( assert ) {
     33                assert.equal( typeof FooSuperClass.prototype.staticProp, 'undefined' );
    3434        });
    3535
    3636        foo = new FooSuperClass( { instanceProp: 'instancePropValue' } );
    37         test( 'FooSuperClass instance foo extended Class', function () {
    38                 equal( foo.extended( wp.customize.Class ), true );
     37        QUnit.test( 'FooSuperClass instance foo extended Class', function( assert ) {
     38                assert.equal( foo.extended( wp.customize.Class ), true );
    3939        });
    40         test( 'foo instance has protoProp', function () {
    41                 equal( foo.protoProp, 'protoPropValue' );
     40        QUnit.test( 'foo instance has protoProp', function( assert ) {
     41                assert.equal( foo.protoProp, 'protoPropValue' );
    4242        });
    43         test( 'foo instance does not have staticProp', function () {
    44                 equal( typeof foo.staticProp, 'undefined' );
     43        QUnit.test( 'foo instance does not have staticProp', function( assert ) {
     44                assert.equal( typeof foo.staticProp, 'undefined' );
    4545        });
    46         test( 'FooSuperClass instance foo ran initialize() and has supplied instanceProp', function () {
    47                 equal( foo.instanceProp, 'instancePropValue' );
     46        QUnit.test( 'FooSuperClass instance foo ran initialize() and has supplied instanceProp', function( assert ) {
     47                assert.equal( foo.instanceProp, 'instancePropValue' );
    4848        });
    4949
    5050        // @todo Test Class.applicator?
    5151        // @todo Do we test object.instance?
    5252
    53         module( 'Customize Base: Subclass' );
     53        QUnit.module( 'Customize Base: Subclass' );
    5454
    5555        BarSubClass = FooSuperClass.extend(
    5656                {
     
    6464                        subStaticProp: 'subStaticPropValue'
    6565                }
    6666        );
    67         test( 'BarSubClass prototype has subProtoProp', function () {
    68                 equal( BarSubClass.prototype.subProtoProp, 'subProtoPropValue' );
     67        QUnit.test( 'BarSubClass prototype has subProtoProp', function( assert ) {
     68                assert.equal( BarSubClass.prototype.subProtoProp, 'subProtoPropValue' );
    6969        });
    70         test( 'BarSubClass prototype has parent FooSuperClass protoProp', function () {
    71                 equal( BarSubClass.prototype.protoProp, 'protoPropValue' );
     70        QUnit.test( 'BarSubClass prototype has parent FooSuperClass protoProp', function( assert ) {
     71                assert.equal( BarSubClass.prototype.protoProp, 'protoPropValue' );
    7272        });
    7373
    7474        bar = new BarSubClass( { instanceProp: 'instancePropValue' } );
    75         test( 'BarSubClass instance bar its initialize() and parent initialize() run', function () {
    76                 equal( bar.instanceProp, 'instancePropValue' );
    77                 equal( bar.subInstanceProp, 'subInstancePropValue' );
     75        QUnit.test( 'BarSubClass instance bar its initialize() and parent initialize() run', function( assert ) {
     76                assert.equal( bar.instanceProp, 'instancePropValue' );
     77                assert.equal( bar.subInstanceProp, 'subInstancePropValue' );
    7878        });
    7979
    80         test( 'BarSubClass instance bar extended FooSuperClass', function () {
    81                 equal( bar.extended( FooSuperClass ), true );
     80        QUnit.test( 'BarSubClass instance bar extended FooSuperClass', function( assert ) {
     81                assert.equal( bar.extended( FooSuperClass ), true );
    8282        });
    8383
    8484
    8585        // Implements todo: Test Class.constructor() manipulation.
    86         module( 'Customize Base: Constructor Manipulation' );
     86        QUnit.module( 'Customize Base: Constructor Manipulation' );
    8787
    8888        newConstructor = function ( instanceProps ) {
    8989                        $.extend( this , instanceProps || {} );
     
    9999                }
    100100        );
    101101
    102         test( 'New constructor added to class' , function () {
    103                 equal( ConstructorTestClass.prototype.constructor , newConstructor );
     102        QUnit.test( 'New constructor added to class', function( assert ) {
     103                assert.equal( ConstructorTestClass.prototype.constructor , newConstructor );
    104104        });
    105         test( 'Class with new constructor has protoPropValue' , function () {
    106                 equal( ConstructorTestClass.prototype.protoProp , 'protoPropValue' );
     105        QUnit.test( 'Class with new constructor has protoPropValue', function( assert ) {
     106                assert.equal( ConstructorTestClass.prototype.protoProp , 'protoPropValue' );
    107107        });
    108108
    109109        constructorTest = new ConstructorTestClass( { instanceProp: 'instancePropValue' } );
    110                 test( 'ConstructorTestClass instance constructorTest has the new constructor', function () {
    111                 equal( constructorTest.constructor, newConstructor );
     110                QUnit.test( 'ConstructorTestClass instance constructorTest has the new constructor', function( assert ) {
     111                assert.equal( constructorTest.constructor, newConstructor );
    112112        });
    113113
    114         test( 'ConstructorTestClass instance constructorTest extended Class', function () {
    115                 equal( constructorTest.extended( wp.customize.Class ), true );
     114        QUnit.test( 'ConstructorTestClass instance constructorTest extended Class', function( assert ) {
     115                assert.equal( constructorTest.extended( wp.customize.Class ), true );
    116116        });
    117117
    118         test( 'ConstructorTestClass instance constructorTest has the added instance property', function () {
    119                 equal( constructorTest.instanceProp , 'instancePropValue' );
     118        QUnit.test( 'ConstructorTestClass instance constructorTest has the added instance property', function( assert ) {
     119                assert.equal( constructorTest.instanceProp , 'instancePropValue' );
    120120        });
    121121
    122122
    123         module( 'Customize Base: wp.customizer.ensure' );
     123        QUnit.module( 'Customize Base: wp.customizer.ensure' );
    124124
    125125        $mockElement = $( '<div id="mockElement"></div>' );
    126126
    127         test( 'Handles jQuery argument' , function() {
    128                 equal( wp.customize.ensure( $mockElement ) , $mockElement );
     127        QUnit.test( 'Handles jQuery argument', function( assert ) {
     128                assert.equal( wp.customize.ensure( $mockElement ) , $mockElement );
    129129        });
    130130
    131131        mockString = '<div class="mockString"></div>';
    132132
    133         test( 'Handles string argument' , function() {
    134                 ok( wp.customize.ensure( mockString ) instanceof jQuery );
     133        QUnit.test( 'Handles string argument', function( assert ) {
     134                assert.ok( wp.customize.ensure( mockString ) instanceof jQuery );
    135135        });
    136136
    137137
    138         module( 'Customize Base: Value Class' );
     138        QUnit.module( 'Customize Base: Value Class' );
    139139
    140140        firstInitialValue = true;
    141141        firstValueInstance = new wp.customize.Value( firstInitialValue );
    142142
    143         test( 'Initialized with the right value' , function() {
    144                 equal( firstValueInstance.get() , firstInitialValue );
     143        QUnit.test( 'Initialized with the right value', function( assert ) {
     144                assert.equal( firstValueInstance.get() , firstInitialValue );
    145145        });
    146146
    147         test( '.set() works' , function() {
     147        QUnit.test( '.set() works', function( assert ) {
    148148                firstValueInstance.set( false );
    149                 equal( firstValueInstance.get() , false );
     149                assert.equal( firstValueInstance.get() , false );
    150150        });
    151151
    152         test( '.bind() adds new callback that fires on set()' , function() {
     152        QUnit.test( '.bind() adds new callback that fires on set()', function( assert ) {
    153153                wasCallbackFired = false;
    154154                mockValueCallback = function() {
    155155                        wasCallbackFired = true;
     
    156156                };
    157157                firstValueInstance.bind( mockValueCallback );
    158158                firstValueInstance.set( 'newValue' );
    159                 ok( wasCallbackFired );
     159                assert.ok( wasCallbackFired );
    160160        });
    161161
    162         module( 'Customize Base: Values Class' );
     162        QUnit.module( 'Customize Base: Values Class' );
    163163
    164164        valuesInstance = new wp.customize.Values();
    165165
    166         test( 'Correct events are triggered when adding to or removing from Values collection', function() {
     166        QUnit.test( 'Correct events are triggered when adding to or removing from Values collection', function( assert ) {
    167167                var hasFooOnAdd = false,
    168168                        hasFooOnRemove = false,
    169169                        hasFooOnRemoved = true,
     
    179179                        valuePassedToAdd = value;
    180180                } );
    181181                valuesInstance.add( 'foo', fooValue );
    182                 ok( hasFooOnAdd );
    183                 equal( valuePassedToAdd.get(), fooValue.get() );
     182                assert.ok( hasFooOnAdd );
     183                assert.equal( valuePassedToAdd.get(), fooValue.get() );
    184184
    185185                // Test events when removing the value.
    186186                valuesInstance.bind( 'remove', function( value ) {
     
    194194                        wasEventFiredOnRemoval = true;
    195195                } );
    196196                valuesInstance.remove( 'foo' );
    197                 ok( hasFooOnRemove );
    198                 equal( valuePassedToRemove.get(), fooValue.get() );
    199                 ok( ! hasFooOnRemoved );
    200                 equal( valuePassedToRemoved.get(), fooValue.get() );
     197                assert.ok( hasFooOnRemove );
     198                assert.equal( valuePassedToRemove.get(), fooValue.get() );
     199                assert.ok( ! hasFooOnRemoved );
     200                assert.equal( valuePassedToRemoved.get(), fooValue.get() );
    201201
    202202                // Confirm no events are fired when nonexistent value is removed.
    203203                wasEventFiredOnRemoval = false;
    204204                valuesInstance.remove( 'bar' );
    205                 ok( ! wasEventFiredOnRemoval );
     205                assert.ok( ! wasEventFiredOnRemoval );
    206206        });
    207207
    208         module( 'Customize Base: Notification' );
    209         test( 'Notification object exists and has expected properties', function ( assert ) {
     208        QUnit.module( 'Customize Base: Notification' );
     209        QUnit.test( 'Notification object exists and has expected properties', function ( assert ) {
    210210                var notification = new wp.customize.Notification( 'mycode', {
    211211                        'message': 'Hello World',
    212212                        'type': 'update',
     
    231231                assert.equal( null, notification.data );
    232232        } );
    233233
    234         module( 'Customize Base: utils.parseQueryString' );
    235         test( 'wp.customize.utils.parseQueryString works', function( assert ) {
     234        QUnit.module( 'Customize Base: utils.parseQueryString' );
     235        QUnit.test( 'wp.customize.utils.parseQueryString works', function( assert ) {
    236236                var queryParams;
    237237                queryParams = wp.customize.utils.parseQueryString( 'a=1&b=2' );
    238238                assert.ok( _.isEqual( queryParams, { a: '1', b: '2' } ) );
  • tests/qunit/wp-admin/js/customize-controls-utils.js

     
    44        var trueMockEvent, falseMockEvent, mockElementLists, $firstMockElement, $secondMockElement, $thirdMockElement,
    55                BubbleTester, BubbleTesterTwoValues, bubbleTesterParent, firstBubbleTester, secondBubbleTester;
    66
    7         module( 'Customizer Model Utility functions' );
     7        QUnit.module( 'Customizer Model Utility functions' );
    88
    99        trueMockEvent = {
    1010                type : 'keydown',
     
    1616                which : 13
    1717        };
    1818
    19         test( 'isKeydownButNotEnterEvent returns true' , function () {
    20                 ok( wp.customize.utils.isKeydownButNotEnterEvent( trueMockEvent ) );
     19        QUnit.test( 'isKeydownButNotEnterEvent returns true', function( assert ) {
     20                assert.ok( wp.customize.utils.isKeydownButNotEnterEvent( trueMockEvent ) );
    2121        });
    2222
    23         test( 'isKeydownButNotEnterEvent returns false' , function () {
    24                 equal( wp.customize.utils.isKeydownButNotEnterEvent( falseMockEvent ) , false );
     23        QUnit.test( 'isKeydownButNotEnterEvent returns false', function( assert ) {
     24                assert.equal( wp.customize.utils.isKeydownButNotEnterEvent( falseMockEvent ) , false );
    2525        });
    2626
    2727        $firstMockElement = $( '<div id="foo"></div>' );
     
    3636                thirdButLonger : [ $firstMockElement, $secondMockElement, $thirdMockElement ]
    3737        };
    3838
    39         test( 'areElementListsEqual returns true' , function () {
    40                 ok( wp.customize.utils.areElementListsEqual( mockElementLists.first , mockElementLists.first ) );
     39        QUnit.test( 'areElementListsEqual returns true', function( assert ) {
     40                assert.ok( wp.customize.utils.areElementListsEqual( mockElementLists.first , mockElementLists.first ) );
    4141        });
    4242
    43         test( 'areElementListsEqual returns false' , function () {
    44                 equal( wp.customize.utils.areElementListsEqual( mockElementLists.first , mockElementLists.second ) , false );
     43        QUnit.test( 'areElementListsEqual returns false', function( assert ) {
     44                assert.equal( wp.customize.utils.areElementListsEqual( mockElementLists.first , mockElementLists.second ) , false );
    4545        });
    4646
    47         test( 'areElementListsEqual: lists have same values, but in reverse order' , function () {
    48                 equal( wp.customize.utils.areElementListsEqual( mockElementLists.first , mockElementLists.firstInReverseOrder ) , false );
     47        QUnit.test( 'areElementListsEqual: lists have same values, but in reverse order', function( assert ) {
     48                assert.equal( wp.customize.utils.areElementListsEqual( mockElementLists.first , mockElementLists.firstInReverseOrder ) , false );
    4949        });
    5050
    51         test( 'areElementListsEqual: lists have same values, but one is longer' , function () {
    52                 equal( wp.customize.utils.areElementListsEqual( mockElementLists.third , mockElementLists.thirdButLonger ) , false );
     51        QUnit.test( 'areElementListsEqual: lists have same values, but one is longer', function( assert ) {
     52                assert.equal( wp.customize.utils.areElementListsEqual( mockElementLists.third , mockElementLists.thirdButLonger ) , false );
    5353        });
    5454
    5555
     
    7272                }
    7373        );
    7474
    75         test( 'bubbleChildValueChanges notifies parent of change' , function() {
     75        QUnit.test( 'bubbleChildValueChanges notifies parent of change', function( assert ) {
    7676                firstBubbleTester = new BubbleTester();
    7777                wp.customize.utils.bubbleChildValueChanges( firstBubbleTester , [ 'fooValue' ] );
    7878                firstBubbleTester.fooValue.set( 'new value' );
    79                 ok( firstBubbleTester.parent.wasChangeTriggered );
     79                assert.ok( firstBubbleTester.parent.wasChangeTriggered );
    8080        });
    8181
    82         test( 'bubbleChildValueChanges passes a reference to its instance' , function() {
    83                 ok( firstBubbleTester.parent.instancePassedInTrigger instanceof BubbleTester );
     82        QUnit.test( 'bubbleChildValueChanges passes a reference to its instance', function( assert ) {
     83                assert.ok( firstBubbleTester.parent.instancePassedInTrigger instanceof BubbleTester );
    8484        });
    8585
    8686        BubbleTesterTwoValues = wp.customize.Class.extend(
     
    9898        wp.customize.utils.bubbleChildValueChanges( secondBubbleTester , [ 'exampleValue' , 'barValue' ] );
    9999        secondBubbleTester.barValue.set( 'new value' );
    100100
    101         test( 'bubbleChildValueChanges notifies parent of change when two values are bound' , function() {
    102                 ok( secondBubbleTester.parent.wasChangeTriggered );
     101        QUnit.test( 'bubbleChildValueChanges notifies parent of change when two values are bound', function( assert ) {
     102                assert.ok( secondBubbleTester.parent.wasChangeTriggered );
    103103        });
    104104
    105         test( 'bubbleChildValueChanges passes a reference to its instance when two values are bound' , function() {
    106                 ok( secondBubbleTester.parent.instancePassedInTrigger instanceof BubbleTesterTwoValues );
     105        QUnit.test( 'bubbleChildValueChanges passes a reference to its instance when two values are bound', function( assert ) {
     106                assert.ok( secondBubbleTester.parent.instancePassedInTrigger instanceof BubbleTesterTwoValues );
    107107        });
    108108});
  • tests/qunit/wp-admin/js/customize-controls.js

     
    1 /* global JSON, wp, test, ok, equal, module */
     1/* global JSON, wp */
    22
    33wp.customize.settingConstructor.abbreviation = wp.customize.Setting.extend({
    44        validate: function( value ) {
     
    66        }
    77});
    88
    9 jQuery( window ).load( function (){
     9jQuery( window ).load( function() {
    1010        'use strict';
    1111
    1212        var controlId, controlLabel, controlType, controlContent, controlDescription, controlData, mockControl,
     
    1919                        throw new Error( 'Must pass value type in expectedValues.' );
    2020                }
    2121                var type = expectedValues.type;
    22                 test( 'Model extends proper type', function () {
    23                         ok( model.extended( wp.customize[ type ] ) );
     22                QUnit.test( 'Model extends proper type', function( assert ) {
     23                        assert.ok( model.extended( wp.customize[ type ] ) );
    2424                } );
    2525
    2626                if ( expectedValues.hasOwnProperty( 'id' ) ) {
    27                         test( type + ' instance has the right id', function () {
    28                                 equal( model.id, expectedValues.id );
     27                        QUnit.test( type + ' instance has the right id', function( assert ) {
     28                                assert.equal( model.id, expectedValues.id );
    2929                        });
    3030                }
    3131                if ( expectedValues.hasOwnProperty( 'title') ) {
    32                         test( type + ' instance has the right title.', function () {
    33                                 equal( model.params.title, expectedValues.title );
     32                        QUnit.test( type + ' instance has the right title.', function( assert ) {
     33                                assert.equal( model.params.title, expectedValues.title );
    3434                        });
    3535                }
    3636                if ( expectedValues.hasOwnProperty( 'description' ) ) {
    37                         test( type + ' instance has the right description.', function () {
    38                                 equal( model.params.description, expectedValues.description );
     37                        QUnit.test( type + ' instance has the right description.', function( assert ) {
     38                                assert.equal( model.params.description, expectedValues.description );
    3939                        });
    4040                }
    4141                if ( expectedValues.hasOwnProperty( 'content' ) ) {
    42                         test( type + ' instance has the right content.', function () {
    43                                 equal( model.params.content, expectedValues.content );
     42                        QUnit.test( type + ' instance has the right content.', function( assert ) {
     43                                assert.equal( model.params.content, expectedValues.content );
    4444                        });
    4545                }
    4646                if ( expectedValues.hasOwnProperty( 'priority' ) ) {
    47                         test( type + ' instance has the right priority.', function () {
    48                                 equal( model.priority(), expectedValues.priority );
     47                        QUnit.test( type + ' instance has the right priority.', function( assert ) {
     48                                assert.equal( model.priority(), expectedValues.priority );
    4949                        });
    5050                }
    5151                if ( expectedValues.hasOwnProperty( 'active' ) ) {
    52                         test( type + ' instance has the right active state.', function () {
    53                                 equal( model.active(), expectedValues.active );
     52                        QUnit.test( type + ' instance has the right active state.', function( assert ) {
     53                                assert.equal( model.active(), expectedValues.active );
    5454                        });
    5555                }
    56                 test( type + ' can be deactivated', function () {
     56                QUnit.test( type + ' can be deactivated', function( assert ) {
    5757                        model.activate();
    5858                        model.deactivate();
    59                         equal( model.active(), false );
     59                        assert.equal( model.active(), false );
    6060                        model.activate();
    61                         equal( model.active(), true );
    62                         ok(true);
     61                        assert.equal( model.active(), true );
     62                        assert.ok(true);
    6363                });
    6464
    6565                if ( type === 'Panel' || type === 'Section' ) {
    6666                        if ( expectedValues.hasOwnProperty( 'expanded' ) ) {
    67                                 test( type + ' instance has the right expanded state.', function () {
    68                                         equal( model.expanded(), expectedValues.expanded );
     67                                QUnit.test( type + ' instance has the right expanded state.', function( assert ) {
     68                                        assert.equal( model.expanded(), expectedValues.expanded );
    6969                                } );
    7070                        }
    7171
    72                         test( type + ' instance is collapsed after calling .collapse()', function () {
     72                        QUnit.test( type + ' instance is collapsed after calling .collapse()', function( assert ) {
    7373                                model.collapse();
    74                                 ok( ! model.expanded() );
     74                                assert.ok( ! model.expanded() );
    7575                        });
    7676
    77                         test( type + ' instance is expanded after calling .expand()', function () {
     77                        QUnit.test( type + ' instance is expanded after calling .expand()', function( assert ) {
    7878                                model.expand();
    79                                 ok( model.expanded() );
     79                                assert.ok( model.expanded() );
    8080                        });
    8181                }
    8282
    8383        };
    8484
    85         module( 'Customizer notifications collection' );
    86         test( 'Notifications collection exists', function() {
    87                 ok( wp.customize.notifications );
    88                 equal( wp.customize.notifications.defaultConstructor, wp.customize.Notification );
     85        QUnit.module( 'Customizer notifications collection' );
     86        QUnit.test( 'Notifications collection exists', function( assert ) {
     87                assert.ok( wp.customize.notifications );
     88                assert.equal( wp.customize.notifications.defaultConstructor, wp.customize.Notification );
    8989        } );
    9090
    91         test( 'Notification objects are rendered as part of notifications collection', function() {
     91        QUnit.test( 'Notification objects are rendered as part of notifications collection', function( assert ) {
    9292                var container = jQuery( '#customize-notifications-test' ), items, collection;
    9393
    9494                collection = new wp.customize.Notifications({
     
    9797                collection.add( 'mycode-1', new wp.customize.Notification( 'mycode-1', { message: 'My message 1' } ) );
    9898                collection.render();
    9999                items = collection.container.find( 'li' );
    100                 equal( items.length, 1 );
    101                 equal( items.first().data( 'code' ), 'mycode-1' );
     100                assert.equal( items.length, 1 );
     101                assert.equal( items.first().data( 'code' ), 'mycode-1' );
    102102
    103103                collection.add( 'mycode-2', new wp.customize.Notification( 'mycode-2', {
    104104                        message: 'My message 2',
     
    106106                } ) );
    107107                collection.render();
    108108                items = collection.container.find( 'li' );
    109                 equal( items.length, 2 );
    110                 equal( items.first().data( 'code' ), 'mycode-2' );
    111                 equal( items.last().data( 'code' ), 'mycode-1' );
     109                assert.equal( items.length, 2 );
     110                assert.equal( items.first().data( 'code' ), 'mycode-2' );
     111                assert.equal( items.last().data( 'code' ), 'mycode-1' );
    112112
    113                 equal( items.first().find( '.notice-dismiss' ).length, 1 );
    114                 equal( items.last().find( '.notice-dismiss' ).length, 0 );
     113                assert.equal( items.first().find( '.notice-dismiss' ).length, 1 );
     114                assert.equal( items.last().find( '.notice-dismiss' ).length, 0 );
    115115
    116116                collection.remove( 'mycode-2' );
    117117                collection.render();
    118118                items = collection.container.find( 'li' );
    119                 equal( items.length, 1 );
    120                 equal( items.first().data( 'code' ), 'mycode-1' );
     119                assert.equal( items.length, 1 );
     120                assert.equal( items.first().data( 'code' ), 'mycode-1' );
    121121
    122122                collection.remove( 'mycode-1' );
    123123                collection.render();
    124                 ok( collection.container.is( ':hidden' ), 'Notifications area is hidden.' );
     124                assert.ok( collection.container.is( ':hidden' ), 'Notifications area is hidden.' );
    125125        } );
    126126
    127         module( 'Customizer Previewed Device' );
    128         test( 'Previewed device defaults to desktop.', function () {
    129                 equal( wp.customize.previewedDevice.get(), 'desktop' );
     127        QUnit.module( 'Customizer Previewed Device' );
     128        QUnit.test( 'Previewed device defaults to desktop.', function( assert ) {
     129                assert.equal( wp.customize.previewedDevice.get(), 'desktop' );
    130130        } );
    131131
    132         module( 'Customizer Setting in Fixture' );
    133         test( 'Setting has fixture value', function () {
    134                 equal( wp.customize( 'fixture-setting' )(), 'Lorem Ipsum' );
     132        QUnit.module( 'Customizer Setting in Fixture' );
     133        QUnit.test( 'Setting has fixture value', function( assert ) {
     134                assert.equal( wp.customize( 'fixture-setting' )(), 'Lorem Ipsum' );
    135135        } );
    136         test( 'Setting has notifications', function () {
     136        QUnit.test( 'Setting has notifications', function( assert ) {
    137137                var setting = wp.customize( 'fixture-setting' );
    138                 ok( setting.notifications.extended( wp.customize.Values ) );
    139                 equal( wp.customize.Notification, setting.notifications.prototype.constructor.defaultConstructor );
     138                assert.ok( setting.notifications.extended( wp.customize.Values ) );
     139                assert.equal( wp.customize.Notification, setting.notifications.prototype.constructor.defaultConstructor );
    140140        } );
    141         test( 'Setting has findControls method', function() {
     141        QUnit.test( 'Setting has findControls method', function( assert ) {
    142142                var controls, setting = wp.customize( 'fixture-setting' );
    143                 equal( 'function', typeof setting.findControls );
     143                assert.equal( 'function', typeof setting.findControls );
    144144                controls = setting.findControls();
    145                 equal( 1, controls.length );
    146                 equal( 'fixture-control', controls[0].id );
     145                assert.equal( 1, controls.length );
     146                assert.equal( 'fixture-control', controls[0].id );
    147147        } );
    148         test( 'Setting constructor object exists', function( assert ) {
     148        QUnit.test( 'Setting constructor object exists', function( assert ) {
    149149                assert.ok( _.isObject( wp.customize.settingConstructor ) );
    150150        } );
    151         test( 'Custom setting constructor is used', function( assert ) {
     151        QUnit.test( 'Custom setting constructor is used', function( assert ) {
    152152                var setting = wp.customize( 'fixture-setting-abbr' );
    153153                assert.ok( setting.extended( wp.customize.settingConstructor.abbreviation ) );
    154154                setting.set( 'usa' );
     
    155155                assert.equal( 'USA', setting.get() );
    156156        } );
    157157
    158         module( 'Customizer Control in Fixture' );
    159         test( 'Control exists', function () {
    160                 ok( wp.customize.control.has( 'fixture-control' ) );
     158        QUnit.module( 'Customizer Control in Fixture' );
     159        QUnit.test( 'Control exists', function( assert ) {
     160                assert.ok( wp.customize.control.has( 'fixture-control' ) );
    161161        } );
    162         test( 'Control has the fixture setting', function () {
     162        QUnit.test( 'Control has the fixture setting', function( assert ) {
    163163                var control = wp.customize.control( 'fixture-control' );
    164                 equal( control.setting(), 'Lorem Ipsum' );
    165                 equal( control.setting.id, 'fixture-setting' );
     164                assert.equal( control.setting(), 'Lorem Ipsum' );
     165                assert.equal( control.setting.id, 'fixture-setting' );
    166166        } );
    167         test( 'Control has the section fixture section ID', function () {
     167        QUnit.test( 'Control has the section fixture section ID', function( assert ) {
    168168                var control = wp.customize.control( 'fixture-control' );
    169                 equal( control.section(), 'fixture-section' );
     169                assert.equal( control.section(), 'fixture-section' );
    170170        } );
    171         test( 'Control has notifications', function ( assert ) {
     171        QUnit.test( 'Control has notifications', function ( assert ) {
    172172                var control = wp.customize.control( 'fixture-control' ), settingNotification, controlOnlyNotification, doneEmbedded;
    173173                assert.ok( control.notifications.extended( wp.customize.Values ) );
    174174                assert.equal( wp.customize.Notification, control.notifications.prototype.constructor.defaultConstructor );
     
    215215                } );
    216216        } );
    217217
    218         module( 'Customizer control without associated settings' );
    219         test( 'Control can be created without settings', function() {
     218        QUnit.module( 'Customizer control without associated settings' );
     219        QUnit.test( 'Control can be created without settings', function( assert ) {
    220220                var control = new wp.customize.Control( 'settingless', {
    221221                        params: {
    222222                                content: jQuery( '<li class="settingless">Hello World</li>' ),
     
    224224                        }
    225225                } );
    226226                wp.customize.control.add( control.id, control );
    227                 equal( control.deferred.embedded.state(), 'resolved' );
    228                 ok( null === control.setting );
    229                 ok( jQuery.isEmptyObject( control.settings ) );
     227                assert.equal( control.deferred.embedded.state(), 'resolved' );
     228                assert.ok( null === control.setting );
     229                assert.ok( jQuery.isEmptyObject( control.settings ) );
    230230        } );
    231231
    232232        // Begin sections.
    233         module( 'Customizer Section in Fixture' );
    234         test( 'Fixture section exists', function () {
    235                 ok( wp.customize.section.has( 'fixture-section' ) );
     233        QUnit.module( 'Customizer Section in Fixture' );
     234        QUnit.test( 'Fixture section exists', function( assert ) {
     235                assert.ok( wp.customize.section.has( 'fixture-section' ) );
    236236        } );
    237         test( 'Fixture section has control among controls()', function () {
     237        QUnit.test( 'Fixture section has control among controls()', function( assert ) {
    238238                var section = wp.customize.section( 'fixture-section' );
    239                 ok( -1 !== _.pluck( section.controls(), 'id' ).indexOf( 'fixture-control' ) );
     239                assert.ok( -1 !== _.pluck( section.controls(), 'id' ).indexOf( 'fixture-control' ) );
    240240        } );
    241         test( 'Fixture section has has expected panel', function () {
     241        QUnit.test( 'Fixture section has has expected panel', function( assert ) {
    242242                var section = wp.customize.section( 'fixture-section' );
    243                 equal( section.panel(), 'fixture-panel' );
     243                assert.equal( section.panel(), 'fixture-panel' );
    244244        } );
    245245
    246         module( 'Customizer Default Section with Template in Fixture' );
    247         test( 'Fixture section exists', function () {
    248                 ok( wp.customize.section.has( 'fixture-section-default-templated' ) );
     246        QUnit.module( 'Customizer Default Section with Template in Fixture' );
     247        QUnit.test( 'Fixture section exists', function( assert ) {
     248                assert.ok( wp.customize.section.has( 'fixture-section-default-templated' ) );
    249249        } );
    250         test( 'Fixture section has expected content', function () {
     250        QUnit.test( 'Fixture section has expected content', function( assert ) {
    251251                var id = 'fixture-section-default-templated', section;
    252252                section = wp.customize.section( id );
    253                 ok( ! section.params.content );
    254                 ok( !! section.container );
    255                 ok( !! section.headContainer );
    256                 ok( !! section.contentContainer );
    257                 ok( section.container.has( section.headContainer ) );
    258                 ok( section.container.has( section.contentContainer ) );
    259                 ok( section.headContainer.is( '.control-section.control-section-default' ) );
    260                 ok( 1 === section.headContainer.find( '> .accordion-section-title' ).length );
    261                 ok( section.contentContainer.is( '.accordion-section-content' ) );
    262                 equal( section.headContainer.attr( 'aria-owns' ), section.contentContainer.attr( 'id' ) );
     253                assert.ok( ! section.params.content );
     254                assert.ok( !! section.container );
     255                assert.ok( !! section.headContainer );
     256                assert.ok( !! section.contentContainer );
     257                assert.ok( section.container.has( section.headContainer ) );
     258                assert.ok( section.container.has( section.contentContainer ) );
     259                assert.ok( section.headContainer.is( '.control-section.control-section-default' ) );
     260                assert.ok( 1 === section.headContainer.find( '> .accordion-section-title' ).length );
     261                assert.ok( section.contentContainer.is( '.accordion-section-content' ) );
     262                assert.equal( section.headContainer.attr( 'aria-owns' ), section.contentContainer.attr( 'id' ) );
    263263        } );
    264264
    265         module( 'Customizer Custom Type (titleless) Section with Template in Fixture' );
    266         test( 'Fixture section exists', function () {
    267                 ok( wp.customize.section.has( 'fixture-section-titleless-templated' ) );
     265        QUnit.module( 'Customizer Custom Type (titleless) Section with Template in Fixture' );
     266        QUnit.test( 'Fixture section exists', function( assert ) {
     267                assert.ok( wp.customize.section.has( 'fixture-section-titleless-templated' ) );
    268268        } );
    269         test( 'Fixture section has expected content', function () {
     269        QUnit.test( 'Fixture section has expected content', function( assert ) {
    270270                var id = 'fixture-section-titleless-templated', section;
    271271                section = wp.customize.section( id );
    272                 ok( ! section.params.content );
    273                 ok( !! section.container );
    274                 ok( !! section.headContainer );
    275                 ok( !! section.contentContainer );
    276                 ok( section.container.has( section.headContainer ) );
    277                 ok( section.container.has( section.contentContainer ) );
    278                 ok( section.container.is( '.control-section.control-section-titleless' ) );
    279                 ok( 0 === section.headContainer.find( '> .accordion-section-title' ).length );
    280                 ok( section.contentContainer.is( '.accordion-section-content' ) );
    281                 equal( section.headContainer.attr( 'aria-owns' ), section.contentContainer.attr( 'id' ) );
     272                assert.ok( ! section.params.content );
     273                assert.ok( !! section.container );
     274                assert.ok( !! section.headContainer );
     275                assert.ok( !! section.contentContainer );
     276                assert.ok( section.container.has( section.headContainer ) );
     277                assert.ok( section.container.has( section.contentContainer ) );
     278                assert.ok( section.container.is( '.control-section.control-section-titleless' ) );
     279                assert.ok( 0 === section.headContainer.find( '> .accordion-section-title' ).length );
     280                assert.ok( section.contentContainer.is( '.accordion-section-content' ) );
     281                assert.equal( section.headContainer.attr( 'aria-owns' ), section.contentContainer.attr( 'id' ) );
    282282        } );
    283         module( 'Customizer Custom Type Section Lacking Specific Template' );
    284         test( 'Fixture section has expected content', function () {
     283        QUnit.module( 'Customizer Custom Type Section Lacking Specific Template' );
     284        QUnit.test( 'Fixture section has expected content', function( assert ) {
    285285                var id = 'fixture-section-reusing-default-template', section;
    286286                section = wp.customize.section( id );
    287                 ok( ! section.params.content );
    288                 ok( !! section.container );
    289                 ok( !! section.headContainer );
    290                 ok( !! section.contentContainer );
    291                 ok( section.container.has( section.headContainer ) );
    292                 ok( section.container.has( section.contentContainer ) );
    293                 ok( section.headContainer.is( '.control-section.control-section-' + section.params.type ) );
    294                 ok( 1 === section.headContainer.find( '> .accordion-section-title' ).length );
    295                 ok( section.contentContainer.is( '.accordion-section-content' ) );
    296                 equal( section.headContainer.attr( 'aria-owns' ), section.contentContainer.attr( 'id' ) );
     287                assert.ok( ! section.params.content );
     288                assert.ok( !! section.container );
     289                assert.ok( !! section.headContainer );
     290                assert.ok( !! section.contentContainer );
     291                assert.ok( section.container.has( section.headContainer ) );
     292                assert.ok( section.container.has( section.contentContainer ) );
     293                assert.ok( section.headContainer.is( '.control-section.control-section-' + section.params.type ) );
     294                assert.ok( 1 === section.headContainer.find( '> .accordion-section-title' ).length );
     295                assert.ok( section.contentContainer.is( '.accordion-section-content' ) );
     296                assert.equal( section.headContainer.attr( 'aria-owns' ), section.contentContainer.attr( 'id' ) );
    297297        } );
    298         module( 'Customizer Section lacking any params' );
    299         test( 'Fixture section has default params supplied', function () {
     298        QUnit.module( 'Customizer Section lacking any params' );
     299        QUnit.test( 'Fixture section has default params supplied', function( assert ) {
    300300                var id = 'fixture-section-without-params', section, defaultParams;
    301301                section = wp.customize.section( id );
    302302                defaultParams = {
     
    310310                        customizeAction: ''
    311311                };
    312312                jQuery.each( defaultParams, function ( key, value ) {
    313                         ok( 'undefined' !== typeof section.params[ key ] );
    314                         equal( value, section.params[ key ] );
     313                        assert.ok( 'undefined' !== typeof section.params[ key ] );
     314                        assert.equal( value, section.params[ key ] );
    315315                } );
    316                 ok( _.isNumber( section.params.instanceNumber ) );
     316                assert.ok( _.isNumber( section.params.instanceNumber ) );
    317317        } );
    318318
    319319
    320320        // Begin panels.
    321         module( 'Customizer Panel in Fixture' );
    322         test( 'Fixture panel exists', function () {
    323                 ok( wp.customize.panel.has( 'fixture-panel' ) );
     321        QUnit.module( 'Customizer Panel in Fixture' );
     322        QUnit.test( 'Fixture panel exists', function( assert ) {
     323                assert.ok( wp.customize.panel.has( 'fixture-panel' ) );
    324324        } );
    325         test( 'Fixture panel has content', function () {
     325        QUnit.test( 'Fixture panel has content', function( assert ) {
    326326                var panel = wp.customize.panel( 'fixture-panel' );
    327                 ok( !! panel.params.content );
    328                 ok( !! panel.container );
    329                 ok( !! panel.headContainer );
    330                 ok( !! panel.contentContainer );
    331                 ok( panel.container.has( panel.headContainer ) );
    332                 ok( panel.container.has( panel.contentContainer ) );
     327                assert.ok( !! panel.params.content );
     328                assert.ok( !! panel.container );
     329                assert.ok( !! panel.headContainer );
     330                assert.ok( !! panel.contentContainer );
     331                assert.ok( panel.container.has( panel.headContainer ) );
     332                assert.ok( panel.container.has( panel.contentContainer ) );
    333333        } );
    334         test( 'Fixture panel has section among its sections()', function () {
     334        QUnit.test( 'Fixture panel has section among its sections()', function( assert ) {
    335335                var panel = wp.customize.panel( 'fixture-panel' );
    336                 ok( -1 !== _.pluck( panel.sections(), 'id' ).indexOf( 'fixture-section' ) );
     336                assert.ok( -1 !== _.pluck( panel.sections(), 'id' ).indexOf( 'fixture-section' ) );
    337337        } );
    338         test( 'Panel is not expanded by default', function () {
     338        QUnit.test( 'Panel is not expanded by default', function( assert ) {
    339339                var panel = wp.customize.panel( 'fixture-panel' );
    340                 ok( ! panel.expanded() );
     340                assert.ok( ! panel.expanded() );
    341341        } );
    342         test( 'Panel is not expanded by default', function () {
     342        QUnit.test( 'Panel is not expanded by default', function( assert ) {
    343343                var panel = wp.customize.panel( 'fixture-panel' );
    344                 ok( ! panel.expanded() );
     344                assert.ok( ! panel.expanded() );
    345345        } );
    346         test( 'Focusing on a control will expand the panel and section', function () {
     346        QUnit.test( 'Focusing on a control will expand the panel and section', function( assert ) {
    347347                var panel, section, control;
    348348                panel = wp.customize.panel( 'fixture-panel' );
    349349                section = wp.customize.section( 'fixture-section' );
    350350                control = wp.customize.control( 'fixture-control' );
    351                 ok( ! panel.expanded() );
    352                 ok( ! section.expanded() );
     351                assert.ok( ! panel.expanded() );
     352                assert.ok( ! section.expanded() );
    353353                control.focus();
    354                 ok( section.expanded() );
    355                 ok( panel.expanded() );
     354                assert.ok( section.expanded() );
     355                assert.ok( panel.expanded() );
    356356        } );
    357357
    358         module( 'Customizer Default Panel with Template in Fixture' );
    359         test( 'Fixture section exists', function () {
    360                 ok( wp.customize.panel.has( 'fixture-panel-default-templated' ) );
     358        QUnit.module( 'Customizer Default Panel with Template in Fixture' );
     359        QUnit.test( 'Fixture section exists', function( assert ) {
     360                assert.ok( wp.customize.panel.has( 'fixture-panel-default-templated' ) );
    361361        } );
    362         test( 'Fixture panel has expected content', function () {
     362        QUnit.test( 'Fixture panel has expected content', function( assert ) {
    363363                var id = 'fixture-panel-default-templated', panel;
    364364                panel = wp.customize.panel( id );
    365                 ok( ! panel.params.content );
    366                 ok( !! panel.container );
    367                 ok( !! panel.headContainer );
    368                 ok( !! panel.contentContainer );
    369                 ok( panel.container.has( panel.headContainer ) );
    370                 ok( panel.container.has( panel.contentContainer ) );
    371                 ok( panel.headContainer.is( '.control-panel.control-panel-default' ) );
    372                 ok( 1 === panel.headContainer.find( '> .accordion-section-title' ).length );
    373                 ok( panel.contentContainer.is( '.control-panel-content' ) );
    374                 equal( panel.headContainer.attr( 'aria-owns' ), panel.contentContainer.attr( 'id' ) );
     365                assert.ok( ! panel.params.content );
     366                assert.ok( !! panel.container );
     367                assert.ok( !! panel.headContainer );
     368                assert.ok( !! panel.contentContainer );
     369                assert.ok( panel.container.has( panel.headContainer ) );
     370                assert.ok( panel.container.has( panel.contentContainer ) );
     371                assert.ok( panel.headContainer.is( '.control-panel.control-panel-default' ) );
     372                assert.ok( 1 === panel.headContainer.find( '> .accordion-section-title' ).length );
     373                assert.ok( panel.contentContainer.is( '.control-panel-content' ) );
     374                assert.equal( panel.headContainer.attr( 'aria-owns' ), panel.contentContainer.attr( 'id' ) );
    375375        } );
    376376
    377         module( 'Customizer Custom Type Panel (titleless) with Template in Fixture' );
    378         test( 'Fixture panel exists', function () {
    379                 ok( wp.customize.panel.has( 'fixture-panel-titleless-templated' ) );
     377        QUnit.module( 'Customizer Custom Type Panel (titleless) with Template in Fixture' );
     378        QUnit.test( 'Fixture panel exists', function( assert ) {
     379                assert.ok( wp.customize.panel.has( 'fixture-panel-titleless-templated' ) );
    380380        } );
    381         test( 'Fixture panel has expected content', function () {
     381        QUnit.test( 'Fixture panel has expected content', function( assert ) {
    382382                var id = 'fixture-panel-titleless-templated', panel;
    383383                panel = wp.customize.panel( id );
    384                 ok( ! panel.params.content );
    385                 ok( !! panel.container );
    386                 ok( !! panel.headContainer );
    387                 ok( !! panel.contentContainer );
    388                 ok( panel.container.has( panel.headContainer ) );
    389                 ok( panel.container.has( panel.contentContainer ) );
    390                 ok( panel.headContainer.is( '.control-panel.control-panel-titleless' ) );
    391                 ok( 0 === panel.headContainer.find( '> .accordion-section-title' ).length );
    392                 ok( panel.contentContainer.is( '.control-panel-content' ) );
    393                 equal( panel.headContainer.attr( 'aria-owns' ), panel.contentContainer.attr( 'id' ) );
     384                assert.ok( ! panel.params.content );
     385                assert.ok( !! panel.container );
     386                assert.ok( !! panel.headContainer );
     387                assert.ok( !! panel.contentContainer );
     388                assert.ok( panel.container.has( panel.headContainer ) );
     389                assert.ok( panel.container.has( panel.contentContainer ) );
     390                assert.ok( panel.headContainer.is( '.control-panel.control-panel-titleless' ) );
     391                assert.ok( 0 === panel.headContainer.find( '> .accordion-section-title' ).length );
     392                assert.ok( panel.contentContainer.is( '.control-panel-content' ) );
     393                assert.equal( panel.headContainer.attr( 'aria-owns' ), panel.contentContainer.attr( 'id' ) );
    394394        } );
    395395
    396         module( 'Customizer Custom Type Panel Lacking Specific Template' );
    397         test( 'Fixture panel has expected content', function () {
     396        QUnit.module( 'Customizer Custom Type Panel Lacking Specific Template' );
     397        QUnit.test( 'Fixture panel has expected content', function( assert ) {
    398398                var id = 'fixture-panel-reusing-default-template', panel;
    399399                panel = wp.customize.panel( id );
    400                 ok( ! panel.params.content );
    401                 ok( !! panel.container );
    402                 ok( !! panel.headContainer );
    403                 ok( !! panel.contentContainer );
    404                 ok( panel.container.has( panel.headContainer ) );
    405                 ok( panel.container.has( panel.contentContainer ) );
    406                 ok( panel.headContainer.is( '.control-panel.control-panel-' + panel.params.type ) );
    407                 ok( 1 === panel.headContainer.find( '> .accordion-section-title' ).length );
    408                 ok( panel.contentContainer.is( '.control-panel-content' ) );
    409                 equal( panel.headContainer.attr( 'aria-owns' ), panel.contentContainer.attr( 'id' ) );
     400                assert.ok( ! panel.params.content );
     401                assert.ok( !! panel.container );
     402                assert.ok( !! panel.headContainer );
     403                assert.ok( !! panel.contentContainer );
     404                assert.ok( panel.container.has( panel.headContainer ) );
     405                assert.ok( panel.container.has( panel.contentContainer ) );
     406                assert.ok( panel.headContainer.is( '.control-panel.control-panel-' + panel.params.type ) );
     407                assert.ok( 1 === panel.headContainer.find( '> .accordion-section-title' ).length );
     408                assert.ok( panel.contentContainer.is( '.control-panel-content' ) );
     409                assert.equal( panel.headContainer.attr( 'aria-owns' ), panel.contentContainer.attr( 'id' ) );
    410410        } );
    411         module( 'Customizer Panel lacking any params' );
    412         test( 'Fixture panel has default params supplied', function () {
     411        QUnit.module( 'Customizer Panel lacking any params' );
     412        QUnit.test( 'Fixture panel has default params supplied', function( assert ) {
    413413                var id = 'fixture-panel-without-params', panel, defaultParams;
    414414                panel = wp.customize.panel( id );
    415415                defaultParams = {
     
    421421                        active: true
    422422                };
    423423                jQuery.each( defaultParams, function ( key, value ) {
    424                         ok( 'undefined' !== typeof panel.params[ key ] );
    425                         equal( value, panel.params[ key ] );
     424                        assert.ok( 'undefined' !== typeof panel.params[ key ] );
     425                        assert.equal( value, panel.params[ key ] );
    426426                } );
    427                 ok( _.isNumber( panel.params.instanceNumber ) );
     427                assert.ok( _.isNumber( panel.params.instanceNumber ) );
    428428        } );
    429429
    430         module( 'Dynamically-created Customizer Setting Model' );
     430        QUnit.module( 'Dynamically-created Customizer Setting Model' );
    431431        settingId = 'new_blogname';
    432432        settingValue = 'Hello World';
    433433
    434         test( 'Create a new setting', function () {
     434        QUnit.test( 'Create a new setting', function( assert ) {
    435435                mockSetting = wp.customize.create(
    436436                        settingId,
    437437                        settingId,
     
    441441                                previewer: wp.customize.previewer
    442442                        }
    443443                );
    444                 equal( mockSetting(), settingValue );
    445                 equal( mockSetting.id, settingId );
     444                assert.equal( mockSetting(), settingValue );
     445                assert.equal( mockSetting.id, settingId );
    446446        } );
    447447
    448         module( 'Dynamically-created Customizer Section Model' );
     448        QUnit.module( 'Dynamically-created Customizer Section Model' );
    449449
    450450        sectionId = 'mock_title_tagline';
    451451        sectionContent = '<li id="accordion-section-mock_title_tagline" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>';
     
    467467
    468468        testCustomizerModel( mockSection, sectionExpectedValues );
    469469
    470         test( 'Section has been embedded', function () {
    471                 equal( mockSection.deferred.embedded.state(), 'resolved' );
     470        QUnit.test( 'Section has been embedded', function( assert ) {
     471                assert.equal( mockSection.deferred.embedded.state(), 'resolved' );
    472472        } );
    473473
    474474        wp.customize.section.add( sectionId, mockSection );
    475475
    476         test( 'Section instance added to the wp.customize.section object', function () {
    477                 ok( wp.customize.section.has( sectionId ) );
     476        QUnit.test( 'Section instance added to the wp.customize.section object', function( assert ) {
     477                assert.ok( wp.customize.section.has( sectionId ) );
    478478        });
    479479
    480480        sectionInstance = wp.customize.section( sectionId );
    481481
    482         test( 'Section instance has right content when accessed from wp.customize.section()', function () {
    483                 equal( sectionInstance.params.content, sectionContent );
     482        QUnit.test( 'Section instance has right content when accessed from wp.customize.section()', function( assert ) {
     483                assert.equal( sectionInstance.params.content, sectionContent );
    484484        });
    485485
    486         test( 'Section instance has no children yet', function () {
    487                 equal( sectionInstance.controls().length, 0 );
     486        QUnit.test( 'Section instance has no children yet', function( assert ) {
     487                assert.equal( sectionInstance.controls().length, 0 );
    488488        });
    489489
    490         module( 'Dynamically-created Customizer Control Model' );
     490        QUnit.module( 'Dynamically-created Customizer Control Model' );
    491491
    492492        controlId = 'new_blogname';
    493493        controlLabel = 'Site Title';
     
    520520
    521521        testCustomizerModel( mockControl, controlExpectedValues );
    522522
    523         test( 'Control instance does not yet belong to a section.', function () {
    524                 equal( mockControl.section(), undefined );
     523        QUnit.test( 'Control instance does not yet belong to a section.', function( assert ) {
     524                assert.equal( mockControl.section(), undefined );
    525525        });
    526         test( 'Control has not been embedded yet', function () {
    527                 equal( mockControl.deferred.embedded.state(), 'pending' );
     526        QUnit.test( 'Control has not been embedded yet', function( assert ) {
     527                assert.equal( mockControl.deferred.embedded.state(), 'pending' );
    528528        } );
    529529
    530         test( 'Control instance has the right selector.', function () {
    531                 equal( mockControl.selector, '#customize-control-new_blogname' );
     530        QUnit.test( 'Control instance has the right selector.', function( assert ) {
     531                assert.equal( mockControl.selector, '#customize-control-new_blogname' );
    532532        });
    533533
    534534        wp.customize.control.add( controlId, mockControl );
    535535
    536         test( 'Control instance was added to the control class.', function () {
    537                 ok( wp.customize.control.has( controlId ) );
     536        QUnit.test( 'Control instance was added to the control class.', function( assert ) {
     537                assert.ok( wp.customize.control.has( controlId ) );
    538538        });
    539539
    540540        mockControlInstance = wp.customize.control( controlId );
    541541
    542         test( 'Control instance has the right id when accessed from api.control().', function () {
    543                 equal( mockControlInstance.id, controlId );
     542        QUnit.test( 'Control instance has the right id when accessed from api.control().', function( assert ) {
     543                assert.equal( mockControlInstance.id, controlId );
    544544        });
    545545
    546         test( 'Control section can be set as expected', function () {
     546        QUnit.test( 'Control section can be set as expected', function( assert ) {
    547547                mockControl.section( mockSection.id );
    548                 equal( mockControl.section(), mockSection.id );
     548                assert.equal( mockControl.section(), mockSection.id );
    549549        });
    550         test( 'Associating a control with a section allows it to be embedded', function () {
    551                 equal( mockControl.deferred.embedded.state(), 'resolved' );
     550        QUnit.test( 'Associating a control with a section allows it to be embedded', function( assert ) {
     551                assert.equal( mockControl.deferred.embedded.state(), 'resolved' );
    552552        });
    553553
    554         test( 'Control is now available on section.controls()', function () {
    555                 equal( sectionInstance.controls().length, 1 );
    556                 equal( sectionInstance.controls()[0], mockControl );
     554        QUnit.test( 'Control is now available on section.controls()', function( assert ) {
     555                assert.equal( sectionInstance.controls().length, 1 );
     556                assert.equal( sectionInstance.controls()[0], mockControl );
    557557        });
    558558
    559         module( 'Dynamically-created Customizer Panel Model' );
     559        QUnit.module( 'Dynamically-created Customizer Panel Model' );
    560560
    561561        panelId = 'mockPanelId';
    562562        panelTitle = 'Mock Panel Title';
     
    584584
    585585        testCustomizerModel( mockPanel, panelExpectedValues );
    586586
    587         test( 'Panel instance is not contextuallyActive', function () {
    588                 equal( mockPanel.isContextuallyActive(), false );
     587        QUnit.test( 'Panel instance is not contextuallyActive', function( assert ) {
     588                assert.equal( mockPanel.isContextuallyActive(), false );
    589589        });
    590590
    591         module( 'Test wp.customize.findControlsForSettings' );
    592         test( 'findControlsForSettings(blogname)', function() {
     591        QUnit.module( 'Test wp.customize.findControlsForSettings' );
     592        QUnit.test( 'findControlsForSettings(blogname)', function( assert ) {
    593593                var controlsForSettings, settingId = 'fixture-setting', controlId = 'fixture-control';
    594                 ok( wp.customize.control.has( controlId ) );
    595                 ok( wp.customize.has( settingId ) );
     594                assert.ok( wp.customize.control.has( controlId ) );
     595                assert.ok( wp.customize.has( settingId ) );
    596596                controlsForSettings = wp.customize.findControlsForSettings( [ settingId ] );
    597                 ok( _.isObject( controlsForSettings ), 'Response is object' );
    598                 ok( _.isArray( controlsForSettings['fixture-setting'] ), 'Response has a fixture-setting array' );
    599                 equal( 1, controlsForSettings['fixture-setting'].length );
    600                 equal( wp.customize.control( controlId ), controlsForSettings['fixture-setting'][0] );
     597                assert.ok( _.isObject( controlsForSettings ), 'Response is object' );
     598                assert.ok( _.isArray( controlsForSettings['fixture-setting'] ), 'Response has a fixture-setting array' );
     599                assert.equal( 1, controlsForSettings['fixture-setting'].length );
     600                assert.equal( wp.customize.control( controlId ), controlsForSettings['fixture-setting'][0] );
    601601        } );
    602602
    603         module( 'Customize Controls wp.customize.dirtyValues' );
    604         test( 'dirtyValues() returns expected values', function() {
     603        QUnit.module( 'Customize Controls wp.customize.dirtyValues' );
     604        QUnit.test( 'dirtyValues() returns expected values', function( assert ) {
    605605                wp.customize.state( 'changesetStatus' ).set( 'auto-draft' );
    606606                wp.customize.each( function( setting ) {
    607607                        setting._dirty = false;
    608608                } );
    609                 ok( _.isEmpty( wp.customize.dirtyValues() ) );
    610                 ok( _.isEmpty( wp.customize.dirtyValues( { unsaved: false } ) ) );
     609                assert.ok( _.isEmpty( wp.customize.dirtyValues() ) );
     610                assert.ok( _.isEmpty( wp.customize.dirtyValues( { unsaved: false } ) ) );
    611611
    612612                wp.customize( 'fixture-setting' )._dirty = true;
    613                 ok( ! _.isEmpty( wp.customize.dirtyValues() ) );
    614                 ok( _.isEmpty( wp.customize.dirtyValues( { unsaved: true } ) ) );
     613                assert.ok( ! _.isEmpty( wp.customize.dirtyValues() ) );
     614                assert.ok( _.isEmpty( wp.customize.dirtyValues( { unsaved: true } ) ) );
    615615
    616616                wp.customize( 'fixture-setting' ).set( 'Modified' );
    617                 ok( ! _.isEmpty( wp.customize.dirtyValues() ) );
    618                 ok( ! _.isEmpty( wp.customize.dirtyValues( { unsaved: true } ) ) );
    619                 equal( 'Modified', wp.customize.dirtyValues()['fixture-setting'] );
     617                assert.ok( ! _.isEmpty( wp.customize.dirtyValues() ) );
     618                assert.ok( ! _.isEmpty( wp.customize.dirtyValues( { unsaved: true } ) ) );
     619                assert.equal( 'Modified', wp.customize.dirtyValues()['fixture-setting'] );
    620620
    621621                // When the changeset does not exist, all dirty settings are necessarily unsaved.
    622622                wp.customize.state( 'changesetStatus' ).set( '' );
    623623                wp.customize( 'fixture-setting' )._dirty = true;
    624                 ok( ! _.isEmpty( wp.customize.dirtyValues() ) );
    625                 ok( ! _.isEmpty( wp.customize.dirtyValues( { unsaved: true } ) ) );
     624                assert.ok( ! _.isEmpty( wp.customize.dirtyValues() ) );
     625                assert.ok( ! _.isEmpty( wp.customize.dirtyValues( { unsaved: true } ) ) );
    626626        } );
    627627
    628         module( 'Customize Controls: wp.customize.requestChangesetUpdate()' );
    629         test( 'requestChangesetUpdate makes request and returns promise', function( assert ) {
     628        QUnit.module( 'Customize Controls: wp.customize.requestChangesetUpdate()' );
     629        QUnit.test( 'requestChangesetUpdate makes request and returns promise', function( assert ) {
    630630                var request, originalBeforeSetup = jQuery.ajaxSettings.beforeSend;
    631631
    632632                jQuery.ajaxSetup( {
     
    677677                } );
    678678        } );
    679679
    680         module( 'Customize Utils: wp.customize.utils.getRemainingTime()' );
    681         test( 'utils.getRemainingTime calculates time correctly', function( assert ) {
     680        QUnit.module( 'Customize Utils: wp.customize.utils.getRemainingTime()' );
     681        QUnit.test( 'utils.getRemainingTime calculates time correctly', function( assert ) {
    682682                var datetime = '2599-08-06 12:12:13', timeRemaining, timeRemainingWithDateInstance, timeRemaingingWithTimestamp;
    683683
    684684                timeRemaining = wp.customize.utils.getRemainingTime( datetime );
     
    692692                assert.deepEqual( timeRemaining, timeRemaingingWithTimestamp );
    693693        });
    694694
    695         module( 'Customize Utils: wp.customize.utils.getCurrentTimestamp()' );
    696         test( 'utils.getCurrentTimestamp returns timestamp', function( assert ) {
     695        QUnit.module( 'Customize Utils: wp.customize.utils.getCurrentTimestamp()' );
     696        QUnit.test( 'utils.getCurrentTimestamp returns timestamp', function( assert ) {
    697697                var currentTimeStamp;
    698698                currentTimeStamp = wp.customize.utils.getCurrentTimestamp();
    699699                assert.equal( typeof currentTimeStamp, 'number' );
    700700        });
    701701
    702         module( 'Customize Controls: wp.customize.DateTimeControl' );
    703         test( 'Test DateTimeControl creation and its methods', function( assert ) {
     702        QUnit.module( 'Customize Controls: wp.customize.DateTimeControl' );
     703        QUnit.test( 'Test DateTimeControl creation and its methods', function( assert ) {
    704704                var control, controlId = 'date_time', section, sectionId = 'fixture-section',
    705705                        datetime = '2599-08-06 18:12:13', dateTimeArray, dateTimeArrayInampm, timeString,
    706706                        day, year, month, minute, meridian, hour;
     
    864864                wp.customize.control.remove( controlId );
    865865        });
    866866
    867         module( 'Customize Sections: wp.customize.OuterSection' );
    868         test( 'Test OuterSection', function( assert ) {
     867        QUnit.module( 'Customize Sections: wp.customize.OuterSection' );
     868        QUnit.test( 'Test OuterSection', function( assert ) {
    869869                var section, sectionId = 'test_outer_section', body = jQuery( 'body' ),
    870870                        defaultSection, defaultSectionId = 'fixture-section';
    871871
     
    899899                wp.customize.section.remove( sectionId );
    900900        });
    901901
    902         module( 'Customize Controls: PreviewLinkControl' );
    903         test( 'Test PreviewLinkControl creation and its methods', function( assert ) {
     902        QUnit.module( 'Customize Controls: PreviewLinkControl' );
     903        QUnit.test( 'Test PreviewLinkControl creation and its methods', function( assert ) {
    904904                var section, sectionId = 'publish_settings', newLink;
    905905
    906906                section = wp.customize.section( sectionId );
  • tests/qunit/wp-admin/js/customize-header.js

     
    11/* global wp, sinon */
    22
    33jQuery( function() {
    4         module('Custom Header: ChoiceList', {
    5                 setup: function() {
     4        QUnit.module('Custom Header: ChoiceList', {
     5                beforeEach: function() {
    66                        wp.customize.HeaderTool.currentHeader = new wp.customize.HeaderTool.ImageModel();
    77                        this.apiStub = sinon.stub(wp.customize, 'get').returns('foo');
    88                        this.choiceList = new wp.customize.HeaderTool.ChoiceList();
    99                },
    10                 teardown: function() {
     10                afterEach: function() {
    1111                        this.apiStub.restore();
    1212                }
    1313        });
    1414
    15         test('should parse _wpCustomizeHeader.uploads into itself', function() {
    16                 equal(this.choiceList.length, 4);
     15        QUnit.test('should parse _wpCustomizeHeader.uploads into itself', function( assert ) {
     16                assert.equal(this.choiceList.length, 4);
    1717        });
    1818
    19         test('should sort by newest first', function() {
    20                 equal(this.choiceList.at(2).get('header').attachment_id, 1);
    21                 equal(this.choiceList.first().get('header').attachment_id, 3);
     19        QUnit.test('should sort by newest first', function( assert ) {
     20                assert.equal(this.choiceList.at(2).get('header').attachment_id, 1);
     21                assert.equal(this.choiceList.first().get('header').attachment_id, 3);
    2222        });
    2323
    24         module('Custom Header: DefaultsList', {
    25                 setup: function() {
     24        QUnit.module('Custom Header: DefaultsList', {
     25                beforeEach: function() {
    2626                        wp.customize.HeaderTool.currentHeader = new wp.customize.HeaderTool.ImageModel();
    2727                        this.apiStub = sinon.stub(wp.customize, 'get').returns('foo');
    2828                        this.choiceList = new wp.customize.HeaderTool.DefaultsList();
    2929                },
    30                 teardown: function() {
     30                afterEach: function() {
    3131                        this.apiStub.restore();
    3232                }
    3333        });
    3434
    35         test('it should parse _wpCustomizeHeader.defaults into itself', function() {
    36                 equal(this.choiceList.length, 4);
     35        QUnit.test('it should parse _wpCustomizeHeader.defaults into itself', function( assert ) {
     36                assert.equal(this.choiceList.length, 4);
    3737        });
    3838
    39         test('it parses the default image names', function() {
    40                 equal(this.choiceList.first().get('header').defaultName, 'circle');
    41                 equal(this.choiceList.at(2).get('header').defaultName, 'star');
     39        QUnit.test('it parses the default image names', function( assert ) {
     40                assert.equal(this.choiceList.first().get('header').defaultName, 'circle');
     41                assert.equal(this.choiceList.at(2).get('header').defaultName, 'star');
    4242        });
    4343
    44         module('Custom Header: HeaderImage shouldBeCropped()', {
    45                 setup: function() {
     44        QUnit.module('Custom Header: HeaderImage shouldBeCropped()', {
     45                beforeEach: function() {
    4646                        wp.customize.HeaderTool.currentHeader = new wp.customize.HeaderTool.ImageModel();
    4747                        this.model = new wp.customize.HeaderTool.ImageModel();
    4848                        this.model.set({
     
    5252                }
    5353        });
    5454
    55         test('should not be cropped when the theme does not support flex width or height and the image has the same dimensions of the theme image', function() {
     55        QUnit.test('should not be cropped when the theme does not support flex width or height and the image has the same dimensions of the theme image', function( assert ) {
    5656                this.model.set({
    5757                        themeFlexWidth: false,
    5858                        themeFlexHeight: false,
     
    6060                        imageHeight: 200
    6161                });
    6262
    63                 equal(this.model.shouldBeCropped(), false);
     63                assert.equal(this.model.shouldBeCropped(), false);
    6464        });
    6565
    66         test('should be cropped when the image has the same dimensions of the theme image', function() {
     66        QUnit.test('should be cropped when the image has the same dimensions of the theme image', function( assert ) {
    6767                this.model.set({
    6868                        themeFlexWidth: false,
    6969                        themeFlexHeight: false,
     
    7171                        imageHeight: 400
    7272                });
    7373
    74                 equal(this.model.shouldBeCropped(), true);
     74                assert.equal(this.model.shouldBeCropped(), true);
    7575        });
    7676
    77         test('should not be cropped when the theme only supports flex width and the image has the same height as the theme image', function() {
     77        QUnit.test('should not be cropped when the theme only supports flex width and the image has the same height as the theme image', function( assert ) {
    7878                this.model.set({
    7979                        themeFlexWidth: true,
    8080                        themeFlexHeight: false,
     
    8282                        imageHeight: 200
    8383                });
    8484
    85                 equal(this.model.shouldBeCropped(), false);
     85                assert.equal(this.model.shouldBeCropped(), false);
    8686        });
    8787
    88         test('should not be cropped when the theme only supports flex height and the image has the same width as the theme image', function() {
     88        QUnit.test('should not be cropped when the theme only supports flex height and the image has the same width as the theme image', function( assert ) {
    8989                this.model.set({
    9090                        themeFlexWidth: false,
    9191                        themeFlexHeight: true,
     
    9393                        imageHeight: 600
    9494                });
    9595
    96                 equal(this.model.shouldBeCropped(), false);
     96                assert.equal(this.model.shouldBeCropped(), false);
    9797        });
    9898
    99         test('should not be cropped when the theme supports flex height AND width', function() {
     99        QUnit.test('should not be cropped when the theme supports flex height AND width', function( assert ) {
    100100                this.model.set({
    101101                        themeFlexWidth: true,
    102102                        themeFlexHeight: true,
     
    104104                        imageHeight: 8600
    105105                });
    106106
    107                 equal(this.model.shouldBeCropped(), false);
     107                assert.equal(this.model.shouldBeCropped(), false);
    108108        });
    109109
    110         test('should not be cropped when the image width is smaller than or equal to theme width', function() {
     110        QUnit.test('should not be cropped when the image width is smaller than or equal to theme width', function( assert ) {
    111111                this.model.set({
    112112                        themeFlexWidth: false,
    113113                        themeFlexHeight: false,
     
    115115                        imageHeight: 100
    116116                });
    117117
    118                 equal(this.model.shouldBeCropped(), false);
     118                assert.equal(this.model.shouldBeCropped(), false);
    119119        });
    120120
    121         test('should not be cropped when the image width is smaller than or equal to theme width, theme supports flex height and width', function() {
     121        QUnit.test('should not be cropped when the image width is smaller than or equal to theme width, theme supports flex height and width', function( assert ) {
    122122                this.model.set({
    123123                        themeFlexWidth: true,
    124124                        themeFlexHeight: true,
     
    126126                        imageHeight: 100
    127127                });
    128128
    129                 equal(this.model.shouldBeCropped(), false);
     129                assert.equal(this.model.shouldBeCropped(), false);
    130130        });
    131131});
  • tests/qunit/wp-admin/js/customize-nav-menus.js

     
    11/* global wp */
    2 jQuery( window ).load( function (){
     2jQuery( window ).load( function() {
    33
    44        var api = wp.customize,
    55                primaryMenuId = 3,
    66                socialMenuId = 2;
    77
    8         module( 'Customize Nav Menus' );
     8        QUnit.module( 'Customize Nav Menus' );
    99
    1010        /**
    1111         * Generate 20 IDs and verify they are all unique.
    1212         */
    13         test( 'generatePlaceholderAutoIncrementId generates unique IDs', function() {
     13        QUnit.test( 'generatePlaceholderAutoIncrementId generates unique IDs', function( assert ) {
    1414                var testIterations = 20,
    1515                        ids = [ api.Menus.generatePlaceholderAutoIncrementId() ];
    1616
     
    1717                while ( testIterations ) {
    1818                        var placeholderID = api.Menus.generatePlaceholderAutoIncrementId();
    1919
    20                         ok( -1 === ids.indexOf( placeholderID ) );
     20                        assert.ok( -1 === ids.indexOf( placeholderID ) );
    2121                        ids.push( placeholderID );
    2222                        testIterations -= 1;
    2323                }
     
    2424
    2525        } );
    2626
    27         test( 'it should parse _wpCustomizeMenusSettings.defaults into itself', function() {
    28                 deepEqual( window._wpCustomizeNavMenusSettings, api.Menus.data );
     27        QUnit.test( 'it should parse _wpCustomizeMenusSettings.defaults into itself', function( assert ) {
     28                assert.deepEqual( window._wpCustomizeNavMenusSettings, api.Menus.data );
    2929        } );
    3030
    31         test( 'empty menus should have no Menu Item Controls', function() {
    32                 ok( 0 === wp.customize.Menus.getMenuControl( socialMenuId ).getMenuItemControls().length, 'empty menus' );
     31        QUnit.test( 'empty menus should have no Menu Item Controls', function( assert ) {
     32                assert.ok( 0 === wp.customize.Menus.getMenuControl( socialMenuId ).getMenuItemControls().length, 'empty menus' );
    3333        } );
    3434
    35         test( 'populated menus should have no Menu Item Controls', function() {
    36                 ok( 0 !== wp.customize.Menus.getMenuControl( primaryMenuId ).getMenuItemControls().length, 'non-empty menus' );
     35        QUnit.test( 'populated menus should have no Menu Item Controls', function( assert ) {
     36                assert.ok( 0 !== wp.customize.Menus.getMenuControl( primaryMenuId ).getMenuItemControls().length, 'non-empty menus' );
    3737        } );
    3838
    3939        // @todo Add tests for api.Menus.AvailableMenuItemsPanelView
    4040        // (and api.Menus.AvailableItemCollection, api.Menus.AvailableItemModel).
    4141
    42         test( 'there is a properly configured MenusPanel', function() {
     42        QUnit.test( 'there is a properly configured MenusPanel', function( assert ) {
    4343                var panel, sections;
    4444
    4545                panel = api.panel( 'nav_menus' );
    46                 ok( panel );
    47                 ok( panel.extended( api.Menus.MenusPanel ) );
     46                assert.ok( panel );
     47                assert.ok( panel.extended( api.Menus.MenusPanel ) );
    4848
    4949                sections = panel.sections();
    50                 ok( 'menu_locations' === sections[0].id, 'first section is menu_locations' );
    51                 ok( sections[1].extended( api.Menus.MenuSection ), 'second section is MenuSection' );
    52                 ok( sections[ sections.length - 1 ].extended( api.Menus.NewMenuSection ), 'last section is NewMenuSection' );
     50                assert.ok( 'menu_locations' === sections[0].id, 'first section is menu_locations' );
     51                assert.ok( sections[1].extended( api.Menus.MenuSection ), 'second section is MenuSection' );
     52                assert.ok( sections[ sections.length - 1 ].extended( api.Menus.NewMenuSection ), 'last section is NewMenuSection' );
    5353        } );
    5454        // @todo Add more tests for api.Menus.MenusPanel behaviors.
    5555
    56         test( 'there an expected MenuSection for the primary menu', function() {
     56        QUnit.test( 'there an expected MenuSection for the primary menu', function( assert ) {
    5757                var section, controls, lastControl;
    5858
    5959                section = api.section( 'nav_menu[' + primaryMenuId + ']' );
    60                 ok( section, 'section exists' );
    61                 ok( section.extended( api.Menus.MenuSection ), 'section is a api.Menus.MenuSection' );
    62                 ok( section.deferred.initSortables, 'has section.deferred.initSortables' );
    63                 ok( section.active(), 'section active() is true' );
    64                 ok( section.active.set( false ).get(), 'section active() cannot be set false' );
     60                assert.ok( section, 'section exists' );
     61                assert.ok( section.extended( api.Menus.MenuSection ), 'section is a api.Menus.MenuSection' );
     62                assert.ok( section.deferred.initSortables, 'has section.deferred.initSortables' );
     63                assert.ok( section.active(), 'section active() is true' );
     64                assert.ok( section.active.set( false ).get(), 'section active() cannot be set false' );
    6565
    6666                controls = section.controls();
    67                 ok( controls[0].extended( api.Menus.MenuNameControl ), 'first control in menu section is MenuNameControl' );
    68                 ok( controls[1].extended( api.Menus.MenuItemControl ), 'second control in menu section is MenuItemControl' );
     67                assert.ok( controls[0].extended( api.Menus.MenuNameControl ), 'first control in menu section is MenuNameControl' );
     68                assert.ok( controls[1].extended( api.Menus.MenuItemControl ), 'second control in menu section is MenuItemControl' );
    6969
    7070                lastControl = controls[ controls.length - 1 ];
    71                 ok( lastControl.extended( api.Control ), 'last control in menu section is a base Control' );
    72                 ok( lastControl.params.templateId === 'nav-menu-delete-button', 'last control in menu section has a delete-button template' );
     71                assert.ok( lastControl.extended( api.Control ), 'last control in menu section is a base Control' );
     72                assert.ok( lastControl.params.templateId === 'nav-menu-delete-button', 'last control in menu section has a delete-button template' );
    7373        } );
    7474        // @todo Add more tests for api.Menus.MenuSection behaviors.
    7575
    76         test( 'changing a MenuNameControl change the corresponding menu value', function() {
     76        QUnit.test( 'changing a MenuNameControl change the corresponding menu value', function( assert ) {
    7777                var section, control;
    7878
    7979                section = api.section( 'nav_menu[' + primaryMenuId + ']' );
    8080                control = section.controls()[0];
    81                 ok( control.extended( api.Menus.MenuNameControl ), 'control is a MenuNameControl' );
    82                 equal( control.setting().name, 'Primary menu' );
    83                 ok( ! control.setting._dirty );
     81                assert.ok( control.extended( api.Menus.MenuNameControl ), 'control is a MenuNameControl' );
     82                assert.equal( control.setting().name, 'Primary menu' );
     83                assert.ok( ! control.setting._dirty );
    8484                control.container.find( 'input[type=text]:first' ).val( 'Main menu' ).trigger( 'change' );
    85                 equal( control.setting().name, 'Main menu' );
    86                 ok( control.setting._dirty );
     85                assert.equal( control.setting().name, 'Main menu' );
     86                assert.ok( control.setting._dirty );
    8787        } );
    8888        // @todo Add more tests for api.Menus.MenuNameControl
    8989
    90         test( 'manipulating a MenuItemControl works', function() {
     90        QUnit.test( 'manipulating a MenuItemControl works', function( assert ) {
    9191                var section, control, value;
    9292                section = api.section( 'nav_menu[' + primaryMenuId + ']' );
    93                 ok( section );
     93                assert.ok( section );
    9494
    9595                control = section.controls()[1];
    96                 ok( control.extended( api.Menus.MenuItemControl ), 'control is a MenuItemControl' );
     96                assert.ok( control.extended( api.Menus.MenuItemControl ), 'control is a MenuItemControl' );
    9797
    9898                control.actuallyEmbed();
    9999
    100100                control.container.find( '.edit-menu-item-title' ).val( 'Hello World' ).trigger( 'change' );
    101                 equal( control.setting().title, 'Hello World' );
     101                assert.equal( control.setting().title, 'Hello World' );
    102102                value = _.clone( control.setting() );
    103103                value.title = 'Hola Mundo';
    104                 equal( control.container.find( '.edit-menu-item-title' ).val(), 'Hello World' );
    105                 equal( value.position, 1 );
    106                 equal( control.priority(), 1 );
     104                assert.equal( control.container.find( '.edit-menu-item-title' ).val(), 'Hello World' );
     105                assert.equal( value.position, 1 );
     106                assert.equal( control.priority(), 1 );
    107107
    108108                // @todo Test control.moveDown().
    109109        } );
     
    118118        // @todo Add tests for api.Menus.focusMenuItemControl.
    119119        // @todo Add tests for api.Menus.createNavMenu.
    120120
    121         test( 'api.Menus.getMenuControl() should return the expected control', function() {
     121        QUnit.test( 'api.Menus.getMenuControl() should return the expected control', function( assert ) {
    122122                var control = api.Menus.getMenuControl( primaryMenuId );
    123                 ok( !! control, 'control is returned' );
    124                 ok( control.extended( api.Menus.MenuControl ), 'control is a MenuControl' );
     123                assert.ok( !! control, 'control is returned' );
     124                assert.ok( control.extended( api.Menus.MenuControl ), 'control is a MenuControl' );
    125125        } );
    126126
    127         test( 'api.Menus.getMenuItemControl() should return the expected control', function() {
     127        QUnit.test( 'api.Menus.getMenuItemControl() should return the expected control', function( assert ) {
    128128                var control = api.Menus.getMenuItemControl( 2000 );
    129                 ok( !! control, 'control is returned' );
    130                 ok( control.extended( api.Menus.MenuItemControl ), 'control is a MenuItemControl' );
     129                assert.ok( !! control, 'control is returned' );
     130                assert.ok( control.extended( api.Menus.MenuItemControl ), 'control is a MenuItemControl' );
    131131        } );
    132132
    133133} );
  • tests/qunit/wp-admin/js/customize-widgets.js

     
    33
    44        var api = wp.customize, $ = jQuery;
    55
    6         module( 'Customize Widgets' );
     6        QUnit.module( 'Customize Widgets' );
    77
    8         test( 'fixtures should be present', function() {
     8        QUnit.test( 'fixtures should be present', function( assert ) {
    99                var widgetControl;
    10                 ok( api.panel( 'widgets' ) );
    11                 ok( api.section( 'sidebar-widgets-sidebar-1' ) );
     10                assert.ok( api.panel( 'widgets' ) );
     11                assert.ok( api.section( 'sidebar-widgets-sidebar-1' ) );
    1212                widgetControl = api.control( 'widget_search[2]' );
    13                 ok( widgetControl );
    14                 ok( api.control( 'sidebars_widgets[sidebar-1]' ) );
    15                 ok( api( 'widget_search[2]' ) );
    16                 ok( api( 'sidebars_widgets[sidebar-1]' ) );
    17                 ok( widgetControl.params.content );
    18                 ok( widgetControl.params.widget_control );
    19                 ok( widgetControl.params.widget_content );
    20                 ok( widgetControl.params.widget_id );
    21                 ok( widgetControl.params.widget_id_base );
     13                assert.ok( widgetControl );
     14                assert.ok( api.control( 'sidebars_widgets[sidebar-1]' ) );
     15                assert.ok( api( 'widget_search[2]' ) );
     16                assert.ok( api( 'sidebars_widgets[sidebar-1]' ) );
     17                assert.ok( widgetControl.params.content );
     18                assert.ok( widgetControl.params.widget_control );
     19                assert.ok( widgetControl.params.widget_content );
     20                assert.ok( widgetControl.params.widget_id );
     21                assert.ok( widgetControl.params.widget_id_base );
    2222        });
    2323
    24         test( 'widget contents should embed (with widget-added event) when section and control expand', function() {
     24        QUnit.test( 'widget contents should embed (with widget-added event) when section and control expand', function( assert ) {
    2525                var control, section, widgetAddedEvent = null, widgetControlRootElement = null;
    2626                control = api.control( 'widget_search[2]' );
    2727                section = api.section( 'sidebar-widgets-sidebar-1' );
     
    3131                        widgetControlRootElement = widgetElement;
    3232                });
    3333
    34                 ok( ! section.expanded() );
    35                 ok( 0 === control.container.find( '> .widget' ).length );
     34                assert.ok( ! section.expanded() );
     35                assert.ok( 0 === control.container.find( '> .widget' ).length );
    3636
    3737                // Preview sets the active state.
    3838                section.active.set( true );
     
    4040                api.control( 'sidebars_widgets[sidebar-1]' ).active.set( true );
    4141
    4242                section.expand();
    43                 ok( ! widgetAddedEvent, 'expected widget added event not fired' );
    44                 ok( 1 === control.container.find( '> .widget' ).length, 'expected there to be one .widget element in the container' );
    45                 ok( 0 === control.container.find( '.widget-content' ).children().length );
     43                assert.ok( ! widgetAddedEvent, 'expected widget added event not fired' );
     44                assert.ok( 1 === control.container.find( '> .widget' ).length, 'expected there to be one .widget element in the container' );
     45                assert.ok( 0 === control.container.find( '.widget-content' ).children().length );
    4646
    4747                control.expand();
    48                 ok( 1 === control.container.find( '.widget-content' ).children().length );
    49                 ok( widgetAddedEvent );
    50                 ok( widgetControlRootElement.is( control.container.find( '> .widget' ) ) );
    51                 ok( 1 === control.container.find( '.widget-content #widget-search-2-title' ).length );
     48                assert.ok( 1 === control.container.find( '.widget-content' ).children().length );
     49                assert.ok( widgetAddedEvent );
     50                assert.ok( widgetControlRootElement.is( control.container.find( '> .widget' ) ) );
     51                assert.ok( 1 === control.container.find( '.widget-content #widget-search-2-title' ).length );
    5252
    5353                $( document ).off( 'widget-added' );
    5454        });
    5555
    56         test( 'widgets panel should have notice', function() {
     56        QUnit.test( 'widgets panel should have notice', function( assert ) {
    5757                var panel = api.panel( 'widgets' );
    58                 ok( panel.extended( api.Widgets.WidgetsPanel ) );
     58                assert.ok( panel.extended( api.Widgets.WidgetsPanel ) );
    5959
    6060                panel.deferred.embedded.done( function() {
    61                         ok( 1 === panel.contentContainer.find( '.no-widget-areas-rendered-notice' ).length );
    62                         ok( panel.contentContainer.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
     61                        assert.ok( 1 === panel.contentContainer.find( '.no-widget-areas-rendered-notice' ).length );
     62                        assert.ok( panel.contentContainer.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
    6363                        api.section( 'sidebar-widgets-sidebar-1' ).active( true );
    6464                        api.control( 'sidebars_widgets[sidebar-1]' ).active( true );
    6565                        api.trigger( 'pane-contents-reflowed' );
    66                         ok( ! panel.contentContainer.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
     66                        assert.ok( ! panel.contentContainer.find( '.no-widget-areas-rendered-notice' ).is( ':visible' ) );
    6767                } );
    6868
    69                 expect( 4 );
     69                assert.expect( 4 );
    7070        });
    7171});
  • tests/qunit/wp-admin/js/nav-menu.js

     
    66                eventsFired = 0;
    77
    88        // Fail if we don't see the expected number of events triggered in 3 seconds.
    9         setTimeout( function() {
     9        setTimeout( function( assert ) {
    1010                // QUnit may load this file without running it, in which case `assert`
    1111                // will never be set to `assertPassed` below.
    1212                assert && assert.equal(
  • tests/qunit/wp-admin/js/password-strength-meter.js

     
    11/* global passwordStrength, wp, jQuery */
    22jQuery( function() {
    3         module( 'password-strength-meter' );
     3        QUnit.module( 'password-strength-meter' );
    44
    5         test( 'mismatched passwords should return 5', function() {
    6                 equal( passwordStrength( 'password1', 'username', 'password2' ), 5, 'mismatched passwords return 5' );
     5        QUnit.test( 'mismatched passwords should return 5', function( assert ) {
     6                assert.equal( passwordStrength( 'password1', 'username', 'password2' ), 5, 'mismatched passwords return 5' );
    77        });
    88
    9         test( 'passwords shorter than 4 characters should return 0', function() {
    10                 equal( passwordStrength( 'abc', 'username', 'abc' ), 0, 'short passwords return 0' );
     9        QUnit.test( 'passwords shorter than 4 characters should return 0', function( assert ) {
     10                assert.equal( passwordStrength( 'abc', 'username', 'abc' ), 0, 'short passwords return 0' );
    1111        });
    1212
    13         test( 'long complicated passwords should return 4', function() {
     13        QUnit.test( 'long complicated passwords should return 4', function( assert ) {
    1414                var password = function( length ) {
    1515                        var i, n, retVal = '',
    1616                                possibility = 'abcdefghijklnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
     
    2121                },
    2222                twofifty = password( 250 );
    2323
    24                 equal( passwordStrength( twofifty, 'username', twofifty ), 4, '250 character complicated password returns 4' );
     24                assert.equal( passwordStrength( twofifty, 'username', twofifty ), 4, '250 character complicated password returns 4' );
    2525        });
    2626
    27         test( 'short uncomplicated passwords should return 0', function() {
     27        QUnit.test( 'short uncomplicated passwords should return 0', function( assert ) {
    2828                var letters = 'aaaa',
    2929                        numbers = '1111',
    3030                        password = 'password',
    3131                        uppercase = 'AAAA';
    32                 equal( passwordStrength( letters, 'username', letters ), 0, 'password of `' + letters + '` returns 0' );
    33                 equal( passwordStrength( numbers, 'username', numbers ), 0, 'password of `' + numbers + '` returns 0' );
    34                 equal( passwordStrength( uppercase, 'username', uppercase ), 0, 'password of `' + uppercase + '` returns 0' );
    35                 equal( passwordStrength( password, 'username', password ), 0, 'password of `' + password + '` returns 0' );
     32                assert.equal( passwordStrength( letters, 'username', letters ), 0, 'password of `' + letters + '` returns 0' );
     33                assert.equal( passwordStrength( numbers, 'username', numbers ), 0, 'password of `' + numbers + '` returns 0' );
     34                assert.equal( passwordStrength( uppercase, 'username', uppercase ), 0, 'password of `' + uppercase + '` returns 0' );
     35                assert.equal( passwordStrength( password, 'username', password ), 0, 'password of `' + password + '` returns 0' );
    3636        });
    3737
    38         test( 'zxcvbn password tests should return the score we expect', function() {
     38        QUnit.test( 'zxcvbn password tests should return the score we expect', function( assert ) {
    3939                var passwords, i;
    4040                passwords = [
    4141                        { pw: 'zxcvbn', score: 0 },
     
    7676                ];
    7777
    7878                for ( i = 0; i < passwords.length; i++ ) {
    79                         equal( passwordStrength( passwords[i].pw, 'username', passwords[i].pw ), passwords[i].score, 'password of `' + passwords[i].pw + '` returns ' + passwords[i].score );
     79                        assert.equal( passwordStrength( passwords[i].pw, 'username', passwords[i].pw ), passwords[i].score, 'password of `' + passwords[i].pw + '` returns ' + passwords[i].score );
    8080                }
    8181        });
    8282
    83         test( 'blacklisted words in password should be penalized', function() {
     83        QUnit.test( 'blacklisted words in password should be penalized', function( assert ) {
    8484                var allowedPasswordScore, penalizedPasswordScore,
    8585                        allowedPassword   = 'a[janedoefoe]4',
    8686                        penalizedPassword = 'a[johndoefoe]4',
     
    8989                allowedPasswordScore = passwordStrength( allowedPassword, blacklist, allowedPassword );
    9090                penalizedPasswordScore = passwordStrength( penalizedPassword, blacklist, penalizedPassword );
    9191
    92                 ok( penalizedPasswordScore < allowedPasswordScore, 'Penalized password scored ' + penalizedPasswordScore + '; allowed password scored: ' + allowedPasswordScore );
     92                assert.ok( penalizedPasswordScore < allowedPasswordScore, 'Penalized password scored ' + penalizedPasswordScore + '; allowed password scored: ' + allowedPasswordScore );
    9393        });
    9494
    95         test( 'user input blacklist array should contain expected words', function() {
     95        QUnit.test( 'user input blacklist array should contain expected words', function( assert ) {
    9696                var blacklist = wp.passwordStrength.userInputBlacklist();
    9797
    98                 ok( jQuery.isArray( blacklist ), 'blacklist is an array' );
    99                 ok( jQuery.inArray( 'WordPress', blacklist ) > -1, 'blacklist contains "WordPress" from page title' );
    100                 ok( jQuery.inArray( 'tests', blacklist ) > -1, 'blacklist contains "tests" from site URL' );
     98                assert.ok( jQuery.isArray( blacklist ), 'blacklist is an array' );
     99                assert.ok( jQuery.inArray( 'WordPress', blacklist ) > -1, 'blacklist contains "WordPress" from page title' );
     100                assert.ok( jQuery.inArray( 'tests', blacklist ) > -1, 'blacklist contains "tests" from site URL' );
    101101        });
    102102});
  • tests/qunit/wp-admin/js/widgets/test-media-gallery-widget.js

     
    66( function() {
    77        'use strict';
    88
    9         module( 'Gallery Media Widget' );
     9        QUnit.module( 'Gallery Media Widget' );
    1010
    11         test( 'gallery widget control', function() {
     11        QUnit.test( 'gallery widget control', function( assert ) {
    1212                var GalleryWidgetControl;
    13                 equal( typeof wp.mediaWidgets.controlConstructors.media_gallery, 'function', 'wp.mediaWidgets.controlConstructors.media_gallery is a function' );
     13                assert.equal( typeof wp.mediaWidgets.controlConstructors.media_gallery, 'function', 'wp.mediaWidgets.controlConstructors.media_gallery is a function' );
    1414                GalleryWidgetControl = wp.mediaWidgets.controlConstructors.media_gallery;
    15                 ok( GalleryWidgetControl.prototype instanceof wp.mediaWidgets.MediaWidgetControl, 'wp.mediaWidgets.controlConstructors.media_gallery subclasses wp.mediaWidgets.MediaWidgetControl' );
     15                assert.ok( GalleryWidgetControl.prototype instanceof wp.mediaWidgets.MediaWidgetControl, 'wp.mediaWidgets.controlConstructors.media_gallery subclasses wp.mediaWidgets.MediaWidgetControl' );
    1616        });
    1717
    18         test( 'gallery media model', function() {
     18        QUnit.test( 'gallery media model', function( assert ) {
    1919                var GalleryWidgetModel, galleryWidgetModelInstance;
    20                 equal( typeof wp.mediaWidgets.modelConstructors.media_gallery, 'function', 'wp.mediaWidgets.modelConstructors.media_gallery is a function' );
     20                assert.equal( typeof wp.mediaWidgets.modelConstructors.media_gallery, 'function', 'wp.mediaWidgets.modelConstructors.media_gallery is a function' );
    2121                GalleryWidgetModel = wp.mediaWidgets.modelConstructors.media_gallery;
    22                 ok( GalleryWidgetModel.prototype instanceof wp.mediaWidgets.MediaWidgetModel, 'wp.mediaWidgets.modelConstructors.media_gallery subclasses wp.mediaWidgets.MediaWidgetModel' );
     22                assert.ok( GalleryWidgetModel.prototype instanceof wp.mediaWidgets.MediaWidgetModel, 'wp.mediaWidgets.modelConstructors.media_gallery subclasses wp.mediaWidgets.MediaWidgetModel' );
    2323
    2424                galleryWidgetModelInstance = new GalleryWidgetModel();
    2525                _.each( galleryWidgetModelInstance.attributes, function( value, key ) {
    26                         equal( value, GalleryWidgetModel.prototype.schema[ key ][ 'default' ], 'Should properly set default for ' + key );
     26                        assert.equal( value, GalleryWidgetModel.prototype.schema[ key ][ 'default' ], 'Should properly set default for ' + key );
    2727                });
    2828        });
    2929
  • tests/qunit/wp-admin/js/widgets/test-media-image-widget.js

     
    66( function() {
    77        'use strict';
    88
    9         module( 'Image Media Widget' );
     9        QUnit.module( 'Image Media Widget' );
    1010
    11         test( 'image widget control', function() {
     11        QUnit.test( 'image widget control', function( assert ) {
    1212                var ImageWidgetControl, imageWidgetControlInstance, imageWidgetModelInstance, mappedProps, testImageUrl, templateProps;
    1313                testImageUrl = 'http://s.w.org/style/images/wp-header-logo.png';
    14                 equal( typeof wp.mediaWidgets.controlConstructors.media_image, 'function', 'wp.mediaWidgets.controlConstructors.media_image is a function' );
     14                assert.equal( typeof wp.mediaWidgets.controlConstructors.media_image, 'function', 'wp.mediaWidgets.controlConstructors.media_image is a function' );
    1515                ImageWidgetControl = wp.mediaWidgets.controlConstructors.media_image;
    16                 ok( ImageWidgetControl.prototype instanceof wp.mediaWidgets.MediaWidgetControl, 'wp.mediaWidgets.controlConstructors.media_image subclasses wp.mediaWidgets.MediaWidgetControl' );
     16                assert.ok( ImageWidgetControl.prototype instanceof wp.mediaWidgets.MediaWidgetControl, 'wp.mediaWidgets.controlConstructors.media_image subclasses wp.mediaWidgets.MediaWidgetControl' );
    1717
    1818                imageWidgetModelInstance = new wp.mediaWidgets.modelConstructors.media_image();
    1919                imageWidgetControlInstance = new ImageWidgetControl({
     
    2424
    2525                // Test mapModelToPreviewTemplateProps() when no data is set.
    2626                templateProps = imageWidgetControlInstance.mapModelToPreviewTemplateProps();
    27                 equal( templateProps.caption, undefined, 'mapModelToPreviewTemplateProps should not return attributes that are should_preview_update false' );
    28                 equal( templateProps.attachment_id, 0, 'mapModelToPreviewTemplateProps should return default values' );
    29                 equal( templateProps.currentFilename, '', 'mapModelToPreviewTemplateProps should return a currentFilename' );
     27                assert.equal( templateProps.caption, undefined, 'mapModelToPreviewTemplateProps should not return attributes that are should_preview_update false' );
     28                assert.equal( templateProps.attachment_id, 0, 'mapModelToPreviewTemplateProps should return default values' );
     29                assert.equal( templateProps.currentFilename, '', 'mapModelToPreviewTemplateProps should return a currentFilename' );
    3030
    3131                // Test mapModelToPreviewTemplateProps() when data is set on model.
    3232                imageWidgetControlInstance.model.set( { url: testImageUrl, alt: 'some alt text', link_type: 'none' } );
    3333                templateProps = imageWidgetControlInstance.mapModelToPreviewTemplateProps();
    34                 equal( templateProps.currentFilename, 'wp-header-logo.png', 'mapModelToPreviewTemplateProps should set currentFilename based off of url' );
    35                 equal( templateProps.url, testImageUrl, 'mapModelToPreviewTemplateProps should return the proper url' );
    36                 equal( templateProps.alt, 'some alt text', 'mapModelToPreviewTemplateProps should return the proper alt text' );
    37                 equal( templateProps.link_type, undefined, 'mapModelToPreviewTemplateProps should ignore attributes that are not needed in the preview' );
    38                 equal( templateProps.error, false, 'mapModelToPreviewTemplateProps should return error state' );
     34                assert.equal( templateProps.currentFilename, 'wp-header-logo.png', 'mapModelToPreviewTemplateProps should set currentFilename based off of url' );
     35                assert.equal( templateProps.url, testImageUrl, 'mapModelToPreviewTemplateProps should return the proper url' );
     36                assert.equal( templateProps.alt, 'some alt text', 'mapModelToPreviewTemplateProps should return the proper alt text' );
     37                assert.equal( templateProps.link_type, undefined, 'mapModelToPreviewTemplateProps should ignore attributes that are not needed in the preview' );
     38                assert.equal( templateProps.error, false, 'mapModelToPreviewTemplateProps should return error state' );
    3939
    4040                // Test mapModelToPreviewTemplateProps() when error is set on model.
    4141                imageWidgetControlInstance.model.set( 'error', 'missing_attachment' );
    4242                templateProps = imageWidgetControlInstance.mapModelToPreviewTemplateProps();
    43                 equal( templateProps.error, 'missing_attachment', 'mapModelToPreviewTemplateProps should return error string' );
     43                assert.equal( templateProps.error, 'missing_attachment', 'mapModelToPreviewTemplateProps should return error string' );
    4444
    4545                // Reset model.
    4646                imageWidgetControlInstance.model.set({ error: false, attachment_id: 0, url: null });
    4747
    4848                // Test isSelected().
    49                 equal( imageWidgetControlInstance.isSelected(), false, 'media_image.isSelected() should return false when no media is selected' );
     49                assert.equal( imageWidgetControlInstance.isSelected(), false, 'media_image.isSelected() should return false when no media is selected' );
    5050                imageWidgetControlInstance.model.set({ error: 'missing_attachment', attachment_id: 777 });
    51                 equal( imageWidgetControlInstance.isSelected(), false, 'media_image.isSelected() should return false when media is selected and error is set' );
     51                assert.equal( imageWidgetControlInstance.isSelected(), false, 'media_image.isSelected() should return false when media is selected and error is set' );
    5252                imageWidgetControlInstance.model.set({ error: false, attachment_id: 777 });
    53                 equal( imageWidgetControlInstance.isSelected(), true, 'media_image.isSelected() should return true when media is selected and no error exists' );
     53                assert.equal( imageWidgetControlInstance.isSelected(), true, 'media_image.isSelected() should return true when media is selected and no error exists' );
    5454                imageWidgetControlInstance.model.set({ error: false, attachment_id: 0, url: testImageUrl });
    55                 equal( imageWidgetControlInstance.isSelected(), true, 'media_image.isSelected() should return true when url is set and no error exists' );
     55                assert.equal( imageWidgetControlInstance.isSelected(), true, 'media_image.isSelected() should return true when url is set and no error exists' );
    5656
    5757                // Reset model.
    5858                imageWidgetControlInstance.model.set({ error: false, attachment_id: 0, url: null });
     
    6060                // Test editing of widget title.
    6161                imageWidgetControlInstance.render();
    6262                imageWidgetControlInstance.$el.find( '.title' ).val( 'Chicken and Ribs' ).trigger( 'input' );
    63                 equal( imageWidgetModelInstance.get( 'title' ), 'Chicken and Ribs', 'Changing title should update model title attribute' );
     63                assert.equal( imageWidgetModelInstance.get( 'title' ), 'Chicken and Ribs', 'Changing title should update model title attribute' );
    6464
    6565                // Test mapMediaToModelProps.
    6666                mappedProps = imageWidgetControlInstance.mapMediaToModelProps( { link: 'file', url: testImageUrl } );
    67                 equal( mappedProps.link_url, testImageUrl, 'mapMediaToModelProps should set file link_url according to mediaFrameProps.link' );
     67                assert.equal( mappedProps.link_url, testImageUrl, 'mapMediaToModelProps should set file link_url according to mediaFrameProps.link' );
    6868                mappedProps = imageWidgetControlInstance.mapMediaToModelProps( { link: 'post', postUrl: 'https://wordpress.org/image-2/' } );
    69                 equal( mappedProps.link_url, 'https://wordpress.org/image-2/', 'mapMediaToModelProps should set file link_url according to mediaFrameProps.link' );
     69                assert.equal( mappedProps.link_url, 'https://wordpress.org/image-2/', 'mapMediaToModelProps should set file link_url according to mediaFrameProps.link' );
    7070                mappedProps = imageWidgetControlInstance.mapMediaToModelProps( { link: 'custom', linkUrl: 'https://wordpress.org' } );
    71                 equal( mappedProps.link_url, 'https://wordpress.org', 'mapMediaToModelProps should set custom link_url according to mediaFrameProps.linkUrl' );
     71                assert.equal( mappedProps.link_url, 'https://wordpress.org', 'mapMediaToModelProps should set custom link_url according to mediaFrameProps.linkUrl' );
    7272
    7373                // Test mapModelToMediaFrameProps().
    7474                imageWidgetControlInstance.model.set({ error: false, url: testImageUrl, 'link_type': 'custom', 'link_url': 'https://wordpress.org', 'size': 'custom', 'width': 100, 'height': 150, 'title': 'widget title', 'image_title': 'title of image' });
    7575                mappedProps = imageWidgetControlInstance.mapModelToMediaFrameProps( imageWidgetControlInstance.model.toJSON() );
    76                 equal( mappedProps.linkUrl, 'https://wordpress.org', 'mapModelToMediaFrameProps should set linkUrl from model.link_url' );
    77                 equal( mappedProps.link, 'custom', 'mapModelToMediaFrameProps should set link from model.link_type' );
    78                 equal( mappedProps.width, 100, 'mapModelToMediaFrameProps should set width when model.size is custom' );
    79                 equal( mappedProps.height, 150, 'mapModelToMediaFrameProps should set height when model.size is custom' );
    80                 equal( mappedProps.title, 'title of image', 'mapModelToMediaFrameProps should set title from model.image_title' );
     76                assert.equal( mappedProps.linkUrl, 'https://wordpress.org', 'mapModelToMediaFrameProps should set linkUrl from model.link_url' );
     77                assert.equal( mappedProps.link, 'custom', 'mapModelToMediaFrameProps should set link from model.link_type' );
     78                assert.equal( mappedProps.width, 100, 'mapModelToMediaFrameProps should set width when model.size is custom' );
     79                assert.equal( mappedProps.height, 150, 'mapModelToMediaFrameProps should set height when model.size is custom' );
     80                assert.equal( mappedProps.title, 'title of image', 'mapModelToMediaFrameProps should set title from model.image_title' );
    8181        });
    8282
    83         test( 'image widget control renderPreview', function( assert ) {
     83        QUnit.test( 'image widget control renderPreview', function( assert ) {
    8484                var imageWidgetControlInstance, imageWidgetModelInstance, done;
    8585                done = assert.async();
    8686
     
    9090                        syncContainer: jQuery( '<div></div>' ),
    9191                        model: imageWidgetModelInstance
    9292                });
    93                 equal( imageWidgetControlInstance.$el.find( 'img' ).length, 0, 'No images should be rendered' );
     93                assert.equal( imageWidgetControlInstance.$el.find( 'img' ).length, 0, 'No images should be rendered' );
    9494                imageWidgetControlInstance.model.set({ error: false, url: 'http://s.w.org/style/images/wp-header-logo.png' });
    9595
    9696                // Due to renderPreview being deferred.
    9797                setTimeout( function() {
    98                         equal( imageWidgetControlInstance.$el.find( 'img[src="http://s.w.org/style/images/wp-header-logo.png"]' ).length, 1, 'One image should be rendered' );
     98                        assert.equal( imageWidgetControlInstance.$el.find( 'img[src="http://s.w.org/style/images/wp-header-logo.png"]' ).length, 1, 'One image should be rendered' );
    9999                        done();
    100100                }, 50 );
    101101
    102                 start();
     102                done();
    103103        });
    104104
    105         test( 'image media model', function() {
     105        QUnit.test( 'image media model', function( assert ) {
    106106                var ImageWidgetModel, imageWidgetModelInstance;
    107                 equal( typeof wp.mediaWidgets.modelConstructors.media_image, 'function', 'wp.mediaWidgets.modelConstructors.media_image is a function' );
     107                assert.equal( typeof wp.mediaWidgets.modelConstructors.media_image, 'function', 'wp.mediaWidgets.modelConstructors.media_image is a function' );
    108108                ImageWidgetModel = wp.mediaWidgets.modelConstructors.media_image;
    109                 ok( ImageWidgetModel.prototype instanceof wp.mediaWidgets.MediaWidgetModel, 'wp.mediaWidgets.modelConstructors.media_image subclasses wp.mediaWidgets.MediaWidgetModel' );
     109                assert.ok( ImageWidgetModel.prototype instanceof wp.mediaWidgets.MediaWidgetModel, 'wp.mediaWidgets.modelConstructors.media_image subclasses wp.mediaWidgets.MediaWidgetModel' );
    110110
    111111                imageWidgetModelInstance = new ImageWidgetModel();
    112112                _.each( imageWidgetModelInstance.attributes, function( value, key ) {
    113                         equal( value, ImageWidgetModel.prototype.schema[ key ][ 'default' ], 'Should properly set default for ' + key );
     113                        assert.equal( value, ImageWidgetModel.prototype.schema[ key ][ 'default' ], 'Should properly set default for ' + key );
    114114                });
    115115        });
    116116
  • tests/qunit/wp-admin/js/widgets/test-media-video-widget.js

     
    66( function() {
    77        'use strict';
    88
    9         module( 'Video Media Widget' );
     9        QUnit.module( 'Video Media Widget' );
    1010
    11         test( 'video widget control', function() {
     11        QUnit.test( 'video widget control', function( assert ) {
    1212                var VideoWidgetControl, videoWidgetControlInstance, videoWidgetModelInstance, mappedProps, testVideoUrl;
    1313                testVideoUrl = 'https://videos.files.wordpress.com/AHz0Ca46/wp4-7-vaughan-r8-mastered_hd.mp4';
    14                 equal( typeof wp.mediaWidgets.controlConstructors.media_video, 'function', 'wp.mediaWidgets.controlConstructors.media_video is a function' );
     14                assert.equal( typeof wp.mediaWidgets.controlConstructors.media_video, 'function', 'wp.mediaWidgets.controlConstructors.media_video is a function' );
    1515                VideoWidgetControl = wp.mediaWidgets.controlConstructors.media_video;
    16                 ok( VideoWidgetControl.prototype instanceof wp.mediaWidgets.MediaWidgetControl, 'wp.mediaWidgets.controlConstructors.media_video subclasses wp.mediaWidgets.MediaWidgetControl' );
     16                assert.ok( VideoWidgetControl.prototype instanceof wp.mediaWidgets.MediaWidgetControl, 'wp.mediaWidgets.controlConstructors.media_video subclasses wp.mediaWidgets.MediaWidgetControl' );
    1717
    1818                videoWidgetModelInstance = new wp.mediaWidgets.modelConstructors.media_video();
    1919                videoWidgetControlInstance = new VideoWidgetControl({
     
    2525                // Test mapModelToMediaFrameProps().
    2626                videoWidgetControlInstance.model.set({ error: false, url: testVideoUrl, loop: false, preload: 'meta' });
    2727                mappedProps = videoWidgetControlInstance.mapModelToMediaFrameProps( videoWidgetControlInstance.model.toJSON() );
    28                 equal( mappedProps.url, testVideoUrl, 'mapModelToMediaFrameProps should set url' );
    29                 equal( mappedProps.loop, false, 'mapModelToMediaFrameProps should set loop' );
    30                 equal( mappedProps.preload, 'meta', 'mapModelToMediaFrameProps should set preload' );
     28                assert.equal( mappedProps.url, testVideoUrl, 'mapModelToMediaFrameProps should set url' );
     29                assert.equal( mappedProps.loop, false, 'mapModelToMediaFrameProps should set loop' );
     30                assert.equal( mappedProps.preload, 'meta', 'mapModelToMediaFrameProps should set preload' );
    3131
    3232                // Test mapMediaToModelProps().
    3333                mappedProps = videoWidgetControlInstance.mapMediaToModelProps( { loop: false, preload: 'meta', url: testVideoUrl, title: 'random movie file title' } );
    34                 equal( mappedProps.title, undefined, 'mapMediaToModelProps should ignore title inputs' );
    35                 equal( mappedProps.loop, false, 'mapMediaToModelProps should set loop' );
    36                 equal( mappedProps.preload, 'meta', 'mapMediaToModelProps should set preload' );
     34                assert.equal( mappedProps.title, undefined, 'mapMediaToModelProps should ignore title inputs' );
     35                assert.equal( mappedProps.loop, false, 'mapMediaToModelProps should set loop' );
     36                assert.equal( mappedProps.preload, 'meta', 'mapMediaToModelProps should set preload' );
    3737        });
    3838
    39         test( 'video widget control renderPreview', function( assert ) {
     39        QUnit.test( 'video widget control renderPreview', function( assert ) {
    4040                var videoWidgetControlInstance, videoWidgetModelInstance, done;
    4141                done = assert.async();
    4242
     
    4646                        syncContainer: jQuery( '<div></div>' ),
    4747                        model: videoWidgetModelInstance
    4848                });
    49                 equal( videoWidgetControlInstance.$el.find( 'a' ).length, 0, 'No video links should be rendered' );
     49                assert.equal( videoWidgetControlInstance.$el.find( 'a' ).length, 0, 'No video links should be rendered' );
    5050                videoWidgetControlInstance.model.set({ error: false, url: 'https://videos.files.wordpress.com/AHz0Ca46/wp4-7-vaughan-r8-mastered_hd.mp4' });
    5151
    5252                // Due to renderPreview being deferred.
    5353                setTimeout( function() {
    54                         equal( videoWidgetControlInstance.$el.find( 'a[href="https://videos.files.wordpress.com/AHz0Ca46/wp4-7-vaughan-r8-mastered_hd.mp4"]' ).length, 1, 'One video link should be rendered' );
     54                        assert.equal( videoWidgetControlInstance.$el.find( 'a[href="https://videos.files.wordpress.com/AHz0Ca46/wp4-7-vaughan-r8-mastered_hd.mp4"]' ).length, 1, 'One video link should be rendered' );
    5555                        done();
    5656                }, 50 );
    57                 start();
     57
     58                done();
    5859        });
    5960
    60         test( 'video media model', function() {
     61        QUnit.test( 'video media model', function( assert ) {
    6162                var VideoWidgetModel, videoWidgetModelInstance;
    62                 equal( typeof wp.mediaWidgets.modelConstructors.media_video, 'function', 'wp.mediaWidgets.modelConstructors.media_video is a function' );
     63                assert.equal( typeof wp.mediaWidgets.modelConstructors.media_video, 'function', 'wp.mediaWidgets.modelConstructors.media_video is a function' );
    6364                VideoWidgetModel = wp.mediaWidgets.modelConstructors.media_video;
    64                 ok( VideoWidgetModel.prototype instanceof wp.mediaWidgets.MediaWidgetModel, 'wp.mediaWidgets.modelConstructors.media_video subclasses wp.mediaWidgets.MediaWidgetModel' );
     65                assert.ok( VideoWidgetModel.prototype instanceof wp.mediaWidgets.MediaWidgetModel, 'wp.mediaWidgets.modelConstructors.media_video subclasses wp.mediaWidgets.MediaWidgetModel' );
    6566
    6667                videoWidgetModelInstance = new VideoWidgetModel();
    6768                _.each( videoWidgetModelInstance.attributes, function( value, key ) {
    68                         equal( value, VideoWidgetModel.prototype.schema[ key ][ 'default' ], 'Should properly set default for ' + key );
     69                        assert.equal( value, VideoWidgetModel.prototype.schema[ key ][ 'default' ], 'Should properly set default for ' + key );
    6970                });
    7071        });
    7172
  • tests/qunit/wp-admin/js/widgets/test-media-widgets.js

     
    22/* jshint qunit: true */
    33/* eslint-env qunit */
    44
    5 (function() {
     5( function() {
    66        'use strict';
    77
    8         module( 'Media Widgets' );
     8        QUnit.module( 'Media Widgets' );
    99
    10         test( 'namespace', function() {
    11                 equal( typeof wp.mediaWidgets, 'object', 'wp.mediaWidgets is an object' );
    12                 equal( typeof wp.mediaWidgets.controlConstructors, 'object', 'wp.mediaWidgets.controlConstructors is an object' );
    13                 equal( typeof wp.mediaWidgets.modelConstructors, 'object', 'wp.mediaWidgets.modelConstructors is an object' );
    14                 equal( typeof wp.mediaWidgets.widgetControls, 'object', 'wp.mediaWidgets.widgetControls is an object' );
    15                 equal( typeof wp.mediaWidgets.handleWidgetAdded, 'function', 'wp.mediaWidgets.handleWidgetAdded is an function' );
    16                 equal( typeof wp.mediaWidgets.handleWidgetUpdated, 'function', 'wp.mediaWidgets.handleWidgetUpdated is an function' );
    17                 equal( typeof wp.mediaWidgets.init, 'function', 'wp.mediaWidgets.init is an function' );
     10        QUnit.test( 'namespace', function( assert ) {
     11                assert.equal( typeof wp.mediaWidgets, 'object', 'wp.mediaWidgets is an object' );
     12                assert.equal( typeof wp.mediaWidgets.controlConstructors, 'object', 'wp.mediaWidgets.controlConstructors is an object' );
     13                assert.equal( typeof wp.mediaWidgets.modelConstructors, 'object', 'wp.mediaWidgets.modelConstructors is an object' );
     14                assert.equal( typeof wp.mediaWidgets.widgetControls, 'object', 'wp.mediaWidgets.widgetControls is an object' );
     15                assert.equal( typeof wp.mediaWidgets.handleWidgetAdded, 'function', 'wp.mediaWidgets.handleWidgetAdded is an function' );
     16                assert.equal( typeof wp.mediaWidgets.handleWidgetUpdated, 'function', 'wp.mediaWidgets.handleWidgetUpdated is an function' );
     17                assert.equal( typeof wp.mediaWidgets.init, 'function', 'wp.mediaWidgets.init is an function' );
    1818        });
    1919
    20         test( 'media widget control', function() {
    21                 equal( typeof wp.mediaWidgets.MediaWidgetControl, 'function', 'wp.mediaWidgets.MediaWidgetControl' );
    22                 ok( wp.mediaWidgets.MediaWidgetControl.prototype instanceof Backbone.View, 'wp.mediaWidgets.MediaWidgetControl subclasses Backbone.View' );
     20        QUnit.test( 'media widget control', function( assert ) {
     21                assert.equal( typeof wp.mediaWidgets.MediaWidgetControl, 'function', 'wp.mediaWidgets.MediaWidgetControl' );
     22                assert.ok( wp.mediaWidgets.MediaWidgetControl.prototype instanceof Backbone.View, 'wp.mediaWidgets.MediaWidgetControl subclasses Backbone.View' );
    2323        });
    2424
    25         test( 'media widget model', function() {
     25        QUnit.test( 'media widget model', function( assert ) {
    2626                var widgetModelInstance;
    27                 equal( typeof wp.mediaWidgets.MediaWidgetModel, 'function', 'wp.mediaWidgets.MediaWidgetModel is a function' );
    28                 ok( wp.mediaWidgets.MediaWidgetModel.prototype instanceof Backbone.Model, 'wp.mediaWidgets.MediaWidgetModel subclasses Backbone.Model' );
     27                assert.equal( typeof wp.mediaWidgets.MediaWidgetModel, 'function', 'wp.mediaWidgets.MediaWidgetModel is a function' );
     28                assert.ok( wp.mediaWidgets.MediaWidgetModel.prototype instanceof Backbone.Model, 'wp.mediaWidgets.MediaWidgetModel subclasses Backbone.Model' );
    2929
    3030                widgetModelInstance = new wp.mediaWidgets.MediaWidgetModel();
    31                 equal( widgetModelInstance.get( 'title' ), '', 'wp.mediaWidgets.MediaWidgetModel defaults title to empty string' );
    32                 equal( widgetModelInstance.get( 'attachment_id' ), 0, 'wp.mediaWidgets.MediaWidgetModel defaults attachment_id to 0' );
    33                 equal( widgetModelInstance.get( 'url' ), 0, 'wp.mediaWidgets.MediaWidgetModel defaults url to empty string' );
     31                assert.equal( widgetModelInstance.get( 'title' ), '', 'wp.mediaWidgets.MediaWidgetModel defaults title to empty string' );
     32                assert.equal( widgetModelInstance.get( 'attachment_id' ), 0, 'wp.mediaWidgets.MediaWidgetModel defaults attachment_id to 0' );
     33                assert.equal( widgetModelInstance.get( 'url' ), 0, 'wp.mediaWidgets.MediaWidgetModel defaults url to empty string' );
    3434
    3535                widgetModelInstance.set({
    3636                        title: 'chicken and ribs',
     
    3737                        attachment_id: '1',
    3838                        url: 'https://wordpress.org'
    3939                });
    40                 equal( widgetModelInstance.get( 'title' ), 'chicken and ribs', 'wp.mediaWidgets.MediaWidgetModel properly sets the title attribute' );
    41                 equal( widgetModelInstance.get( 'url' ), 'https://wordpress.org', 'wp.mediaWidgets.MediaWidgetModel properly sets the url attribute' );
    42                 equal( widgetModelInstance.get( 'attachment_id' ), 1, 'wp.mediaWidgets.MediaWidgetModel properly sets and casts the attachment_id attribute' );
     40                assert.equal( widgetModelInstance.get( 'title' ), 'chicken and ribs', 'wp.mediaWidgets.MediaWidgetModel properly sets the title attribute' );
     41                assert.equal( widgetModelInstance.get( 'url' ), 'https://wordpress.org', 'wp.mediaWidgets.MediaWidgetModel properly sets the url attribute' );
     42                assert.equal( widgetModelInstance.get( 'attachment_id' ), 1, 'wp.mediaWidgets.MediaWidgetModel properly sets and casts the attachment_id attribute' );
    4343        });
    4444
    4545})();
  • tests/qunit/wp-includes/js/shortcode.js

     
    11/* global wp, jQuery */
    22jQuery( function() {
    3         module( 'shortcode' );
     3        QUnit.module( 'shortcode' );
    44
    5         test( 'next() should find the shortcode', function() {
     5        QUnit.test( 'next() should find the shortcode', function( assert ) {
    66                var result;
    77
    88                // Basic.
    99                result = wp.shortcode.next( 'foo', 'this has the [foo] shortcode' );
    10                 equal( result.index, 13, 'foo shortcode found at index 13' );
     10                assert.equal( result.index, 13, 'foo shortcode found at index 13' );
    1111
    1212                result = wp.shortcode.next( 'foo', 'this has the [foo param="foo"] shortcode' );
    13                 equal( result.index, 13, 'foo shortcode with params found at index 13' );
     13                assert.equal( result.index, 13, 'foo shortcode with params found at index 13' );
    1414        });
    1515
    16         test( 'next() should not find shortcodes that are not there', function() {
     16        QUnit.test( 'next() should not find shortcodes that are not there', function( assert ) {
    1717                var result;
    1818
    1919                // Not found.
    2020                result = wp.shortcode.next( 'bar', 'this has the [foo] shortcode' );
    21                 equal( result, undefined, 'bar shortcode not found' );
     21                assert.equal( result, undefined, 'bar shortcode not found' );
    2222
    2323                result = wp.shortcode.next( 'bar', 'this has the [foo param="bar"] shortcode' );
    24                 equal( result, undefined, 'bar shortcode not found with params' );
     24                assert.equal( result, undefined, 'bar shortcode not found with params' );
    2525        });
    2626
    27         test( 'next() should find the shortcode when told to start looking beyond the start of the string', function() {
     27        QUnit.test( 'next() should find the shortcode when told to start looking beyond the start of the string', function( assert ) {
    2828                var result;
    2929
    3030                // Starting at indices.
    3131                result = wp.shortcode.next( 'foo', 'this has the [foo] shortcode', 12 );
    32                 equal( result.index, 13, 'foo shortcode found before index 13' );
     32                assert.equal( result.index, 13, 'foo shortcode found before index 13' );
    3333
    3434                result = wp.shortcode.next( 'foo', 'this has the [foo] shortcode', 13 );
    35                 equal( result.index, 13, 'foo shortcode found at index 13' );
     35                assert.equal( result.index, 13, 'foo shortcode found at index 13' );
    3636
    3737                result = wp.shortcode.next( 'foo', 'this has the [foo] shortcode', 14 );
    38                 equal( result, undefined, 'foo shortcode not found after index 13' );
     38                assert.equal( result, undefined, 'foo shortcode not found after index 13' );
    3939        });
    4040
    41         test( 'next() should find the second instances of the shortcode when the starting indice is after the start of the first one', function() {
     41        QUnit.test( 'next() should find the second instances of the shortcode when the starting indice is after the start of the first one', function( assert ) {
    4242                var result;
    4343
    4444                result = wp.shortcode.next( 'foo', 'this has the [foo] shortcode [foo] twice', 14 );
    45                 equal( result.index, 29, 'foo shortcode found the second foo at index 29' );
     45                assert.equal( result.index, 29, 'foo shortcode found the second foo at index 29' );
    4646        });
    4747
    4848
    49         test( 'next() should not find escaped shortcodes', function() {
     49        QUnit.test( 'next() should not find escaped shortcodes', function( assert ) {
    5050                var result;
    5151
    5252                // Escaped.
    5353                result = wp.shortcode.next( 'foo', 'this has the [[foo]] shortcode' );
    54                 equal( result, undefined, 'foo shortcode not found when escaped' );
     54                assert.equal( result, undefined, 'foo shortcode not found when escaped' );
    5555
    5656                result = wp.shortcode.next( 'foo', 'this has the [[foo param="foo"]] shortcode' );
    57                 equal( result, undefined, 'foo shortcode not found when escaped with params' );
     57                assert.equal( result, undefined, 'foo shortcode not found when escaped with params' );
    5858        });
    5959
    60         test( 'next() should find shortcodes that are incorrectly escaped by newlines', function() {
     60        QUnit.test( 'next() should find shortcodes that are incorrectly escaped by newlines', function( assert ) {
    6161                var result;
    6262
    6363                result = wp.shortcode.next( 'foo', 'this has the [\n[foo]] shortcode' );
    64                 equal( result.index, 15, 'shortcode found when incorrectly escaping the start of it' );
     64                assert.equal( result.index, 15, 'shortcode found when incorrectly escaping the start of it' );
    6565
    6666                result = wp.shortcode.next( 'foo', 'this has the [[foo]\n] shortcode' );
    67                 equal( result.index, 14, 'shortcode found when incorrectly escaping the end of it' );
     67                assert.equal( result.index, 14, 'shortcode found when incorrectly escaping the end of it' );
    6868        });
    6969
    70         test( 'next() should still work when there are not equal ammounts of square brackets', function() {
     70        QUnit.test( 'next() should still work when there are not equal ammounts of square brackets', function( assert ) {
    7171                var result;
    7272
    7373                result = wp.shortcode.next( 'foo', 'this has the [[foo] shortcode' );
    74                 equal( result.index, 14, 'shortcode found when there are offset square brackets' );
     74                assert.equal( result.index, 14, 'shortcode found when there are offset square brackets' );
    7575
    7676                result = wp.shortcode.next( 'foo', 'this has the [foo]] shortcode' );
    77                 equal( result.index, 13, 'shortcode found when there are offset square brackets' );
     77                assert.equal( result.index, 13, 'shortcode found when there are offset square brackets' );
    7878        });
    7979
    80         test( 'next() should find the second instances of the shortcode when the first one is escaped', function() {
     80        QUnit.test( 'next() should find the second instances of the shortcode when the first one is escaped', function( assert ) {
    8181                var result;
    8282
    8383
    8484                result = wp.shortcode.next( 'foo', 'this has the [[foo]] shortcode [foo] twice' );
    85                 equal( result.index, 31, 'foo shortcode found the non-escaped foo at index 31' );
     85                assert.equal( result.index, 31, 'foo shortcode found the non-escaped foo at index 31' );
    8686        });
    8787
    88         test( 'next() should not find shortcodes that are not full matches', function() {
     88        QUnit.test( 'next() should not find shortcodes that are not full matches', function( assert ) {
    8989                var result;
    9090
    9191                // Stubs.
    9292                result = wp.shortcode.next( 'foo', 'this has the [foobar] shortcode' );
    93                 equal( result, undefined, 'stub does not trigger match' );
     93                assert.equal( result, undefined, 'stub does not trigger match' );
    9494
    9595                result = wp.shortcode.next( 'foobar', 'this has the [foo] shortcode' );
    96                 equal( result, undefined, 'stub does not trigger match' );
     96                assert.equal( result, undefined, 'stub does not trigger match' );
    9797        });
    9898
    99         test( 'replace() should replace the shortcode', function() {
     99        QUnit.test( 'replace() should replace the shortcode', function( assert ) {
    100100                var result;
    101101
    102102                // Basic.
    103103                result = wp.shortcode.replace( 'foo', 'this has the [foo] shortcode', shortcodeReplaceCallback );
    104                 equal( result, 'this has the bar shortcode', 'foo replaced with bar' );
     104                assert.equal( result, 'this has the bar shortcode', 'foo replaced with bar' );
    105105
    106106                result = wp.shortcode.replace( 'foo', 'this has the [foo param="foo"] shortcode', shortcodeReplaceCallback );
    107                 equal( result, 'this has the bar shortcode', 'foo and params replaced with bar' );
     107                assert.equal( result, 'this has the bar shortcode', 'foo and params replaced with bar' );
    108108        });
    109109
    110         test( 'replace() should not replace the shortcode when it does not match', function() {
     110        QUnit.test( 'replace() should not replace the shortcode when it does not match', function( assert ) {
    111111                var result;
    112112
    113113                // Not found.
    114114                result = wp.shortcode.replace( 'bar', 'this has the [foo] shortcode', shortcodeReplaceCallback );
    115                 equal( result, 'this has the [foo] shortcode', 'bar not found' );
     115                assert.equal( result, 'this has the [foo] shortcode', 'bar not found' );
    116116
    117117                result = wp.shortcode.replace( 'bar', 'this has the [foo param="bar"] shortcode', shortcodeReplaceCallback );
    118                 equal( result, 'this has the [foo param="bar"] shortcode', 'bar not found with params' );
     118                assert.equal( result, 'this has the [foo param="bar"] shortcode', 'bar not found with params' );
    119119        });
    120120
    121         test( 'replace() should replace the shortcode in all instances of its use', function() {
     121        QUnit.test( 'replace() should replace the shortcode in all instances of its use', function( assert ) {
    122122                var result;
    123123
    124124                // Multiple instances.
    125125                result = wp.shortcode.replace( 'foo', 'this has the [foo] shortcode [foo] twice', shortcodeReplaceCallback );
    126                 equal( result, 'this has the bar shortcode bar twice', 'foo replaced with bar twice' );
     126                assert.equal( result, 'this has the bar shortcode bar twice', 'foo replaced with bar twice' );
    127127
    128128                result = wp.shortcode.replace( 'foo', 'this has the [foo param="foo"] shortcode [foo] twice', shortcodeReplaceCallback );
    129                 equal( result, 'this has the bar shortcode bar twice', 'foo and params replaced with bar twice' );
     129                assert.equal( result, 'this has the bar shortcode bar twice', 'foo and params replaced with bar twice' );
    130130        });
    131131
    132         test( 'replace() should not replace the escaped shortcodes', function() {
     132        QUnit.test( 'replace() should not replace the escaped shortcodes', function( assert ) {
    133133                var result;
    134134
    135135                // Escaped.
    136136                result = wp.shortcode.replace( 'foo', 'this has the [[foo]] shortcode', shortcodeReplaceCallback );
    137                 equal( result, 'this has the [[foo]] shortcode', 'escaped foo not replaced' );
     137                assert.equal( result, 'this has the [[foo]] shortcode', 'escaped foo not replaced' );
    138138
    139139                result = wp.shortcode.replace( 'foo', 'this has the [[foo param="bar"]] shortcode', shortcodeReplaceCallback );
    140                 equal( result, 'this has the [[foo param="bar"]] shortcode', 'escaped foo with params not replaced' );
     140                assert.equal( result, 'this has the [[foo param="bar"]] shortcode', 'escaped foo with params not replaced' );
    141141
    142142                result = wp.shortcode.replace( 'foo', 'this [foo] has the [[foo param="bar"]] shortcode escaped', shortcodeReplaceCallback );
    143                 equal( result, 'this bar has the [[foo param="bar"]] shortcode escaped', 'escaped foo with params not replaced but unescaped foo replaced' );
     143                assert.equal( result, 'this bar has the [[foo param="bar"]] shortcode escaped', 'escaped foo with params not replaced but unescaped foo replaced' );
    144144        });
    145145
    146         test( 'replace() should replace improperly escaped shortcodes that include newlines', function() {
     146        QUnit.test( 'replace() should replace improperly escaped shortcodes that include newlines', function( assert ) {
    147147                var result;
    148148
    149149                result = wp.shortcode.replace( 'foo', 'this [foo] has the [[foo param="bar"]\n] shortcode ', shortcodeReplaceCallback );
    150                 equal( result, 'this bar has the [bar\n] shortcode ', 'escaping with newlines should not actually escape the content' );
     150                assert.equal( result, 'this bar has the [bar\n] shortcode ', 'escaping with newlines should not actually escape the content' );
    151151
    152152                result = wp.shortcode.replace( 'foo', 'this [foo] has the [\n[foo param="bar"]] shortcode ', shortcodeReplaceCallback );
    153                 equal( result, 'this bar has the [\nbar] shortcode ', 'escaping with newlines should not actually escape the content' );
     153                assert.equal( result, 'this bar has the [\nbar] shortcode ', 'escaping with newlines should not actually escape the content' );
    154154        });
    155155
    156         test( 'replace() should not replace the shortcode when it is an incomplete match', function() {
     156        QUnit.test( 'replace() should not replace the shortcode when it is an incomplete match', function( assert ) {
    157157                var result;
    158158
    159159                // Stubs.
    160160                result = wp.shortcode.replace( 'foo', 'this has the [foobar] shortcode', shortcodeReplaceCallback );
    161                 equal( result, 'this has the [foobar] shortcode', 'stub not replaced' );
     161                assert.equal( result, 'this has the [foobar] shortcode', 'stub not replaced' );
    162162
    163163                result = wp.shortcode.replace( 'foobar', 'this has the [foo] shortcode', shortcodeReplaceCallback );
    164                 equal( result, 'this has the [foo] shortcode', 'stub not replaced' );
     164                assert.equal( result, 'this has the [foo] shortcode', 'stub not replaced' );
    165165        });
    166166
    167167        /**
     
    171171                return 'bar';
    172172        }
    173173
    174     test( 'attrs() should return named attributes created with single, double, and no quotes', function() {
     174    QUnit.test( 'attrs() should return named attributes created with single, double, and no quotes', function( assert ) {
    175175        var expected = {
    176176            'named': {
    177177                'param': 'foo',
     
    180180            }, 'numeric' : []
    181181        };
    182182
    183         deepEqual( wp.shortcode.attrs('param="foo" another=\'bar\' andagain=baz'), expected, 'attr parsed all three named types');
     183        assert.deepEqual( wp.shortcode.attrs('param="foo" another=\'bar\' andagain=baz'), expected, 'attr parsed all three named types');
    184184    });
    185185
    186     test( 'attrs() should return numeric attributes in the order they are used', function() {
     186    QUnit.test( 'attrs() should return numeric attributes in the order they are used', function( assert ) {
    187187        var expected = {
    188188            'named': {}, 'numeric' : ['foo', 'bar', 'baz']
    189189        };
    190190
    191         deepEqual( wp.shortcode.attrs('foo bar baz'), expected, 'attr parsed numeric attributes');
     191        assert.deepEqual( wp.shortcode.attrs('foo bar baz'), expected, 'attr parsed numeric attributes');
    192192    });
    193193
    194     test( 'attrs() should return numeric attributes in the order they are used when they have named attributes in between', function() {
     194    QUnit.test( 'attrs() should return numeric attributes in the order they are used when they have named attributes in between', function( assert ) {
    195195        var expected = {
    196196            'named': { 'not': 'a blocker'  }, 'numeric' : ['foo', 'bar', 'baz']
    197197        };
    198198
    199         deepEqual( wp.shortcode.attrs('foo not="a blocker" bar baz'), expected, 'attr parsed numeric attributes');
     199        assert.deepEqual( wp.shortcode.attrs('foo not="a blocker" bar baz'), expected, 'attr parsed numeric attributes');
    200200    });
    201201
    202         test( 'attrs() should return numeric attributes created with single, double, and no quotes', function() {
     202        QUnit.test( 'attrs() should return numeric attributes created with single, double, and no quotes', function( assert ) {
    203203                var expected = {
    204204                        'named': {}, 'numeric' : ['foo', 'bar', 'baz']
    205205                };
    206206
    207                 deepEqual( wp.shortcode.attrs('foo "bar" \'baz\''), expected, 'attr parsed numeric attributes');
     207                assert.deepEqual( wp.shortcode.attrs('foo "bar" \'baz\''), expected, 'attr parsed numeric attributes');
    208208        });
    209209       
    210         test( 'attrs() should return mixed attributes created with single, double, and no quotes', function() {
     210        QUnit.test( 'attrs() should return mixed attributes created with single, double, and no quotes', function( assert ) {
    211211                var expected = {
    212212                        'named': { a: 'foo', b: 'bar', c: 'baz' }, 'numeric' : ['foo', 'bar', 'baz']
    213213                };
    214214
    215                 deepEqual( wp.shortcode.attrs('a="foo" b=\'bar\' c=baz foo "bar" \'baz\''), expected, 'attr parsed numeric attributes');
     215                assert.deepEqual( wp.shortcode.attrs('a="foo" b=\'bar\' c=baz foo "bar" \'baz\''), expected, 'attr parsed numeric attributes');
    216216        });
    217217
    218         test( 'string() should accept attrs in any order', function() {
     218        QUnit.test( 'string() should accept attrs in any order', function( assert ) {
    219219                var expected = '[short abc123 foo="bar"]';
    220220                var result;
    221221
     
    227227                                numeric : [ 'abc123' ]
    228228                        }
    229229                });
    230                 deepEqual( result, expected, 'attributes are accepted in any order' );
     230                assert.deepEqual( result, expected, 'attributes are accepted in any order' );
    231231
    232232                result = wp.shortcode.string({
    233233                        tag   : 'short',
     
    237237                                named   : { foo : 'bar' }
    238238                        }
    239239                });
    240                 deepEqual( result, expected, 'attributes are accepted in any order' );
     240                assert.deepEqual( result, expected, 'attributes are accepted in any order' );
    241241        });
    242242});
  • tests/qunit/wp-includes/js/tinymce/tinymce-obsolete.js

     
    8181
    8282        // Ref: http://www.w3.org/TR/html5/obsolete.html, http://developers.whatwg.org/obsolete.html
    8383
    84         QUnit.test('HTML elements non-conforming to HTML 5.0', function() {
     84        QUnit.test('HTML elements non-conforming to HTML 5.0', function( assert ) {
    8585                var testString;
    8686
    8787                /*
     
    109109                The rest are still supported in TinyMCE but "...must not be used by authors".
    110110                */
    111111
    112                 expect(6);
     112                assert.expect(6);
    113113
    114114                text = 'acronym';
    115115                testString = '<p><acronym title="www">WWW</acronym></p>';
    116116                editor.setContent( testString );
    117                 equal( editor.getContent(), testString, text );
     117                assert.equal( editor.getContent(), testString, text );
    118118
    119119                text = 'strike, converted to span';
    120120                editor.setContent( '<strike>test</strike>' );
    121                 equal( editor.getContent(), '<p><span style="text-decoration: line-through;">test</span></p>', text );
     121                assert.equal( editor.getContent(), '<p><span style="text-decoration: line-through;">test</span></p>', text );
    122122
    123123                text = 'big';
    124124                testString = '<p><big>test</big></p>';
    125125                editor.setContent( testString );
    126                 equal( editor.getContent(), testString, text );
     126                assert.equal( editor.getContent(), testString, text );
    127127
    128128                text = 'center';
    129129                testString = '<center>test</center>';
    130130                editor.setContent( testString );
    131                 equal( editor.getContent(), testString, text );
     131                assert.equal( editor.getContent(), testString, text );
    132132
    133133                text = 'font, converted to span';
    134134                editor.setContent( '<p><font size="4">test</font></p>' );
    135                 equal( editor.getContent(), '<p><span style="font-size: large;">test</span></p>', text );
     135                assert.equal( editor.getContent(), '<p><span style="font-size: large;">test</span></p>', text );
    136136
    137137                text = 'tt';
    138138                testString = '<p><tt>test</tt></p>';
    139139                editor.setContent( testString );
    140                 equal( editor.getContent(), testString, text );
     140                assert.equal( editor.getContent(), testString, text );
    141141        });
    142142
    143         QUnit.test('Obsolete (but still conforming) HTML attributes', function() {
     143        QUnit.test('Obsolete (but still conforming) HTML attributes', function( assert ) {
    144144                var testString;
    145145
    146                 expect(3);
     146                assert.expect(3);
    147147
    148148                text = 'border on <img>';
    149149                testString = '<p><img src="../../test.gif" alt="" border="5" /></p>';
    150150                editor.setContent( testString );
    151                 equal( editor.getContent(), testString, text );
     151                assert.equal( editor.getContent(), testString, text );
    152152
    153153                text = 'Old style anchors';
    154154                testString = '<p><a name="test"></a></p>';
    155155                editor.setContent( testString );
    156                 equal( editor.getContent(), testString, text );
     156                assert.equal( editor.getContent(), testString, text );
    157157
    158158                text = 'maxlength, size on input type="number"';
    159159                testString = '<p><input maxlength="5" size="10" type="number" value="" /></p>';
    160160                editor.setContent( testString );
    161                 ok( hasAttr( editor.getContent(), { input: 'maxlength size' } ), text );
     161                assert.ok( hasAttr( editor.getContent(), { input: 'maxlength size' } ), text );
    162162        });
    163163
    164         QUnit.test('Obsolete attributes in HTML 5.0', function() {
     164        QUnit.test('Obsolete attributes in HTML 5.0', function( assert ) {
    165165                var testString, text;
    166166
    167                 expect(22);
     167                assert.expect(22);
    168168
    169169                text = 'charset, rev, shape, coords on <a> elements';
    170170                testString = '<p><a href="javascript;:" charset="en" rev="made" shape="rect" coords="5,5">test</a></p>';
    171171                editor.setContent( testString );
    172                 ok( hasAttr( editor.getContent(), { a: 'charset rev shape coords' } ), text );
     172                assert.ok( hasAttr( editor.getContent(), { a: 'charset rev shape coords' } ), text );
    173173
    174174                text = 'name, align, hspace, vspace on img elements';
    175175                testString = '<p><img src="../../test.gif" alt="" name="test" align="left" hspace="5" vspace="5" /></p>';
    176176                editor.setContent( testString );
    177                 ok( hasAttr( editor.getContent(), { img: 'name align hspace vspace' } ), text );
     177                assert.ok( hasAttr( editor.getContent(), { img: 'name align hspace vspace' } ), text );
    178178
    179179                text = 'name, align, hspace, vspace, on embed elements';
    180180                testString = '<p><embed width="100" height="100" src="test.swf" vspace="5" hspace="5" align="left" name="test"></embed></p>';
    181181                editor.setContent( testString );
    182                 ok( hasAttr( editor.getContent(), { embed: 'name align hspace vspace' } ), text );
     182                assert.ok( hasAttr( editor.getContent(), { embed: 'name align hspace vspace' } ), text );
    183183
    184184                text = 'archive, classid, code, codebase, codetype, declare, standby on object elements';
    185185                testString = '<p><object width="100" height="100" classid="clsid" codebase="clsid" standby="standby" codetype="1" code="1" archive="1" declare="declare"></object></p>';
    186186                editor.setContent( testString );
    187                 ok( hasAttr( editor.getContent(), { object: 'archive classid code codebase codetype declare standby' } ), text );
     187                assert.ok( hasAttr( editor.getContent(), { object: 'archive classid code codebase codetype declare standby' } ), text );
    188188
    189189                text = 'type, valuetype on param elements';
    190190                testString = '<p><object width="100" height="100"><param type="" valuetype="" /></object></p>';
    191191                editor.setContent( testString );
    192                 ok( hasAttr( editor.getContent(), { param: 'type valuetype' } ), text );
     192                assert.ok( hasAttr( editor.getContent(), { param: 'type valuetype' } ), text );
    193193
    194194                text = 'align, bgcolor, border, cellpadding, cellspacing, frame, rules, summary, width on table elements';
    195195                testString = '<table border="1" summary="" width="100" frame="" rules="" cellspacing="5" cellpadding="5" align="left" bgcolor="blue"><tbody><tr><td>test</td></tr></tbody></table>';
    196196                editor.setContent( testString );
    197                 ok( hasAttr( editor.getContent(), { table: 'align bgcolor border cellpadding cellspacing frame rules summary width' } ), text );
     197                assert.ok( hasAttr( editor.getContent(), { table: 'align bgcolor border cellpadding cellspacing frame rules summary width' } ), text );
    198198
    199199                text = 'align, char, charoff, valign on tbody, thead, and tfoot elements';
    200200                testString = '<table><thead align="left" char="" charoff="" valign="top"></thead><tfoot align="left" char="" charoff="" valign="top"></tfoot><tbody align="left" char="" charoff="" valign="top"><tr><th>test</th><td>test</td></tr></tbody></table>';
    201201                editor.setContent( testString );
    202                 ok( hasAttr( editor.getContent(), {
     202                assert.ok( hasAttr( editor.getContent(), {
    203203                        thead: 'align char charoff valign',
    204204                        tfoot: 'align char charoff valign',
    205205                        tbody: 'align char charoff valign'
     
    208208                text = 'axis, align, bgcolor, char, charoff, height, nowrap, valign, width on td and th elements, scope on td elements';
    209209                testString = '<table><tbody><tr><th axis="" align="left" char="" charoff="" valign="top" nowrap="nowrap" bgcolor="blue" width="100" height="10">test</th><td axis="" align="left" char="" charoff="" valign="top" nowrap="nowrap" bgcolor="blue" width="100" height="10" scope="">test</td></tr></tbody></table>';
    210210                editor.setContent( testString );
    211                 ok( hasAttr( editor.getContent(), {
     211                assert.ok( hasAttr( editor.getContent(), {
    212212                        th: 'axis align bgcolor char charoff height nowrap valign width',
    213213                        td: 'axis align bgcolor char charoff height nowrap valign width scope'
    214214                } ), text );
     
    216216                text = 'align, bgcolor, char, charoff, valign on tr elements';
    217217                testString = '<table><tbody><tr align="left" char="" charoff="" valign="top" bgcolor="blue"><td>test</td></tr></tbody></table>';
    218218                editor.setContent( testString );
    219                 ok( hasAttr( editor.getContent(), { tr: 'align bgcolor char charoff valign' } ), text );
     219                assert.ok( hasAttr( editor.getContent(), { tr: 'align bgcolor char charoff valign' } ), text );
    220220
    221221                text = 'clear on br elements';
    222222                testString = '<p>test<br clear="all" />test</p>';
    223223                editor.setContent( testString );
    224                 equal( editor.getContent(), testString, text );
     224                assert.equal( editor.getContent(), testString, text );
    225225
    226226                text = 'align on caption elements';
    227227                testString = '<table><caption align="left">test</caption><tbody><tr><td>test</td></tr></tbody></table>';
    228228                editor.setContent( testString );
    229                 equal( editor.getContent(), testString, text );
     229                assert.equal( editor.getContent(), testString, text );
    230230
    231231                text = 'align, char, charoff, valign, width on col elements';
    232232                testString = '<table><colgroup><col width="100" align="left" char="a" charoff="1" valign="top" /><col /></colgroup><tbody><tr><td>test</td><td>test</td></tr></tbody></table>';
    233233                editor.setContent( testString );
    234                 ok( hasAttr( editor.getContent(), { col: 'align char charoff valign width' } ), text );
     234                assert.ok( hasAttr( editor.getContent(), { col: 'align char charoff valign width' } ), text );
    235235
    236236                text = 'align on div, h1—h6, input, legend, p elements';
    237237                testString = '<div align="left">1</div><h3 align="left">1</h3><p align="left">1</p><form><fieldset><legend align="left">test</legend><input type="text" align="left" /></fieldset></form>';
    238238                editor.setContent( testString );
    239                 equal( editor.getContent(), testString, text );
     239                assert.equal( editor.getContent(), testString, text );
    240240
    241241                text = 'compact on dl elements';
    242242                testString = '<dl compact="compact"><dd>1</dd></dl>';
    243243                editor.setContent( testString );
    244                 equal( editor.getContent(), testString, text );
     244                assert.equal( editor.getContent(), testString, text );
    245245
    246246                text = 'align, hspace, vspace on embed elements';
    247247                testString = '<p><embed width="100" height="100" vspace="5" hspace="5" align="left"></embed></p>';
    248248                editor.setContent( testString );
    249                 ok( hasAttr( editor.getContent(), { embed: 'align hspace vspace' } ), text );
     249                assert.ok( hasAttr( editor.getContent(), { embed: 'align hspace vspace' } ), text );
    250250
    251251                text = 'align, noshade, size, width on hr elements';
    252252                testString = '<hr align="left" noshade="noshade" size="1" width="100" />';
    253253                editor.setContent( testString );
    254                 ok( hasAttr( editor.getContent(), { hr: 'align noshade size width' } ), text );
     254                assert.ok( hasAttr( editor.getContent(), { hr: 'align noshade size width' } ), text );
    255255
    256256                text = 'align, frameborder, marginheight, marginwidth, scrolling on iframe elements';
    257257                testString = '<p><iframe width="100" height="100" frameborder="1" marginwidth="5" marginheight="5" scrolling="" align="left"></iframe></p>';
    258258                editor.setContent( testString );
    259                 ok( hasAttr( editor.getContent(), { iframe: 'align frameborder marginheight marginwidth scrolling' } ), text );
     259                assert.ok( hasAttr( editor.getContent(), { iframe: 'align frameborder marginheight marginwidth scrolling' } ), text );
    260260
    261261                text = 'type on li elements';
    262262                testString = '<ul><li type="disc">test</li></ul>';
    263263                editor.setContent( testString );
    264                 equal( editor.getContent(), testString, text );
     264                assert.equal( editor.getContent(), testString, text );
    265265
    266266                text = 'align, border, hspace, vspace on object elements';
    267267                testString = '<p><object width="100" height="100" border="1" vspace="5" hspace="5" align="left"></object></p>';
    268268                editor.setContent( testString );
    269                 ok( hasAttr( editor.getContent(), { object: 'align border hspace vspace' } ), text );
     269                assert.ok( hasAttr( editor.getContent(), { object: 'align border hspace vspace' } ), text );
    270270
    271271                text = 'compact on ol elements';
    272272                testString = '<ol compact="compact"><li>test</li></ol>';
    273273                editor.setContent( testString );
    274                 equal( editor.getContent(), testString, text );
     274                assert.equal( editor.getContent(), testString, text );
    275275
    276276                text = 'compact, type on ul elements';
    277277                testString = '<ul type="disc" compact="compact"><li>test</li></ul>';
    278278                editor.setContent( testString );
    279                 ok( hasAttr( editor.getContent(), { ul: 'compact type' } ), text );
     279                assert.ok( hasAttr( editor.getContent(), { ul: 'compact type' } ), text );
    280280
    281281                text = 'width on pre elements';
    282282                testString = '<pre width="100">1</pre>';
    283283                editor.setContent( testString );
    284                 equal( editor.getContent(), testString, text );
     284                assert.equal( editor.getContent(), testString, text );
    285285        });
    286286
    287287} )( window.jQuery, window.QUnit, window.tinymce );
  • tests/qunit/wp-includes/js/wp-api.js

     
    11/* global wp, JSON */
    22( function( QUnit ) {
    3         module( 'wpapi' );
     3        QUnit.module( 'wpapi' );
    44
    55        QUnit.test( 'API Loaded correctly', function( assert ) {
    66                var done = assert.async();
     
    122122
    123123        _.each( modelsWithIdsClassNames, function( className ) {
    124124
    125                 QUnit.test( 'Checking ' + className + ' model.' , function( assert ) {
     125                QUnit.test( 'Checking ' + className + ' model.', function( assert ) {
    126126                        var done = assert.async();
    127127
    128128                        assert.expect( 2 );
     
    160160
    161161        _.each( modelsWithIndexes, function( className ) {
    162162
    163                 QUnit.test( 'Testing ' + className + ' model.' , function( assert ) {
     163                QUnit.test( 'Testing ' + className + ' model.', function( assert ) {
    164164                        var done = assert.async();
    165165
    166166                        assert.expect( 2 );
     
    296296
    297297                // Check that we have and can get each model type.
    298298                _.each( customClasses, function( className ) {
    299                         QUnit.test( 'Checking ' + className + ' class name.' , function( assert ) {
     299                        QUnit.test( 'Checking ' + className + ' class name.', function( assert ) {
    300300                                var done = assert.async();
    301301
    302302                                assert.expect( 2 );
     
    316316
    317317        // Check connecting to a second URL.
    318318        wp.api.loadPromise.done( function() {
    319                 QUnit.test( 'Checking connecting to a remote url.' , function( assert ) {
     319                QUnit.test( 'Checking connecting to a remote url.', function( assert ) {
    320320                        var done = assert.async();
    321321
    322322                        wp.api.init({