Daniel Werner has uploaded a new change for review. https://gerrit.wikimedia.org/r/76741
Change subject: (bug 48611) valueview expert test setups won't create instances anymore ...................................................................... (bug 48611) valueview expert test setups won't create instances anymore Instead, the actual valueview instances will only created as part of the actual tests. This will prevent from side-effects happening during expert's initialization. E.g. widgets being initialized, causing global DOM nodes to be added to the body which would interfere with other tests assumptions about the state of the DOM. Enhanced qunit.parameterize to take an array of functions returning objects as test cases for this to work conveniently. Change-Id: Id3af61c498f4cd71e31b3424e71cce8d2eab4a83 --- M DataValues/resources/qunit.parameterize/qunit.parameterize.js M ValueView/tests/qunit/jquery.valueview/valueview.tests.testExpert.js 2 files changed, 91 insertions(+), 70 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues refs/changes/41/76741/1 diff --git a/DataValues/resources/qunit.parameterize/qunit.parameterize.js b/DataValues/resources/qunit.parameterize/qunit.parameterize.js index 24daae7..ad3ad73 100644 --- a/DataValues/resources/qunit.parameterize/qunit.parameterize.js +++ b/DataValues/resources/qunit.parameterize/qunit.parameterize.js @@ -23,24 +23,35 @@ /** * @param {[]|Function} testCases An Array (or a callback returning such an object) which - * has to hold different Objects where each defines what will be passed to tests which - * will be registered to the Object returned by the function. By providing a callback, + * has to hold different Objects and/or Functions returning an Object. Each of the + * objects results in a test case variation and defines what will be passed to test + * cases registered to the Object returned by the function. By providing a callback, * the parameters provided to the tests will be created separately for each test. This * allows to provide instances which involve state without running into problems when * manipulating state in one test case but expecting initial state in another one. * @return {Object} */ return function(testCases) { - var createTest = function(methodName, title, expected, callback, parameters) { + var createTest = function( methodName, title, expected, callback, paramsOrProvider ) { + var finalCallback = function( assert ) { + var parameters = QUnit.is( 'function', paramsOrProvider ) + ? paramsOrProvider() + : paramsOrProvider; + + return callback.call( this, parameters, assert ); + }; + QUnit[methodName]( title, expected, - function(assert) { return callback.call(this, parameters, assert); } + finalCallback ); }; var iterateTestCases = function(methodName, title, expected, callback) { - if (!testCases) return; + if (!testCases) { + return; + } if (!callback) { callback = expected; @@ -52,16 +63,16 @@ : testCases; for (var i = 0; i < testTestCases.length; ++i) { - var parameters = testTestCases[i]; + var testTestCase = testTestCases[i]; var testCaseTitle = title; - if (parameters.title) { - testCaseTitle += "[" + parameters.title + "]"; + if( testTestCase.title ) { + testCaseTitle += "[" + testTestCase.title + "]"; } - createTest(methodName, testCaseTitle, expected, callback, parameters); + createTest( methodName, testCaseTitle, expected, callback, testTestCase ); } - } + }; return { test : function(title, expected, callback) { @@ -73,7 +84,7 @@ iterateTestCases("asyncTest", title, expected, callback); return this; } - } - } + }; + }; }( QUnit ) ); diff --git a/ValueView/tests/qunit/jquery.valueview/valueview.tests.testExpert.js b/ValueView/tests/qunit/jquery.valueview/valueview.tests.testExpert.js index 2fa30e0..6d4dac0 100644 --- a/ValueView/tests/qunit/jquery.valueview/valueview.tests.testExpert.js +++ b/ValueView/tests/qunit/jquery.valueview/valueview.tests.testExpert.js @@ -46,67 +46,78 @@ } validRawValues.push( null ); - // Used as source for expertProvider(). - var expertDefinitions = [ - { - title: 'instance without notifier', - args: [ - $( '<span/>' ), - new valueview.MockViewState() - ] - }, { - title: 'instance with notifier', - args: [ - $( '<div/>' ), - new valueview.MockViewState(), - new Notifier() - ] - }, { - title: 'instance with ViewState of disabled view', - args: [ - $( '<div/>' ), - new valueview.MockViewState( { isDisabled: true } ), - new Notifier() - ] - } - ]; + // Used as source for expertProviders. + function createExpertDefinitions() { + return [ + { + title: 'instance without notifier', + args: [ + $( '<span/>' ), + new valueview.MockViewState() + ] + }, { + title: 'instance with notifier', + args: [ + $( '<div/>' ), + new valueview.MockViewState(), + new Notifier() + ] + }, { + title: 'instance with ViewState of disabled view', + args: [ + $( '<div/>' ), + new valueview.MockViewState( { isDisabled: true } ), + new Notifier() + ] + } + ]; + } /** - * Returns an array of Objects holding the following fields: - * - title: Description of the set. + * Returns an array of Functions. Each function returns an object with the following fields + * when executed: * - expert: A valueview Expert instance. * - constructorArgs: Arguments used to construct the expert given in the "expert" field. * - * @returns {Array} + * Each function has a "title" field which describes the expert instance mentioned above. + * + * @type {Function[]} */ - function expertProvider() { - var experts = []; + function createExpertProviders() { + return $.map( createExpertDefinitions(), function( definition ) { + // provide a setup function for cases parameter creation instead of creating a case + // definition object directly. If we would do the later, the expert would already + // be created and in some cases create conflicts with other tests since some experts + // immediately instantiate certain widgets (e.g. inputextender). + var caseSetup = function() { + var $viewPort = definition.args[0], + viewState = definition.args[1], + notifier = definition.args[2]; - $.each( expertDefinitions, function( i, definition ) { - var $viewPort = definition.args[0], - viewState = definition.args[1], - notifier = definition.args[2]; + var expert = new Expert( $viewPort, viewState, notifier ); - var expert = new Expert( $viewPort, viewState, notifier ); - - experts.push( { - title: definition.title, - expert: expert, - constructorArgs: definition.args - } ); + return { + expert: expert, + constructorArgs: definition.args + }; + }; + caseSetup.title = definition.title; + return caseSetup; } ); - - return experts; } - var expertCases = QUnit.cases( - // provide fresh instances for each test - function() { - return expertProvider(); - } - ); + var expertCases = QUnit.cases( createExpertProviders ); - expertCases.test( 'constructor', function( args, assert ) { + // We always have to destroy experts so all widgets used by them get destroyed as well in case + // they add something to the body. + function expertCasesTestAndCleanup( description, testFn ) { + expertCases.test( description, function( args, assert ) { + testFn( args, assert ); + args.expert.destroy(); + } ); + } + + expertCasesTestAndCleanup( 'constructor', function( args, assert ) { assert.ok( args.expert instanceof Expert, 'expert successfully constructed' @@ -130,7 +141,7 @@ ); } ); - expertCases.test( 'valueCharacteristics', function( args, assert ) { + expertCasesTestAndCleanup( 'valueCharacteristics', function( args, assert ) { var valueCharacteristics = args.expert.valueCharacteristics(); assert.ok( @@ -139,7 +150,7 @@ ); } ); - expertCases.test( 'viewState', function( args, assert ) { + expertCasesTestAndCleanup( 'viewState', function( args, assert ) { var viewState = args.expert.viewState(); assert.ok( viewState instanceof valueview.ViewState, @@ -147,7 +158,7 @@ ); } ); - expertCases.test( 'rawValueCompare: Test of different raw values', function( args, assert ) { + expertCasesTestAndCleanup( 'rawValueCompare: Test of different raw values', function( args, assert ) { $.each( validRawValues, function( i, testValue ) { $.each( validRawValues, function( j, otherValue ) { var successExpected = i === j; @@ -162,7 +173,7 @@ } ); } ); - expertCases.test( 'rawValueCompare: Works with 2nd parameter omitted', function( args, assert ) { + expertCasesTestAndCleanup( 'rawValueCompare: Works with 2nd parameter omitted', function( args, assert ) { var expert = args.expert; assert.ok( @@ -178,7 +189,7 @@ ); } ); - expertCases.test( 'rawValue: initial value', function( args, assert ) { + expertCasesTestAndCleanup( 'rawValue: initial value', function( args, assert ) { assert.equal( args.expert.rawValue(), null, @@ -186,7 +197,7 @@ ); } ); - expertCases.test( 'rawValue: setting and getting raw value', function( args, assert ) { + expertCasesTestAndCleanup( 'rawValue: setting and getting raw value', function( args, assert ) { var expert = args.expert; $.each( validRawValues, function( i, testValue ) { @@ -203,8 +214,7 @@ } ); } ); - expertCases.test( 'rawValue: setting value to unknown value', function( args, assert ) { - + expertCasesTestAndCleanup( 'rawValue: setting value to unknown value', function( args, assert ) { $.each( unknownRawValues, function( i, testValue ) { QUnit.stop(); @@ -360,7 +370,7 @@ ] }, /** - * Defines what kind of value parser the Expert's parse() function is exptected to return. + * Defines what kind of value parser the Expert's parse() function is expected to return. * @type Function Constructor implementation of valueParsers.ValueParser. */ relatedValueParser: null -- To view, visit https://gerrit.wikimedia.org/r/76741 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Id3af61c498f4cd71e31b3424e71cce8d2eab4a83 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/DataValues Gerrit-Branch: master Gerrit-Owner: Daniel Werner <daniel.wer...@wikimedia.de> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits