Merge "Replace Linker::link() with LinkRenderer in some special pages"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 30 Nov 2016 10:04:09 +0000 (10:04 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 30 Nov 2016 10:04:09 +0000 (10:04 +0000)
15 files changed:
RELEASE-NOTES-1.29
includes/DefaultSettings.php
includes/Setup.php
includes/cache/MessageCache.php
maintenance/protect.php
resources/src/mediawiki/api.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.category.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.messages.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.options.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.parse.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.upload.test.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.api.watch.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.loader.test.js

index 5b5640f..ab52544 100644 (file)
@@ -54,6 +54,8 @@ changes to languages because of Phabricator reports.
 === Other changes in 1.29 ===
 * Database::getSearchEngine() (deprecated in 1.28) was removed. Use
   SearchEngineFactory::getSearchEngineClass() instead.
+* $wgSessionsInMemcached (deprecated in 1.20) was removed. No replacement is
+  required as all sessions are stored in Object Cache now.
 
 == Compatibility ==
 
index eb778b5..5557dca 100644 (file)
@@ -2365,13 +2365,6 @@ $wgMainStash = 'db-replicated';
  */
 $wgParserCacheExpireTime = 86400;
 
-/**
- * Deprecated alias for $wgSessionsInObjectCache.
- *
- * @deprecated since 1.20; Use $wgSessionsInObjectCache
- */
-$wgSessionsInMemcached = true;
-
 /**
  * @deprecated since 1.27, session data is always stored in object cache.
  */
index 357c76d..9f722af 100644 (file)
@@ -462,7 +462,7 @@ if ( $wgMaximalPasswordLength !== false ) {
 }
 
 // Backwards compatibility warning
-if ( !$wgSessionsInObjectCache && !$wgSessionsInMemcached ) {
+if ( !$wgSessionsInObjectCache ) {
        wfDeprecated( '$wgSessionsInObjectCache = false', '1.27' );
        if ( $wgSessionHandler ) {
                wfDeprecated( '$wgSessionsHandler', '1.27' );
index 0c2f9de..4e6b2fd 100644 (file)
@@ -496,6 +496,7 @@ class MessageCache {
                        if ( $text === false ) {
                                // Failed to fetch data; possible ES errors?
                                // Store a marker to fetch on-demand as a workaround...
+                               // TODO Use a differnt marker
                                $entry = '!TOO BIG';
                                wfDebugLog(
                                        'MessageCache',
@@ -510,6 +511,10 @@ class MessageCache {
 
                $cache['VERSION'] = MSG_CACHE_VERSION;
                ksort( $cache );
+
+               # Hash for validating local cache (APC). No need to take into account
+               # messages larger than $wgMaxMsgCacheEntrySize, since those are only
+               # stored and fetched from memcache.
                $cache['HASH'] = md5( serialize( $cache ) );
                $cache['EXPIRY'] = wfTimestamp( TS_MW, time() + $this->mExpiry );
 
@@ -568,7 +573,8 @@ class MessageCache {
                }
 
                ScopedCallback::consume( $scopedLock );
-               // Relay the purge to APC and other DCs
+               // Relay the purge. Touching this check key expires cache contents
+               // and local cache (APC) validation hash across all datacenters.
                $this->wanCache->touchCheckKey( wfMemcKey( 'messages', $code ) );
 
                // Also delete cached sidebar... just in case it is affected
index 31b2101..f6bb253 100644 (file)
@@ -41,8 +41,8 @@ class Protect extends Maintenance {
        }
 
        public function execute() {
-               $userName = $this->getOption( 'u', false );
-               $reason = $this->getOption( 'r', '' );
+               $userName = $this->getOption( 'user', false );
+               $reason = $this->getOption( 'reason', '' );
 
                $cascade = $this->hasOption( 'cascade' );
 
index b9e05c3..0c08ca4 100644 (file)
                                                        } );
                                                }
 
-                                               // Different error, pass on to let caller handle the error code
-                                               return this;
+                                               // Let caller handle the error code
+                                               return $.Deferred().rejectWith( this, arguments );
                                        }
                                );
                        } ).promise( { abort: function () {
                        promiseGroup = promises[ this.defaults.ajax.url ];
                        d = promiseGroup && promiseGroup[ type + 'Token' ];
 
+                       if ( !promiseGroup ) {
+                               promiseGroup = promises[ this.defaults.ajax.url ] = {};
+                       }
+
                        if ( !d ) {
                                apiPromise = this.get( {
                                        action: 'query',
                                                // Clear promise. Do not cache errors.
                                                delete promiseGroup[ type + 'Token' ];
 
-                                               // Pass on to allow the caller to handle the error
-                                               return this;
+                                               // Let caller handle the error code
+                                               return $.Deferred().rejectWith( this, arguments );
                                        } )
                                        // Attach abort handler
                                        .promise( { abort: apiPromise.abort } );
 
                                // Store deferred now so that we can use it again even if it isn't ready yet
-                               if ( !promiseGroup ) {
-                                       promiseGroup = promises[ this.defaults.ajax.url ] = {};
-                               }
                                promiseGroup[ type + 'Token' ] = d;
                        }
 
index 9d0fdf5..1676130 100644 (file)
@@ -3,16 +3,10 @@
                setup: function () {
                        this.server = this.sandbox.useFakeServer();
                        this.server.respondImmediately = true;
-                       this.clock = this.sandbox.useFakeTimers();
-               },
-               teardown: function () {
-                       // https://github.com/jquery/jquery/issues/2453
-                       this.clock.tick();
                }
        } ) );
 
-       QUnit.test( 'origin is included in GET requests', function ( assert ) {
-               QUnit.expect( 1 );
+       QUnit.test( 'origin is included in GET requests', 1, function ( assert ) {
                var api = new mw.ForeignApi( '//localhost:4242/w/api.php' );
 
                this.server.respond( function ( request ) {
                        request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
                } );
 
-               api.get( {} );
+               return api.get( {} );
        } );
 
-       QUnit.test( 'origin is included in POST requests', function ( assert ) {
-               QUnit.expect( 2 );
+       QUnit.test( 'origin is included in POST requests', 2, function ( assert ) {
                var api = new mw.ForeignApi( '//localhost:4242/w/api.php' );
 
                this.server.respond( function ( request ) {
@@ -33,7 +26,7 @@
                        request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
                } );
 
-               api.post( {} );
+               return api.post( {} );
        } );
 
 }( mediaWiki ) );
index a0c7daf..a79bff6 100644 (file)
@@ -2,29 +2,24 @@
        QUnit.module( 'mediawiki.api.category', QUnit.newMwEnvironment( {
                setup: function () {
                        this.server = this.sandbox.useFakeServer();
+                       this.server.respondImmediately = true;
                }
        } ) );
 
-       QUnit.test( '.getCategoriesByPrefix()', function ( assert ) {
-               QUnit.expect( 1 );
+       QUnit.test( '.getCategoriesByPrefix()', 1, function ( assert ) {
+               this.server.respondWith( [ 200, { 'Content-Type': 'application/json' },
+                       '{ "query": { "allpages": [ ' +
+                               '{ "title": "Category:Food" },' +
+                               '{ "title": "Category:Fool Supermarine S.6" },' +
+                               '{ "title": "Category:Fools" }' +
+                               '] } }'
+               ] );
 
-               var api = new mw.Api();
-
-               api.getCategoriesByPrefix( 'Foo' ).done( function ( matches ) {
+               return new mw.Api().getCategoriesByPrefix( 'Foo' ).then( function ( matches ) {
                        assert.deepEqual(
                                matches,
                                [ 'Food', 'Fool Supermarine S.6', 'Fools' ]
                        );
                } );
-
-               this.server.respond( function ( req ) {
-                       req.respond( 200, { 'Content-Type': 'application/json' },
-                               '{ "query": { "allpages": [ ' +
-                                       '{ "title": "Category:Food" },' +
-                                       '{ "title": "Category:Fool Supermarine S.6" },' +
-                                       '{ "title": "Category:Fools" }' +
-                                       '] } }'
-                       );
-               } );
        } );
 }( mediaWiki ) );
index 5880962..d8b5db8 100644 (file)
@@ -2,14 +2,21 @@
        QUnit.module( 'mediawiki.api.messages', QUnit.newMwEnvironment( {
                setup: function () {
                        this.server = this.sandbox.useFakeServer();
+                       this.server.respondImmediately = true;
                }
        } ) );
 
-       QUnit.test( '.getMessages()', function ( assert ) {
-               QUnit.expect( 1 );
+       QUnit.test( '.getMessages()', 1, function ( assert ) {
+               this.server.respondWith( /ammessages=foo%7Cbaz/, [
+                       200,
+                       { 'Content-Type': 'application/json' },
+                       '{ "query": { "allmessages": [' +
+                               '{ "name": "foo", "content": "Foo bar" },' +
+                               '{ "name": "baz", "content": "Baz Quux" }' +
+                               '] } }'
+               ] );
 
-               var api = new mw.Api();
-               api.getMessages( [ 'foo', 'baz' ] ).then( function ( messages ) {
+               return new mw.Api().getMessages( [ 'foo', 'baz' ] ).then( function ( messages ) {
                        assert.deepEqual(
                                messages,
                                {
                                }
                        );
                } );
-
-               this.server.respond( /ammessages=foo%7Cbaz/, [
-                       200,
-                       { 'Content-Type': 'application/json' },
-                       '{ "query": { "allmessages": [' +
-                               '{ "name": "foo", "content": "Foo bar" },' +
-                               '{ "name": "baz", "content": "Baz Quux" }' +
-                               '] } }'
-               ] );
        } );
 }( mediaWiki ) );
index 0797f32..7ed1875 100644 (file)
@@ -2,14 +2,12 @@
        QUnit.module( 'mediawiki.api.options', QUnit.newMwEnvironment( {
                setup: function () {
                        this.server = this.sandbox.useFakeServer();
+                       this.server.respondImmediately = true;
                }
        } ) );
 
-       QUnit.test( 'saveOption', function ( assert ) {
-               QUnit.expect( 2 );
-
-               var
-                       api = new mw.Api(),
+       QUnit.test( 'saveOption', 2, function ( assert ) {
+               var api = new mw.Api(),
                        stub = this.sandbox.stub( mw.Api.prototype, 'saveOptions' );
 
                api.saveOption( 'foo', 'bar' );
@@ -18,9 +16,7 @@
                assert.deepEqual( stub.getCall( 0 ).args, [ { foo: 'bar' } ], '#saveOptions called correctly' );
        } );
 
-       QUnit.test( 'saveOptions without Unit Separator', function ( assert ) {
-               QUnit.expect( 13 );
-
+       QUnit.test( 'saveOptions without Unit Separator', 13, function ( assert ) {
                var api = new mw.Api( { useUS: false } );
 
                // We need to respond to the request for token first, otherwise the other requests won't be sent
                                '{ "query": { "tokens": { "csrftoken": "+\\\\" } } }' ]
                );
 
-               api.saveOptions( {} ).done( function () {
-                       assert.ok( true, 'Request completed: empty case' );
-               } );
-               api.saveOptions( { foo: 'bar' } ).done( function () {
-                       assert.ok( true, 'Request completed: simple' );
-               } );
-               api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
-                       assert.ok( true, 'Request completed: two options' );
-               } );
-               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
-                       assert.ok( true, 'Request completed: not bundleable' );
-               } );
-               api.saveOptions( { foo: null } ).done( function () {
-                       assert.ok( true, 'Request completed: reset an option' );
-               } );
-               api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
-                       assert.ok( true, 'Request completed: reset an option, not bundleable' );
-               } );
-
                // Requests are POST, match requestBody instead of url
                this.server.respond( function ( request ) {
                        switch ( request.requestBody ) {
                                        assert.ok( false, 'Unexpected request: ' + request.requestBody );
                        }
                } );
-       } );
 
-       QUnit.test( 'saveOptions with Unit Separator', function ( assert ) {
-               QUnit.expect( 14 );
+               return QUnit.whenPromisesComplete(
+                       api.saveOptions( {} ).then( function () {
+                               assert.ok( true, 'Request completed: empty case' );
+                       } ),
+                       api.saveOptions( { foo: 'bar' } ).then( function () {
+                               assert.ok( true, 'Request completed: simple' );
+                       } ),
+                       api.saveOptions( { foo: 'bar', baz: 'quux' } ).then( function () {
+                               assert.ok( true, 'Request completed: two options' );
+                       } ),
+                       api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).then( function () {
+                               assert.ok( true, 'Request completed: not bundleable' );
+                       } ),
+                       api.saveOptions( { foo: null } ).then( function () {
+                               assert.ok( true, 'Request completed: reset an option' );
+                       } ),
+                       api.saveOptions( { 'foo|bar=quux': null } ).then( function () {
+                               assert.ok( true, 'Request completed: reset an option, not bundleable' );
+                       } )
+               );
+       } );
 
+       QUnit.test( 'saveOptions with Unit Separator', 14, function ( assert ) {
                var api = new mw.Api( { useUS: true } );
 
                // We need to respond to the request for token first, otherwise the other requests won't be sent
                                '{ "query": { "tokens": { "csrftoken": "+\\\\" } } }' ]
                );
 
-               api.saveOptions( {} ).done( function () {
-                       assert.ok( true, 'Request completed: empty case' );
-               } );
-               api.saveOptions( { foo: 'bar' } ).done( function () {
-                       assert.ok( true, 'Request completed: simple' );
-               } );
-               api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
-                       assert.ok( true, 'Request completed: two options' );
-               } );
-               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
-                       assert.ok( true, 'Request completed: bundleable with unit separator' );
-               } );
-               api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', 'baz=baz': 'quux' } ).done( function () {
-                       assert.ok( true, 'Request completed: not bundleable with unit separator' );
-               } );
-               api.saveOptions( { foo: null } ).done( function () {
-                       assert.ok( true, 'Request completed: reset an option' );
-               } );
-               api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
-                       assert.ok( true, 'Request completed: reset an option, not bundleable' );
-               } );
-
                // Requests are POST, match requestBody instead of url
                this.server.respond( function ( request ) {
                        switch ( request.requestBody ) {
                                        assert.ok( false, 'Unexpected request: ' + request.requestBody );
                        }
                } );
+
+               return QUnit.whenPromisesComplete(
+                       api.saveOptions( {} ).done( function () {
+                               assert.ok( true, 'Request completed: empty case' );
+                       } ),
+                       api.saveOptions( { foo: 'bar' } ).done( function () {
+                               assert.ok( true, 'Request completed: simple' );
+                       } ),
+                       api.saveOptions( { foo: 'bar', baz: 'quux' } ).done( function () {
+                               assert.ok( true, 'Request completed: two options' );
+                       } ),
+                       api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', baz: 'quux' } ).done( function () {
+                               assert.ok( true, 'Request completed: bundleable with unit separator' );
+                       } ),
+                       api.saveOptions( { foo: 'bar|quux', bar: 'a|b|c', 'baz=baz': 'quux' } ).done( function () {
+                               assert.ok( true, 'Request completed: not bundleable with unit separator' );
+                       } ),
+                       api.saveOptions( { foo: null } ).done( function () {
+                               assert.ok( true, 'Request completed: reset an option' );
+                       } ),
+                       api.saveOptions( { 'foo|bar=quux': null } ).done( function () {
+                               assert.ok( true, 'Request completed: reset an option, not bundleable' );
+                       } )
+               );
        } );
 }( mediaWiki ) );
index dc0cff4..7d27352 100644 (file)
@@ -2,42 +2,44 @@
        QUnit.module( 'mediawiki.api.parse', QUnit.newMwEnvironment( {
                setup: function () {
                        this.server = this.sandbox.useFakeServer();
+                       this.server.respondImmediately = true;
                }
        } ) );
 
-       QUnit.test( 'Hello world', function ( assert ) {
-               QUnit.expect( 3 );
+       QUnit.test( '.parse( string )', function ( assert ) {
+               this.server.respondWith( /action=parse.*&text='''Hello(\+|%20)world'''/, [ 200,
+                       { 'Content-Type': 'application/json' },
+                       '{ "parse": { "text": "<p><b>Hello world</b></p>" } }'
+               ] );
 
-               var api = new mw.Api();
-
-               api.parse( '\'\'\'Hello world\'\'\'' ).done( function ( html ) {
+               return new mw.Api().parse( '\'\'\'Hello world\'\'\'' ).done( function ( html ) {
                        assert.equal( html, '<p><b>Hello world</b></p>', 'Parse wikitext by string' );
                } );
+       } );
 
-               api.parse( {
+       QUnit.test( '.parse( Object.toString )', function ( assert ) {
+               this.server.respondWith( /action=parse.*&text='''Hello(\+|%20)world'''/, [ 200,
+                       { 'Content-Type': 'application/json' },
+                       '{ "parse": { "text": "<p><b>Hello world</b></p>" } }'
+               ] );
+
+               return new mw.Api().parse( {
                        toString: function () {
                                return '\'\'\'Hello world\'\'\'';
                        }
                } ).done( function ( html ) {
                        assert.equal( html, '<p><b>Hello world</b></p>', 'Parse wikitext by toString object' );
                } );
+       } );
 
-               this.server.respondWith( /action=parse.*&text='''Hello\+world'''/, function ( request ) {
-                       request.respond( 200, { 'Content-Type': 'application/json' },
-                               '{ "parse": { "text": "<p><b>Hello world</b></p>" } }'
-                       );
-               } );
+       QUnit.test( '.parse( mw.Title )', function ( assert ) {
+               this.server.respondWith( /action=parse.*&page=Earth/, [ 200,
+                       { 'Content-Type': 'application/json' },
+                       '{ "parse": { "text": "<p><b>Earth</b> is a planet.</p>" } }'
+               ] );
 
-               api.parse( new mw.Title( 'Earth' ) ).done( function ( html ) {
+               return new mw.Api().parse( new mw.Title( 'Earth' ) ).done( function ( html ) {
                        assert.equal( html, '<p><b>Earth</b> is a planet.</p>', 'Parse page by Title object'  );
                } );
-
-               this.server.respondWith( /action=parse.*&page=Earth/, function ( request ) {
-                       request.respond( 200, { 'Content-Type': 'application/json' },
-                               '{ "parse": { "text": "<p><b>Earth</b> is a planet.</p>" } }'
-                       );
-               } );
-
-               this.server.respond();
        } );
 }( mediaWiki ) );
index 10fcd5d..b1bd12b 100644 (file)
@@ -1,8 +1,7 @@
 ( function ( mw, $ ) {
        QUnit.module( 'mediawiki.api.upload', QUnit.newMwEnvironment( {} ) );
 
-       QUnit.test( 'Basic functionality', function ( assert ) {
-               QUnit.expect( 2 );
+       QUnit.test( 'Basic functionality', 2, function ( assert ) {
                var api = new mw.Api();
                assert.ok( api.upload );
                assert.throws( function () {
@@ -10,8 +9,7 @@
                } );
        } );
 
-       QUnit.test( 'Set up iframe upload', function ( assert ) {
-               QUnit.expect( 5 );
+       QUnit.test( 'Set up iframe upload', 5, function ( assert ) {
                var $iframe, $form, $input,
                        api = new mw.Api();
 
index 64a5184..8641469 100644 (file)
@@ -2,37 +2,45 @@
        QUnit.module( 'mediawiki.api.watch', QUnit.newMwEnvironment( {
                setup: function () {
                        this.server = this.sandbox.useFakeServer();
+                       this.server.respondImmediately = true;
                }
        } ) );
 
-       QUnit.test( '.watch()', function ( assert ) {
-               QUnit.expect( 4 );
-
-               var api = new mw.Api();
-
-               // Ensure we don't mistake a single item array for a single item and vice versa.
-               // The query parameter in request is the same either way (separated by pipe).
-               api.watch( 'Foo' ).done( function ( item ) {
-                       assert.equal( item.title, 'Foo' );
-               } );
-
-               api.watch( [ 'Foo' ] ).done( function ( items ) {
-                       assert.equal( items[ 0 ].title, 'Foo' );
+       QUnit.test( '.watch( string )', function ( assert ) {
+               this.server.respond( function ( req ) {
+                       // Match POST requestBody
+                       if ( /action=watch.*&titles=Foo(&|$)/.test( req.requestBody ) ) {
+                               req.respond( 200, { 'Content-Type': 'application/json' },
+                                       '{ "watch": [ { "title": "Foo", "watched": true, "message": "<b>Added</b>" } ] }'
+                               );
+                       }
                } );
 
-               api.watch( [ 'Foo', 'Bar' ] ).done( function ( items ) {
-                       assert.equal( items[ 0 ].title, 'Foo' );
-                       assert.equal( items[ 1 ].title, 'Bar' );
+               return new mw.Api().watch( 'Foo' ).done( function ( item ) {
+                       assert.equal( item.title, 'Foo' );
                } );
+       } );
 
-               // Requests are POST, match requestBody instead of url
+       // Ensure we don't mistake a single item array for a single item and vice versa.
+       // The query parameter in request is the same either way (separated by pipe).
+       QUnit.test( '.watch( Array ) - single', function ( assert ) {
                this.server.respond( function ( req ) {
+                       // Match POST requestBody
                        if ( /action=watch.*&titles=Foo(&|$)/.test( req.requestBody ) ) {
                                req.respond( 200, { 'Content-Type': 'application/json' },
                                        '{ "watch": [ { "title": "Foo", "watched": true, "message": "<b>Added</b>" } ] }'
                                );
                        }
+               } );
+
+               return new mw.Api().watch( [ 'Foo' ] ).done( function ( items ) {
+                       assert.equal( items[ 0 ].title, 'Foo' );
+               } );
+       } );
 
+       QUnit.test( '.watch( Array ) - multi', function ( assert ) {
+               this.server.respond( function ( req ) {
+                       // Match POST requestBody
                        if ( /action=watch.*&titles=Foo%7CBar/.test( req.requestBody ) ) {
                                req.respond( 200, { 'Content-Type': 'application/json' },
                                        '{ "watch": [ ' +
                                );
                        }
                } );
+
+               return new mw.Api().watch( [ 'Foo', 'Bar' ] ).done( function ( items ) {
+                       assert.equal( items[ 0 ].title, 'Foo' );
+                       assert.equal( items[ 1 ].title, 'Bar' );
+               } );
        } );
+
 }( mediaWiki ) );
index 1218137..7a00943 100644 (file)
        QUnit.test( 'Match PHP parser', mw.libs.phpParserData.tests.length, function ( assert ) {
                mw.messages.set( mw.libs.phpParserData.messages );
                var tasks = $.map( mw.libs.phpParserData.tests, function ( test ) {
+                       var done = assert.async();
                        return function ( next, abort ) {
-                               var done = assert.async();
                                getMwLanguage( test.lang )
                                        .then( function ( langClass ) {
                                                mw.config.set( 'wgUserLanguage', test.lang );
                mw.messages.set( 'formatnum-msg', '{{formatnum:$1}}' );
                mw.messages.set( 'formatnum-msg-int', '{{formatnum:$1|R}}' );
                var queue = $.map( formatnumTests, function ( test ) {
+                       var done = assert.async();
                        return function ( next, abort ) {
-                               var done = assert.async();
                                getMwLanguage( test.lang )
                                        .then( function ( langClass ) {
                                                mw.config.set( 'wgUserLanguage', test.lang );
index 92d1326..92ee7dd 100644 (file)
                .done( function () {
                        assert.strictEqual( isAwesomeDone, true, 'test.promise module should\'ve caused isAwesomeDone to be true' );
                        delete mw.loader.testCallback;
-
                } )
                .fail( function () {
                        assert.ok( false, 'Error callback fired while loader.using "test.promise" module' );
        } );
 
        QUnit.test( '.using() Error: Circular dependency', function ( assert ) {
+               var done = assert.async();
+
                mw.loader.register( [
                        [ 'test.circle1', '0', [ 'test.circle2' ] ],
                        [ 'test.circle2', '0', [ 'test.circle3' ] ],
                        function fail( e ) {
                                assert.ok( /Circular/.test( String( e ) ), 'Detect circular dependency' );
                        }
-               );
+               )
+               .always( done );
        } );
 
        QUnit.test( '.load() - Error: Circular dependency', function ( assert ) {
        } );
 
        QUnit.test( '.using() - Error: Unregistered', function ( assert ) {
+               var done = assert.async();
+
                mw.loader.using( 'test.using.unreg' ).then(
                        function done() {
                                assert.ok( false, 'Unexpected resolution, expected error.' );
                        function fail( e ) {
                                assert.ok( /Unknown/.test( String( e ) ), 'Detect unknown dependency' );
                        }
-               );
+               ).always( done );
        } );
 
        QUnit.test( '.load() - Error: Unregistered (ignored)', 0, function ( assert ) {