mediawiki.api.test: Refactor to embrace async
authorTimo Tijhof <krinklemail@gmail.com>
Sat, 12 Mar 2016 02:15:14 +0000 (02:15 +0000)
committerKrinkle <krinklemail@gmail.com>
Mon, 14 Mar 2016 17:17:10 +0000 (17:17 +0000)
This was using lots of synchronous hacks but those don't work in newer
versions of Sinon.JS (and only worked by accident up until now).

The test will still run pretty quick, but it will yield to some extend because
mw.Api and jQuery.ajax both create XHR objects with async=true.

* Remove redundant QUnit.expect() calls (deprecated).
* Make use of QUnit's new Promise-return feature.
  This simplifies some tests by no longer having to call async() or start()
  manually when only dealing with a single promise.

This blocks upgrade of Sinon.JS.

Change-Id: I08b08ea3c3e8b41f4aea50d86b2c9dc0f579ba67

tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js

index 394f3bd..520db42 100644 (file)
@@ -3,11 +3,6 @@
                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();
                }
        } ) );
 
                return sequence( bodies );
        }
 
-       QUnit.test( 'Basic functionality', function ( assert ) {
-               QUnit.expect( 2 );
+       QUnit.test( 'get()', function ( assert ) {
                var api = new mw.Api();
 
                this.server.respond( [ 200, { 'Content-Type': 'application/json' }, '[]' ] );
 
-               api.get( {} )
-                       .done( function ( data ) {
-                               assert.deepEqual( data, [], 'If request succeeds without errors, resolve deferred' );
-                       } );
+               return api.get( {} ).then( function ( data ) {
+                       assert.deepEqual( data, [], 'If request succeeds without errors, resolve deferred' );
+               } );
+       } );
 
-               api.post( {} )
-                       .done( function ( data ) {
-                               assert.deepEqual( data, [], 'Simple POST request' );
-                       } );
+       QUnit.test( 'post()', function ( assert ) {
+               var api = new mw.Api();
+
+               this.server.respond( [ 200, { 'Content-Type': 'application/json' }, '[]' ] );
+
+               return api.post( {} ).then( function ( data ) {
+                       assert.deepEqual( data, [], 'Simple POST request' );
+               } );
        } );
 
        QUnit.test( 'API error', function ( assert ) {
-               QUnit.expect( 1 );
                var api = new mw.Api();
 
                this.server.respond( [ 200, { 'Content-Type': 'application/json' },
                api.get( { action: 'doesntexist' } )
                        .fail( function ( errorCode ) {
                                assert.equal( errorCode, 'unknown_action', 'API error should reject the deferred' );
-                       } );
+                       } )
+                       .always( assert.async() );
        } );
 
        QUnit.test( 'FormData support', function ( assert ) {
-               QUnit.expect( 2 );
                var api = new mw.Api();
 
                this.server.respond( function ( request ) {
                        request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
                } );
 
-               api.post( { action: 'test' }, { contentType: 'multipart/form-data' } );
+               return api.post( { action: 'test' }, { contentType: 'multipart/form-data' } );
        } );
 
        QUnit.test( 'Converting arrays to pipe-separated', function ( assert ) {
-               QUnit.expect( 1 );
                var api = new mw.Api();
 
                this.server.respond( function ( request ) {
                        request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
                } );
 
-               api.get( { test: [ 'foo', 'bar', 'baz' ] } );
+               return api.get( { test: [ 'foo', 'bar', 'baz' ] } );
        } );
 
        QUnit.test( 'Omitting false booleans', function ( assert ) {
-               QUnit.expect( 2 );
                var api = new mw.Api();
 
                this.server.respond( function ( request ) {
                        request.respond( 200, { 'Content-Type': 'application/json' }, '[]' );
                } );
 
-               api.get( { foo: false, bar: true } );
+               return api.get( { foo: false, bar: true } );
        } );
 
        QUnit.test( 'getToken() - cached', function ( assert ) {
-               QUnit.expect( 2 );
-               var api = new mw.Api();
+               var api = new mw.Api(),
+                       test = this;
 
                // Get editToken for local wiki, this should not make
                // a request as it should be retrieved from mw.user.tokens.
-               api.getToken( 'edit' )
-                       .done( function ( token ) {
+               return api.getToken( 'edit' )
+                       .then( function ( token ) {
                                assert.ok( token.length, 'Got a token' );
-                       } )
-                       .fail( function ( err ) {
+                       }, function ( err ) {
                                assert.equal( '', err, 'API error' );
+                       } )
+                       .then( function () {
+                               assert.equal( test.server.requests.length, 0, 'Requests made' );
                        } );
-
-               assert.equal( this.server.requests.length, 0, 'Requests made' );
        } );
 
        QUnit.test( 'getToken() - uncached', function ( assert ) {
-               QUnit.expect( 3 );
-               var api = new mw.Api();
+               var api = new mw.Api(),
+                       firstDone = assert.async(),
+                       secondDone = assert.async();
 
                this.server.respondWith( /type=testuncached/, [ 200, { 'Content-Type': 'application/json' },
                        '{ "query": { "tokens": { "testuncachedtoken": "good" } } }'
                        } )
                        .fail( function ( err ) {
                                assert.equal( err, '', 'API error' );
-                       } );
+                       } )
+                       .always( firstDone );
 
                api.getToken( 'testuncached' )
                        .done( function ( token ) {
                        } )
                        .fail( function ( err ) {
                                assert.equal( err, '', 'API error' );
-                       } );
+                       } )
+                       .always( secondDone );
 
                assert.equal( this.server.requests.length, 1, 'Requests made' );
        } );
 
        QUnit.test( 'getToken() - error', function ( assert ) {
-               QUnit.expect( 2 );
                var api = new mw.Api();
 
                this.server.respondWith( /type=testerror/, sequenceBodies( 200, { 'Content-Type': 'application/json' },
                ) );
 
                // Don't cache error (bug 65268)
-               api.getToken( 'testerror' ).fail( function ( err ) {
-                       assert.equal( err, 'bite-me', 'Expected error' );
-
-                       // Make this request after the first one has finished.
-                       // If we make it simultaneously we still want it to share
-                       // the cache, but as soon as it is fulfilled as error we
-                       // reject it so that the next one tries fresh.
-                       api.getToken( 'testerror' ).done( function ( token ) {
+               return api.getToken( 'testerror' )
+                       .then( null, function ( err ) {
+                               assert.equal( err, 'bite-me', 'Expected error' );
+
+                               return api.getToken( 'testerror' );
+                       } )
+                       .then( function ( token ) {
                                assert.equal( token, 'good', 'The token' );
                        } );
-               } );
        } );
 
        QUnit.test( 'getToken() - deprecated', function ( assert ) {
-               QUnit.expect( 2 );
                // Cache API endpoint from default to avoid cachehit in mw.user.tokens
-               var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } );
+               var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } ),
+                       test = this;
 
                this.server.respondWith( /type=csrf/, [ 200, { 'Content-Type': 'application/json' },
                        '{ "query": { "tokens": { "csrftoken": "csrfgood" } } }'
                ] );
 
                // Get a token of a type that is in the legacy map.
-               api.getToken( 'email' )
+               return api.getToken( 'email' )
                        .done( function ( token ) {
                                assert.equal( token, 'csrfgood', 'Token' );
                        } )
                        .fail( function ( err ) {
                                assert.equal( err, '', 'API error' );
+                       } )
+                       .always( function () {
+                               assert.equal( test.server.requests.length, 1, 'Requests made' );
                        } );
-
-               assert.equal( this.server.requests.length, 1, 'Requests made' );
        } );
 
        QUnit.test( 'badToken()', function ( assert ) {
-               QUnit.expect( 2 );
                var api = new mw.Api(),
                        test = this;
 
                        ]
                ) );
 
-               api.getToken( 'testbad' )
+               return api.getToken( 'testbad' )
                        .then( function () {
                                api.badToken( 'testbad' );
                                return api.getToken( 'testbad' );
        } );
 
        QUnit.test( 'badToken( legacy )', function ( assert ) {
-               QUnit.expect( 2 );
                var api = new mw.Api( { ajax: { url: '/badTokenLegacy/api.php' } } ),
                        test = this;
 
                        ]
                ) );
 
-               api.getToken( 'options' )
+               return api.getToken( 'options' )
                        .then( function () {
                                api.badToken( 'options' );
                                return api.getToken( 'options' );
        } );
 
        QUnit.test( 'postWithToken( tokenType, params )', function ( assert ) {
-               QUnit.expect( 1 );
                var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } );
 
                this.server.respondWith( 'GET', /type=testpost/, [ 200, { 'Content-Type': 'application/json' },
                        }
                } );
 
-               api.postWithToken( 'testpost', { action: 'example', key: 'foo' } )
-                       .done( function ( data ) {
+               return api.postWithToken( 'testpost', { action: 'example', key: 'foo' } )
+                       .then( function ( data ) {
                                assert.deepEqual( data, { example: { foo: 'quux' } } );
                        } );
        } );
 
        QUnit.test( 'postWithToken( tokenType, params with assert )', function ( assert ) {
-               QUnit.expect( 2 );
-               var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } );
+               var api = new mw.Api( { ajax: { url: '/postWithToken/api.php' } } ),
+                       test = this;
 
                this.server.respondWith( /assert=user/, [ 200, { 'Content-Type': 'application/json' },
                        '{ "error": { "code": "assertuserfailed", "info": "Assertion failed" } }'
                ] );
 
-               api.postWithToken( 'testassertpost', { action: 'example', key: 'foo', assert: 'user' } )
-                       .fail( function ( errorCode ) {
+               return api.postWithToken( 'testassertpost', { action: 'example', key: 'foo', assert: 'user' } )
+                       // Cast error to success and vice versa
+                       .then( function ( ) {
+                               return $.Deferred().reject( 'Unexpected success' );
+                       }, function ( errorCode ) {
                                assert.equal( errorCode, 'assertuserfailed', 'getToken fails assert' );
+                               return $.Deferred().resolve();
+                       } )
+                       .then( function () {
+                               assert.equal( test.server.requests.length, 1, 'Requests made' );
                        } );
-
-               assert.equal( this.server.requests.length, 1, 'Requests made' );
        } );
 
        QUnit.test( 'postWithToken( tokenType, params, ajaxOptions )', function ( assert ) {
-               QUnit.expect( 3 );
-               var api = new mw.Api();
+               var api = new mw.Api(),
+                       test = this;
 
                this.server.respond( [ 200, { 'Content-Type': 'application/json' }, '{ "example": "quux" }' ] );
 
-               api.postWithToken(
-                       'edit',
-                       {
-                               action: 'example'
-                       },
-                       {
-                               headers: {
-                                       'X-Foo': 'Bar'
+               return api.postWithToken( 'edit',
+                               { action: 'example' },
+                               {
+                                       headers: {
+                                               'X-Foo': 'Bar'
+                                       }
                                }
-                       }
-               );
+                       )
+                       .then( function () {
+                               assert.equal( test.server.requests[ 0 ].requestHeaders[ 'X-Foo' ], 'Bar', 'Header sent' );
 
-               api.postWithToken(
-                       'edit',
-                       {
-                               action: 'example'
-                       },
-                       function () {
-                               assert.ok( false, 'This parameter cannot be a callback' );
-                       }
-               )
-               .always( function ( data ) {
-                       assert.equal( data.example, 'quux' );
-               } );
+                               return api.postWithToken( 'edit',
+                                       { action: 'example' },
+                                       function () {
+                                               assert.ok( false, 'This parameter cannot be a callback' );
+                                       }
+                               );
+                       } )
+                       .then( function ( data ) {
+                               assert.equal( data.example, 'quux' );
 
-               assert.equal( this.server.requests.length, 2, 'Request made' );
-               assert.equal( this.server.requests[ 0 ].requestHeaders[ 'X-Foo' ], 'Bar', 'Header sent' );
+                               assert.equal( test.server.requests.length, 2, 'Request made' );
+                       } );
        } );
 
        QUnit.test( 'postWithToken() - badtoken', function ( assert ) {
-               QUnit.expect( 1 );
                var api = new mw.Api();
 
                this.server.respondWith( /type=testbadtoken/, sequenceBodies( 200, { 'Content-Type': 'application/json' },
                // - Request: action=example -> badtoken error
                // - Request: new token -> good
                // - Request: action=example -> success
-               api.postWithToken( 'testbadtoken', { action: 'example', key: 'foo' } )
-                       .done( function ( data ) {
+               return api.postWithToken( 'testbadtoken', { action: 'example', key: 'foo' } )
+                       .then( function ( data ) {
                                assert.deepEqual( data, { example: { foo: 'quux' } } );
                        } );
        } );
 
        QUnit.test( 'postWithToken() - badtoken-cached', function ( assert ) {
-               QUnit.expect( 2 );
                var sequenceA,
                        api = new mw.Api();
 
 
                // - Request: new token -> A
                // - Request: action=example
-               api.postWithToken( 'testonce', { action: 'example', key: 'foo' } )
-                       .done( function ( data ) {
+               return api.postWithToken( 'testonce', { action: 'example', key: 'foo' } )
+                       .then( function ( data ) {
                                assert.deepEqual( data, { example: { value: 'A' } } );
-                       } );
 
-               // - Request: action=example w/ token A -> badtoken error
-               // - Request: new token -> B
-               // - Request: action=example w/ token B -> success
-               api.postWithToken( 'testonce', { action: 'example', key: 'bar' } )
-                       .done( function ( data ) {
+                               // - Request: action=example w/ token A -> badtoken error
+                               // - Request: new token -> B
+                               // - Request: action=example w/ token B -> success
+                               return api.postWithToken( 'testonce', { action: 'example', key: 'bar' } );
+                       } )
+                       .then( function ( data ) {
                                assert.deepEqual( data, { example: { value: 'B' } } );
                        } );
        } );