X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=tests%2Fqunit%2Fdata%2Ftestrunner.js;h=d8972ab389b2ff24eecda681c75ea84c624f6639;hb=4e6a6b4871e6895a6b7dc1f634b189e6dd6ccb4f;hp=59432945c26386c729050b62f012ad6aab7574f3;hpb=d3538125dfe64afa94ef68aa000549a03eb49977;p=lhc%2Fweb%2Fwiklou.git diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js index 59432945c2..d8972ab389 100644 --- a/tests/qunit/data/testrunner.js +++ b/tests/qunit/data/testrunner.js @@ -6,18 +6,21 @@ /** * Make a safe copy of localEnv: - * - Creates a copy so that when the same object reference to module hooks is - * used by multipe test hooks, our QUnit.module extension will not wrap the - * callbacks multiple times. Instead, they wrap using a new object. - * - Normalise setup/teardown to avoid having to repeat this in each extension + * - Creates a new object that inherits, instead of modifying the original. + * This prevents recursion in the event that a test suite stores inherits + * hooks object statically and passes it to multiple QUnit.module() calls. + * - Supporting QUnit 1.x 'setup' and 'teardown' hooks * (deprecated in QUnit 1.16, removed in QUnit 2). - * - Strip any other properties. */ function makeSafeEnv( localEnv ) { - return { - beforeEach: localEnv.setup || localEnv.beforeEach, - afterEach: localEnv.teardown || localEnv.afterEach - }; + var wrap = localEnv ? Object.create( localEnv ) : {}; + if ( wrap.setup ) { + wrap.beforeEach = wrap.beforeEach || wrap.setup; + } + if ( wrap.teardown ) { + wrap.afterEach = wrap.afterEach || wrap.teardown; + } + return wrap; } /** @@ -37,7 +40,7 @@ * Configuration */ - // For each test() that is asynchronous, allow this time to pass before + // For each test that is asynchronous, allow this time to pass before // killing the test and assuming timeout failure. QUnit.config.testTimeout = 60 * 1000; @@ -73,13 +76,16 @@ useFakeTimers: false, useFakeServer: false }; - // Extend QUnit.module to provide a Sinon sandbox. + // Extend QUnit.module with: + // - Add support for QUnit 1.x 'setup' and 'teardown' hooks + // - Add a Sinon sandbox to the test context. + // - Add a test fixture to the test context. ( function () { var orgModule = QUnit.module; QUnit.module = function ( name, localEnv, executeNow ) { - var orgBeforeEach, orgAfterEach, orgExecute; + var orgExecute, orgBeforeEach, orgAfterEach; if ( nested ) { - // In a nested module, don't re-run our handlers. + // In a nested module, don't re-add our hooks, QUnit does that already. return orgModule.apply( this, arguments ); } if ( arguments.length === 2 && typeof localEnv === 'function' ) { @@ -98,49 +104,17 @@ }; } - localEnv = localEnv || {}; + localEnv = makeSafeEnv( localEnv ); orgBeforeEach = localEnv.beforeEach; orgAfterEach = localEnv.afterEach; + localEnv.beforeEach = function () { + // Sinon sandbox var config = sinon.getConfig( sinon.config ); config.injectInto = this; sinon.sandbox.create( config ); - if ( orgBeforeEach ) { - return orgBeforeEach.apply( this, arguments ); - } - }; - localEnv.afterEach = function () { - var ret; - if ( orgAfterEach ) { - ret = orgAfterEach.apply( this, arguments ); - } - - this.sandbox.verifyAndRestore(); - return ret; - }; - return orgModule( name, localEnv, executeNow ); - }; - }() ); - - // Extend QUnit.module to provide a fixture element. - ( function () { - var orgModule = QUnit.module; - QUnit.module = function ( name, localEnv, executeNow ) { - var orgBeforeEach, orgAfterEach; - if ( nested ) { - // In a nested module, don't re-run our handlers. - return orgModule.apply( this, arguments ); - } - if ( arguments.length === 2 && typeof localEnv === 'function' ) { - executeNow = localEnv; - localEnv = undefined; - } - - localEnv = localEnv || {}; - orgBeforeEach = localEnv.beforeEach; - orgAfterEach = localEnv.afterEach; - localEnv.beforeEach = function () { + // Fixture element this.fixture = document.createElement( 'div' ); this.fixture.id = 'qunit-fixture'; document.body.appendChild( this.fixture ); @@ -154,29 +128,17 @@ if ( orgAfterEach ) { ret = orgAfterEach.apply( this, arguments ); } - + this.sandbox.verifyAndRestore(); this.fixture.parentNode.removeChild( this.fixture ); return ret; }; - return orgModule( name, localEnv, executeNow ); - }; - }() ); - // Extend QUnit.module to normalise localEnv. - // NOTE: This MUST be the last QUnit.module extension so that the above extensions - // may safely modify the object and assume beforeEach/afterEach. - ( function () { - var orgModule = QUnit.module; - QUnit.module = function ( name, localEnv, executeNow ) { - if ( typeof localEnv === 'object' ) { - localEnv = makeSafeEnv( localEnv ); - } return orgModule( name, localEnv, executeNow ); }; }() ); /** - * Reset mw.config and others to a fresh copy of the live config for each test(), + * Reset mw.config and others to a fresh copy of the live config for each test, * and restore it back to the live one afterwards. * * @param {Object} [localEnv] @@ -239,98 +201,101 @@ } return function ( orgEnv ) { - var localEnv = orgEnv ? makeSafeEnv( orgEnv ) : {}; - // MediaWiki env testing - localEnv.config = orgEnv && orgEnv.config || {}; - localEnv.messages = orgEnv && orgEnv.messages || {}; - - return { - beforeEach: function () { - // Greetings, mock environment! - mw.config = new MwMap(); - mw.config.set( freshConfigCopy( localEnv.config ) ); - mw.messages = new MwMap(); - mw.messages.set( freshMessagesCopy( localEnv.messages ) ); - // Update reference to mw.messages - mw.jqueryMsg.setParserDefaults( { - messages: mw.messages - } ); - - this.suppressWarnings = suppressWarnings; - this.restoreWarnings = restoreWarnings; + var localEnv, orgBeforeEach, orgAfterEach; - // Start tracking ajax requests - $( document ).on( 'ajaxSend', trackAjax ); - - if ( localEnv.beforeEach ) { - return localEnv.beforeEach.apply( this, arguments ); - } - }, + localEnv = makeSafeEnv( orgEnv ); + // MediaWiki env testing + localEnv.config = localEnv.config || {}; + localEnv.messages = localEnv.messages || {}; - afterEach: function () { - var timers, pending, $activeLen, ret; + orgBeforeEach = localEnv.beforeEach; + orgAfterEach = localEnv.afterEach; - if ( localEnv.afterEach ) { - ret = localEnv.afterEach.apply( this, arguments ); - } + localEnv.beforeEach = function () { + // Greetings, mock environment! + mw.config = new MwMap(); + mw.config.set( freshConfigCopy( localEnv.config ) ); + mw.messages = new MwMap(); + mw.messages.set( freshMessagesCopy( localEnv.messages ) ); + // Update reference to mw.messages + mw.jqueryMsg.setParserDefaults( { + messages: mw.messages + } ); + + this.suppressWarnings = suppressWarnings; + this.restoreWarnings = restoreWarnings; + + // Start tracking ajax requests + $( document ).on( 'ajaxSend', trackAjax ); - // Stop tracking ajax requests - $( document ).off( 'ajaxSend', trackAjax ); + if ( orgBeforeEach ) { + return orgBeforeEach.apply( this, arguments ); + } + }; + localEnv.afterEach = function () { + var timers, pending, $activeLen, ret; - // As a convenience feature, automatically restore warnings if they're - // still suppressed by the end of the test. - restoreWarnings(); + if ( orgAfterEach ) { + ret = orgAfterEach.apply( this, arguments ); + } - // Farewell, mock environment! - mw.config = liveConfig; - mw.messages = liveMessages; - // Restore reference to mw.messages - mw.jqueryMsg.setParserDefaults( { - messages: liveMessages + // Stop tracking ajax requests + $( document ).off( 'ajaxSend', trackAjax ); + + // As a convenience feature, automatically restore warnings if they're + // still suppressed by the end of the test. + restoreWarnings(); + + // Farewell, mock environment! + mw.config = liveConfig; + mw.messages = liveMessages; + // Restore reference to mw.messages + mw.jqueryMsg.setParserDefaults( { + messages: liveMessages + } ); + + // Tests should use fake timers or wait for animations to complete + // Check for incomplete animations/requests/etc and throw if there are any. + if ( $.timers && $.timers.length !== 0 ) { + timers = $.timers.length; + $.each( $.timers, function ( i, timer ) { + var node = timer.elem; + mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' + + mw.html.element( node.nodeName.toLowerCase(), $( node ).getAttrs() ) + ); } ); + // Force animations to stop to give the next test a clean start + $.timers = []; + $.fx.stop(); - // Tests should use fake timers or wait for animations to complete - // Check for incomplete animations/requests/etc and throw if there are any. - if ( $.timers && $.timers.length !== 0 ) { - timers = $.timers.length; - $.each( $.timers, function ( i, timer ) { - var node = timer.elem; - mw.log.warn( 'Unfinished animation #' + i + ' in ' + timer.queue + ' queue on ' + - mw.html.element( node.nodeName.toLowerCase(), $( node ).getAttrs() ) - ); - } ); - // Force animations to stop to give the next test a clean start - $.timers = []; - $.fx.stop(); - - throw new Error( 'Unfinished animations: ' + timers ); - } + throw new Error( 'Unfinished animations: ' + timers ); + } - // Test should use fake XHR, wait for requests, or call abort() - $activeLen = $.active; - if ( $activeLen !== undefined && $activeLen !== 0 ) { - pending = ajaxRequests.filter( function ( ajax ) { - return ajax.xhr.state() === 'pending'; - } ); - if ( pending.length !== $activeLen ) { - mw.log.warn( 'Pending requests does not match jQuery.active count' ); - } - // Force requests to stop to give the next test a clean start - ajaxRequests.forEach( function ( ajax, i ) { - mw.log.warn( - 'AJAX request #' + i + ' (state: ' + ajax.xhr.state() + ')', - ajax.options - ); - ajax.xhr.abort(); - } ); - ajaxRequests = []; - - throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' ); + // Test should use fake XHR, wait for requests, or call abort() + $activeLen = $.active; + if ( $activeLen !== undefined && $activeLen !== 0 ) { + pending = ajaxRequests.filter( function ( ajax ) { + return ajax.xhr.state() === 'pending'; + } ); + if ( pending.length !== $activeLen ) { + mw.log.warn( 'Pending requests does not match jQuery.active count' ); } + // Force requests to stop to give the next test a clean start + ajaxRequests.forEach( function ( ajax, i ) { + mw.log.warn( + 'AJAX request #' + i + ' (state: ' + ajax.xhr.state() + ')', + ajax.options + ); + ajax.xhr.abort(); + } ); + ajaxRequests = []; - return ret; + throw new Error( 'Pending AJAX requests: ' + pending.length + ' (active: ' + $activeLen + ')' ); } + + return ret; }; + return localEnv; }; }() ); @@ -526,17 +491,17 @@ } ) ); QUnit.test( 'Setup', function ( assert ) { - assert.equal( mw.html.escape( 'foo' ), 'mocked', 'setup() callback was ran.' ); - assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object applied' ); - assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object applied' ); + assert.strictEqual( mw.html.escape( 'foo' ), 'mocked', 'setup() callback was ran.' ); + assert.strictEqual( mw.config.get( 'testVar' ), 'foo', 'config object applied' ); + assert.strictEqual( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object applied' ); mw.config.set( 'testVar', 'bar' ); mw.messages.set( 'testMsg', 'Bar.' ); } ); QUnit.test( 'Teardown', function ( assert ) { - assert.equal( mw.config.get( 'testVar' ), 'foo', 'config object restored and re-applied after test()' ); - assert.equal( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object restored and re-applied after test()' ); + assert.strictEqual( mw.config.get( 'testVar' ), 'foo', 'config object restored and re-applied after test()' ); + assert.strictEqual( mw.messages.get( 'testMsg' ), 'Foo.', 'messages object restored and re-applied after test()' ); } ); QUnit.test( 'Loader status', function ( assert ) { @@ -610,9 +575,9 @@ QUnit.module( 'testrunner-after', QUnit.newMwEnvironment() ); QUnit.test( 'Teardown', function ( assert ) { - assert.equal( mw.html.escape( '<' ), '<', 'teardown() callback was ran.' ); - assert.equal( mw.config.get( 'testVar' ), null, 'config object restored to live in next module()' ); - assert.equal( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' ); + assert.strictEqual( mw.html.escape( '<' ), '<', 'teardown() callback was ran.' ); + assert.strictEqual( mw.config.get( 'testVar' ), null, 'config object restored to live in next module()' ); + assert.strictEqual( mw.messages.get( 'testMsg' ), null, 'messages object restored to live in next module()' ); } ); QUnit.module( 'testrunner-each', { @@ -628,13 +593,15 @@ mw.html = null; } ); QUnit.test( 'afterEach', function ( assert ) { - assert.equal( mw.html.escape( '<' ), '<', 'afterEach() ran' ); + assert.strictEqual( mw.html.escape( '<' ), '<', 'afterEach() ran' ); } ); QUnit.module( 'testrunner-each-compat', { + // eslint-disable-next-line qunit/no-setup-teardown setup: function () { this.mwHtmlLive = mw.html; }, + // eslint-disable-next-line qunit/no-setup-teardown teardown: function () { mw.html = this.mwHtmlLive; } @@ -644,7 +611,7 @@ mw.html = null; } ); QUnit.test( 'teardown', function ( assert ) { - assert.equal( mw.html.escape( '<' ), '<', 'teardown() ran' ); + assert.strictEqual( mw.html.escape( '<' ), '<', 'teardown() ran' ); } ); // Regression test for 'this.sandbox undefined' error, fixed by @@ -657,4 +624,31 @@ } ); } ); + QUnit.module( 'testrunner-hooks-outer', function () { + var beforeHookWasExecuted = false, + afterHookWasExecuted = false; + QUnit.module( 'testrunner-hooks', { + before: function () { + beforeHookWasExecuted = true; + + // This way we can be sure that module `testrunner-hook-after` will always + // be executed after module `testrunner-hooks` + QUnit.module( 'testrunner-hooks-after' ); + QUnit.test( + '`after` hook for module `testrunner-hooks` was executed', + function ( assert ) { + assert.ok( afterHookWasExecuted ); + } + ); + }, + after: function () { + afterHookWasExecuted = true; + } + } ); + + QUnit.test( '`before` hook was executed', function ( assert ) { + assert.ok( beforeHookWasExecuted ); + } ); + } ); + }( jQuery, mediaWiki, QUnit ) );