From 07032195b7fc671083e2e5a793a3846c0e15890d Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Sat, 12 Mar 2016 02:15:14 +0000 Subject: [PATCH] mediawiki.api.test: Refactor to embrace async 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 --- .../mediawiki.api/mediawiki.api.test.js | 194 +++++++++--------- 1 file changed, 93 insertions(+), 101 deletions(-) diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js index 394f3bd56b..520db428c0 100644 --- a/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.api.test.js @@ -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(); } } ) ); @@ -29,25 +24,27 @@ 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' }, @@ -57,11 +54,11 @@ 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 ) { @@ -75,11 +72,10 @@ 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 ) { @@ -87,11 +83,10 @@ 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 ) { @@ -100,29 +95,30 @@ 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" } } }' @@ -137,7 +133,8 @@ } ) .fail( function ( err ) { assert.equal( err, '', 'API error' ); - } ); + } ) + .always( firstDone ); api.getToken( 'testuncached' ) .done( function ( token ) { @@ -145,13 +142,13 @@ } ) .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' }, @@ -162,42 +159,40 @@ ) ); // 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; @@ -208,7 +203,7 @@ ] ) ); - api.getToken( 'testbad' ) + return api.getToken( 'testbad' ) .then( function () { api.badToken( 'testbad' ); return api.getToken( 'testbad' ); @@ -221,7 +216,6 @@ } ); QUnit.test( 'badToken( legacy )', function ( assert ) { - QUnit.expect( 2 ); var api = new mw.Api( { ajax: { url: '/badTokenLegacy/api.php' } } ), test = this; @@ -232,7 +226,7 @@ ] ) ); - api.getToken( 'options' ) + return api.getToken( 'options' ) .then( function () { api.badToken( 'options' ); return api.getToken( 'options' ); @@ -245,7 +239,6 @@ } ); 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' }, @@ -259,65 +252,65 @@ } } ); - 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' }, @@ -343,14 +336,13 @@ // - 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(); @@ -378,16 +370,16 @@ // - 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' } } ); } ); } ); -- 2.20.1