Mooeypoo has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/357124 )
Change subject: RCFilters: Add UriProcessor and unit tests for URL manipulations ...................................................................... RCFilters: Add UriProcessor and unit tests for URL manipulations Bug: T166974 Change-Id: I066c33a01770b7d8026aa3e039af5fe5d9c4cdf9 --- M resources/Resources.php M resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js M resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js A resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js M tests/qunit/QUnitTestResources.php A tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js 6 files changed, 646 insertions(+), 187 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/24/357124/1 diff --git a/resources/Resources.php b/resources/Resources.php index c4baab7..dd62a84 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -1749,6 +1749,7 @@ 'resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.SavedQueriesModel.js', 'resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.ChangesListViewModel.js', 'resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js', + 'resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js', ], 'dependencies' => [ 'oojs', diff --git a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js index 59c0a19..dd698cd 100644 --- a/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js +++ b/resources/src/mediawiki.rcfilters/dm/mw.rcfilters.dm.FilterGroup.js @@ -75,8 +75,8 @@ var subsetNames = [], filterItem = new mw.rcfilters.dm.FilterItem( filter.name, model, { group: model.getName(), - label: mw.msg( filter.label ), - description: mw.msg( filter.description ), + label: filter.label ? mw.msg( filter.label ) : filter.name, + description: filter.description ? mw.msg( filter.description ) : '', cssClass: filter.cssClass } ); diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js index 897162a..09c889e 100644 --- a/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.Controller.js @@ -13,7 +13,7 @@ this.savedQueriesModel = savedQueriesModel; this.requestCounter = 0; this.baseFilterState = {}; - this.emptyParameterState = {}; + this.uriProcessor = null; this.initializing = false; }; @@ -26,7 +26,7 @@ * @param {Array} filterStructure Filter definition and structure for the model */ mw.rcfilters.Controller.prototype.initialize = function ( filterStructure ) { - var parsedSavedQueries, validParameterNames, baseParams, + var parsedSavedQueries, useDataFromServer = false, uri = new mw.Uri(), $changesList = $( '.mw-changeslist' ).first().contents(); @@ -35,14 +35,9 @@ this.filtersModel.initializeFilters( filterStructure ); this._buildBaseFilterState(); - this._buildEmptyParameterState(); - validParameterNames = Object.keys( this._getEmptyParameterState() ) - .filter( function ( param ) { - // Remove 'highlight' parameter from this check; - // if it's the only parameter in the URL we still - // want to consider the URL 'empty' for defaults to load - return param !== 'highlight'; - } ); + this.uriProcessor = new mw.rcfilters.UriProcessor( + this.filtersModel + ); try { parsedSavedQueries = JSON.parse( mw.user.options.get( 'rcfilters-saved-queries' ) || '{}' ); @@ -67,11 +62,7 @@ // Defaults should only be applied on load (if necessary) // or on request this.initializing = true; - if ( - Object.keys( uri.query ).some( function ( parameter ) { - return validParameterNames.indexOf( parameter ) > -1; - } ) - ) { + if ( this.uriProcessor.isQueryValidForLoad( uri.query ) ) { // There are parameters in the url, update model state this.updateStateBasedOnUrl(); useDataFromServer = true; @@ -85,25 +76,7 @@ this.applySavedQuery( this.savedQueriesModel.getDefault() ); useDataFromServer = false; } else { - baseParams = this.filtersModel.getDefaultParams(); - if ( uri.query.urlversion === '2' ) { - baseParams = {}; - } - - this._updateModelState( - $.extend( - true, - baseParams, - // We've ignored the highlight and invert parameters for - // the sake of seeing whether we need to apply defaults - but - // while we do load the defaults, we still want to retain - // the actual value given in the URL for it on top of the - // defaults - { - highlight: String( Number( uri.query.highlight ) ) - } - ) - ); + this.uriProcessor.updateModelBasedOnQuery( uri.query ); // In this case, there's no need to re-request the AJAX call // the initial data will be processed because we are getting // exactly what the server produced for us @@ -126,7 +99,7 @@ * Reset to default filters */ mw.rcfilters.Controller.prototype.resetToDefaults = function () { - this._updateModelState( $.extend( true, { highlight: '0' }, this._getDefaultParams() ) ); + this.uriProcessor.updateModelBasedOnQuery( this._getDefaultParams() ); this.updateChangesList(); }; @@ -409,24 +382,6 @@ }; /** - * Build an empty representation of the parameters, where all parameters - * are either set to '0' or '' depending on their type. - * This must run during initialization, before highlights are set. - */ - mw.rcfilters.Controller.prototype._buildEmptyParameterState = function () { - var emptyParams = this.filtersModel.getParametersFromFilters( {} ), - emptyHighlights = this.filtersModel.getHighlightParameters(); - - this.emptyParameterState = $.extend( - true, - {}, - emptyParams, - emptyHighlights, - { highlight: '0' } - ); - }; - - /** * Get an object representing the base filter state of both * filters and highlights. The structure is similar to what we use * to store each query in the saved queries object: @@ -444,22 +399,6 @@ */ mw.rcfilters.Controller.prototype._getBaseFilterState = function () { return this.baseFilterState; - }; - - /** - * Get an object representing the base state of parameters - * and highlights. The structure is similar to what we use - * to store each query in the saved queries object: - * { - * param1: "value", - * param2: "value1|value2" - * } - * - * @return {Object} Object representing the base state of - * parameters and highlights - */ - mw.rcfilters.Controller.prototype._getEmptyParameterState = function () { - return this.emptyParameterState; }; /** @@ -526,11 +465,7 @@ * without adding an history entry. */ mw.rcfilters.Controller.prototype.replaceUrl = function () { - window.history.replaceState( - { tag: 'rcfilters' }, - document.title, - this._getUpdatedUri().toString() - ); + mw.rcfilters.UriProcessor.static.replaceState( this._getUpdatedUri() ); }; /** @@ -538,22 +473,7 @@ * on current URL values. */ mw.rcfilters.Controller.prototype.updateStateBasedOnUrl = function () { - var uri = new mw.Uri(), - base = this.filtersModel.getDefaultParams(); - - // Check whether we are dealing with urlversion=2 - // If we are, we do not merge the initial request with - // defaults. Not having urlversion=2 means we need to - // reproduce the server-side request and merge the - // requested parameters (or starting state) with the - // wiki default. - // Any subsequent change of the URL through the RCFilters - // system will receive 'urlversion=2' - if ( uri.query.urlversion === '2' ) { - base = {}; - } - - this._updateModelState( $.extend( true, {}, base, uri.query ) ); + this.uriProcessor.updateModelBasedOnQuery( new mw.Uri().query ); this.updateChangesList(); }; @@ -578,35 +498,40 @@ }; /** - * Update the model state from given the given parameters. + * Get an object representing the default parameter state, whether + * it is from the model defaults or from the saved queries. * - * This is an internal method, and should only be used from inside - * the controller. - * - * @param {Object} parameters Object representing the parameters for - * filters and highlights + * @return {Object} Default parameters */ - mw.rcfilters.Controller.prototype._updateModelState = function ( parameters ) { - // Update filter states - this.filtersModel.toggleFiltersSelected( - this.filtersModel.getFiltersFromParameters( - parameters - ) + mw.rcfilters.Controller.prototype._getDefaultParams = function () { + var data, queryHighlights, + savedParams = {}, + savedHighlights = {}, + defaultSavedQueryItem = this.savedQueriesModel.getItemByID( this.savedQueriesModel.getDefault() ); + + if ( mw.config.get( 'wgStructuredChangeFiltersEnableSaving' ) && + defaultSavedQueryItem ) { + + data = defaultSavedQueryItem.getData(); + + queryHighlights = data.highlights || {}; + savedParams = this.filtersModel.getParametersFromFilters( data.filters || {} ); + + // Translate highlights to parameters + savedHighlights.highlight = String( Number( queryHighlights.highlight ) ); + $.each( queryHighlights, function ( filterName, color ) { + if ( filterName !== 'highlights' ) { + savedHighlights[ filterName + '_color' ] = color; + } + } ); + + return $.extend( true, {}, savedParams, savedHighlights ); + } + + return $.extend( + { highlight: '0' }, + this.filtersModel.getDefaultParams() ); - - // Update highlight state - this.filtersModel.toggleHighlight( !!Number( parameters.highlight ) ); - this.filtersModel.getItems().forEach( function ( filterItem ) { - var color = parameters[ filterItem.getName() + '_color' ]; - if ( color ) { - filterItem.setHighlightColor( color ); - } else { - filterItem.clearHighlightColor(); - } - } ); - - // Check all filter interactions - this.filtersModel.reassessFilterInteractions(); }; /** @@ -620,48 +545,21 @@ * @param {Object} [params] Extra parameters to add to the API call */ mw.rcfilters.Controller.prototype._updateURL = function ( params ) { - var currentFilterState, updatedFilterState, updatedUri, - uri = new mw.Uri(), - notEquivalent = function ( obj1, obj2 ) { - var keys = Object.keys( obj1 ).concat( Object.keys( obj2 ) ); - return keys.some( function ( key ) { - return obj1[ key ] != obj2[ key ]; // eslint-disable-line eqeqeq - } ); - }; + var currentUri = new mw.Uri(), + updatedUri = this._getUpdatedUri(); - params = params || {}; - - updatedUri = this._getUpdatedUri(); - updatedUri.extend( params ); - - // Compare states instead of parameters - // This will allow us to always have a proper check of whether - // the requested new url is one to change or not, regardless of - // actual parameter visibility/representation in the URL - currentFilterState = this.filtersModel.getFiltersFromParameters( uri.query ); - updatedFilterState = this.filtersModel.getFiltersFromParameters( updatedUri.query ); - // Include highlight states - $.extend( true, - currentFilterState, - this.filtersModel.extractHighlightValues( uri.query ), - { highlight: !!Number( uri.query.highlight ) } - ); - $.extend( true, - updatedFilterState, - this.filtersModel.extractHighlightValues( updatedUri.query ), - { highlight: !!Number( updatedUri.query.highlight ) } - ); + updatedUri.extend( params || {} ); if ( - uri.query.urlversion !== '2' || - notEquivalent( currentFilterState, updatedFilterState ) + this.uriProcessor.getVersion( currentUri.query ) !== 2 || + this.uriProcessor.isNewState( currentUri.query, updatedUri.query ) ) { if ( this.initializing ) { // Initially, when we just build the first page load // out of defaults, we want to replace the history - window.history.replaceState( { tag: 'rcfilters' }, document.title, updatedUri.toString() ); + mw.rcfilters.UriProcessor.static.replaceState( updatedUri ); } else { - window.history.pushState( { tag: 'rcfilters' }, document.title, updatedUri.toString() ); + mw.rcfilters.UriProcessor.static.pushState( updatedUri ); } } }; @@ -672,41 +570,8 @@ * @return {mw.Uri} Updated Uri */ mw.rcfilters.Controller.prototype._getUpdatedUri = function () { - var uri = new mw.Uri(), - highlightParams = this.filtersModel.getHighlightParameters(), - modelParameters = this.filtersModel.getParametersFromFilters(), - baseParams = this._getEmptyParameterState(); - - // Minimize values of the model parameters; show only the values that - // are non-zero. We assume that all parameters that are not literally - // showing in the URL are set to zero or empty - $.each( modelParameters, function ( paramName, value ) { - if ( baseParams[ paramName ] !== value ) { - uri.query[ paramName ] = value; - } else { - // We need to remove this value from the url - delete uri.query[ paramName ]; - } - } ); - - // highlight params - $.each( highlightParams, function ( paramName, value ) { - // Only output if it is different than the base parameters - if ( baseParams[ paramName ] !== value ) { - uri.query[ paramName ] = value; - } else { - delete uri.query[ paramName ]; - } - } ); - - if ( this.filtersModel.isHighlightEnabled() ) { - uri.query.highlight = '1'; - } else { - delete uri.query.highlight; - } - - // Add the urlversion=2 param for all URLs made by the RCFilters system - uri.query.urlversion = '2'; + var uri = new mw.Uri(); + uri.extend( this.uriProcessor.getUpdatedUriQuery( uri.query ) ); return uri; }; diff --git a/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js b/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js new file mode 100644 index 0000000..35e24ed --- /dev/null +++ b/resources/src/mediawiki.rcfilters/mw.rcfilters.UriProcessor.js @@ -0,0 +1,286 @@ +( function ( mw, $ ) { + /* eslint no-underscore-dangle: "off" */ + /** + * URI Processor for RCFilters + * + * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model + * @param {Object} [config] Configuration options + */ + mw.rcfilters.UriProcessor = function MwRcfiltersController( filtersModel ) { + this.emptyParameterState = {}; + this.filtersModel = filtersModel; + + // Initialize + this._buildEmptyParameterState(); + }; + + /* Initialization */ + OO.initClass( mw.rcfilters.UriProcessor ); + + /* Static methods */ + + /** + * Replace the url history through replaceState + * + * @param {mw.Uri} newUri New URI to replace + */ + mw.rcfilters.UriProcessor.static.replaceState = function ( newUri ) { + window.history.replaceState( + { tag: 'rcfilters' }, + document.title, + newUri.toString() + ); + }; + + /** + * Push the url to history through pushState + * + * @param {mw.Uri} newUri New URI to push + */ + mw.rcfilters.UriProcessor.static.pushState = function ( newUri ) { + window.history.pushState( + { tag: 'rcfilters' }, + document.title, + newUri.toString() + ); + }; + + /* Methods */ + + /** + * Get the version that this URL query is tagged with. + * + * @param {Object} [uriQuery] URI query + * @return {Number} URL version + */ + mw.rcfilters.UriProcessor.prototype.getVersion = function ( uriQuery ) { + uriQuery = uriQuery || new mw.Uri().query; + + return Number( uriQuery.urlversion || 1 ); + }; + + /** + * Update the filters model based on the URI query + * + * @param {Object} uriQuery URI query + */ + mw.rcfilters.UriProcessor.prototype.updateModelBasedOnQuery = function ( uriQuery ) { + var parameters = this.getNormalizedQueryParams( uriQuery || new mw.Uri().query ); + + // Update filter states + this.filtersModel.toggleFiltersSelected( + this.filtersModel.getFiltersFromParameters( + parameters + ) + ); + + // Update highlight state + this.filtersModel.toggleHighlight( !!Number( parameters.highlight ) ); + this.filtersModel.getItems().forEach( function ( filterItem ) { + var color = parameters[ filterItem.getName() + '_color' ]; + if ( color ) { + filterItem.setHighlightColor( color ); + } else { + filterItem.clearHighlightColor(); + } + } ); + + // Check all filter interactions + this.filtersModel.reassessFilterInteractions(); + }; + + /** + * Get an updated mw.Uri object based on the model state + * + * @param {Object} [currentUriQuery] Current Uri object to manipulate + * @return {Object} Updated Uri query + */ + mw.rcfilters.UriProcessor.prototype.getUpdatedUriQuery = function ( currentUriQuery ) { + var paramsFromModel = this.getUriParametersFromModel(); + + currentUriQuery = currentUriQuery || new mw.Uri().query; + + // See if we need to merge defaults + if ( this.getVersion( currentUriQuery ) !== 2 ) { + // Merge with defaults if we don't have url version 2 + $.extend( + true, + currentUriQuery, + this.filtersModel.getDefaultParams() + ); + } + + // Add the urlversion=2 param for all URLs made by the RCFilters system + currentUriQuery.urlversion = '2'; + + // We want to still redo the minimization when we merge model params with the + // initial query, because there may have been a case + // where model parameters overrode the original (default) parameters with a + // state that is now part of the base state. + return this._minimizeQuery( + $.extend( + true, + {}, + currentUriQuery, + paramsFromModel + ) + ); + }; + + /** + * Get parameters representing the current state of the model + * + * @return {Object} Uri query parameters + */ + mw.rcfilters.UriProcessor.prototype.getUriParametersFromModel = function () { + return this._minimizeQuery( $.extend( + true, + {}, + this.filtersModel.getParametersFromFilters(), + this.filtersModel.getHighlightParameters(), + { highlight: String( Number( this.filtersModel.isHighlightEnabled() ) ) } + ) ); + }; + + /** + * Remove all parameters that have the same value as the base state + * + * @private + * @param {Object} uriQuery Current uri query + * @return {Object} Minimized query + */ + mw.rcfilters.UriProcessor.prototype._minimizeQuery = function ( uriQuery ) { + var baseParams = this.getEmptyParameterState(); + + $.each( uriQuery, function ( paramName, paramValue ) { + if ( baseParams[ paramName ] === paramValue ) { + // Remove parameter from query + delete uriQuery[ paramName ]; + } + } ); + + return uriQuery; + }; + /** + * Compare two URI queries to decide whether they are different + * enough to represent a new state. + * + * @param {Object} currentUriQuery Current Uri query + * @param {Object} updatedUriQuery Updated Uri query + * @return {boolean} This is a new state + */ + mw.rcfilters.UriProcessor.prototype.isNewState = function ( currentUriQuery, updatedUriQuery ) { + var currentFilterState, updatedFilterState, + notEquivalent = function ( obj1, obj2 ) { + var keys = Object.keys( obj1 ).concat( Object.keys( obj2 ) ); + return keys.some( function ( key ) { + return obj1[ key ] != obj2[ key ]; // eslint-disable-line eqeqeq + } ); + }; + + // Compare states instead of parameters + // This will allow us to always have a proper check of whether + // the requested new url is one to change or not, regardless of + // actual parameter visibility/representation in the URL + currentFilterState = this._buildFullFilterState( currentUriQuery ); + updatedFilterState = this._buildFullFilterState( updatedUriQuery ); + + return notEquivalent( currentFilterState, updatedFilterState ); + }; + + /** + * Check whether the given query has parameters that are + * recognized as parameters we should load the system with + * + * @param {mw.Uri} [uriQuery] Given URI query + * @return {boolean} Query contains valid recognized parameters + */ + mw.rcfilters.UriProcessor.prototype.isQueryValidForLoad = function ( uriQuery ) { + var anyValidInUrl, + validParameterNames = Object.keys( this.getEmptyParameterState() ) + .filter( function ( param ) { + // Remove 'highlight' parameter from this check; + // if it's the only parameter in the URL we still + // want to consider the URL 'empty' for defaults to load + return param !== 'highlight'; + } ); + + uriQuery = uriQuery || new mw.Uri().query; + + anyValidInUrl = Object.keys( uriQuery ).some( function ( parameter ) { + return validParameterNames.indexOf( parameter ) > -1; + } ); + + // URL version 2 is allowed to be empty or within nonrecognized params + return anyValidInUrl || ( this.getVersion( uriQuery ) === 2 ); + }; + + /** + * Get the adjusted URI params based on the url version + * If the urlversion is not 2, the parameters are merged with + * the model's defaults. + * + * @param {Object} uriQuery Current URI query + * @return {Object} Normalized parameters + */ + mw.rcfilters.UriProcessor.prototype.getNormalizedQueryParams = function ( uriQuery ) { + // Check whether we are dealing with urlversion=2 + // If we are, we do not merge the initial request with + // defaults. Not having urlversion=2 means we need to + // reproduce the server-side request and merge the + // requested parameters (or starting state) with the + // wiki default. + // Any subsequent change of the URL through the RCFilters + // system will receive 'urlversion=2' + var base = this.getVersion( uriQuery ) === 2 ? + {} : + this.filtersModel.getDefaultParams(); + + return this._minimizeQuery( $.extend( true, base, uriQuery, { urlversion: '2' } ) ); + }; + + /** + * Get the representation of an empty parameter state + * + * @return {Object} Empty parameter state + */ + mw.rcfilters.UriProcessor.prototype.getEmptyParameterState = function () { + return this.emptyParameterState; + }; + + /** + * Build the full filter state based on parameters + * + * @private + * @param {Object} uriQuery Given URI query + * @return {Object} Full filter state representing the URI query + */ + mw.rcfilters.UriProcessor.prototype._buildFullFilterState = function ( uriQuery ) { + return $.extend( true, + {}, + this.filtersModel.getFiltersFromParameters( uriQuery ), + this.filtersModel.extractHighlightValues( uriQuery ), + { highlight: !!Number( uriQuery.highlight ) } + ); + }; + + /** + * Build an empty representation of the parameters, where all parameters + * are either set to '0' or '' depending on their type. + * This must run during initialization, before highlights are set. + * + * @private + */ + mw.rcfilters.UriProcessor.prototype._buildEmptyParameterState = function () { + var emptyParams = this.filtersModel.getParametersFromFilters( {} ), + emptyHighlights = this.filtersModel.getHighlightParameters(); + + this.emptyParameterState = $.extend( + true, + {}, + emptyParams, + emptyHighlights, + { highlight: '0' } + ); + }; +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/QUnitTestResources.php b/tests/qunit/QUnitTestResources.php index 53362c4..ee3cd5b 100644 --- a/tests/qunit/QUnitTestResources.php +++ b/tests/qunit/QUnitTestResources.php @@ -94,6 +94,7 @@ 'tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js', 'tests/qunit/suites/resources/mediawiki.rcfilters/dm.FiltersViewModel.test.js', 'tests/qunit/suites/resources/mediawiki.rcfilters/dm.FilterItem.test.js', + 'tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.language.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.cldr.test.js', 'tests/qunit/suites/resources/mediawiki/mediawiki.cookie.test.js', diff --git a/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js b/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js new file mode 100644 index 0000000..0b398ea --- /dev/null +++ b/tests/qunit/suites/resources/mediawiki.rcfilters/UriProcessor.test.js @@ -0,0 +1,306 @@ +/* eslint-disable camelcase */ +( function ( mw, $ ) { + var mockFilterStructure = [ { + name: 'group1', + title: 'Group 1', + type: 'send_unselected_if_any', + filters: [ + { name: 'filter1', default: true }, + { name: 'filter2' } + ] + }, { + name: 'group2', + title: 'Group 2', + type: 'send_unselected_if_any', + filters: [ + { name: 'filter3' }, + { name: 'filter4', default: true } + ] + }, { + name: 'group3', + title: 'Group 3', + type: 'string_options', + filters: [ + { name: 'filter5' }, + { name: 'filter6' } + ] + } ], + minimalDefaultParams = { + filter1: '1', + filter4: '1' + }; + + QUnit.module( 'mediawiki.rcfilters - UriProcessor' ); + + QUnit.test( 'getVersion', function ( assert ) { + var uriProcessor = new mw.rcfilters.UriProcessor( new mw.rcfilters.dm.FiltersViewModel() ); + + assert.ok( + uriProcessor.getVersion( { param1: 'foo', urlversion: '2' } ), + 2, + 'Retrieving the version from the URI query' + ); + + assert.ok( + uriProcessor.getVersion( { param1: 'foo' } ), + 1, + 'Getting version 1 if no version is specified' + ); + } ); + + QUnit.test( 'updateModelBasedOnQuery', function ( assert ) { + var uriProcessor, + filtersModel = new mw.rcfilters.dm.FiltersViewModel(); + + filtersModel.initializeFilters( mockFilterStructure ); + uriProcessor = new mw.rcfilters.UriProcessor( filtersModel ); + + uriProcessor.updateModelBasedOnQuery( {} ); + assert.deepEqual( + uriProcessor.getUriParametersFromModel(), + minimalDefaultParams, + 'Version 1: Empty url query sets model to defaults' + ); + + uriProcessor.updateModelBasedOnQuery( { urlversion: '2' } ); + assert.deepEqual( + uriProcessor.getUriParametersFromModel(), + {}, + 'Version 2: Empty url query sets model to all-false' + ); + + uriProcessor.updateModelBasedOnQuery( { filter1: '1', urlversion: '2' } ); + assert.deepEqual( + uriProcessor.getUriParametersFromModel(), + { filter1: '1' }, + 'Parameters in Uri query set parameter value in the model' + ); + + uriProcessor.updateModelBasedOnQuery( { highlight: '1', group1__filter1_color: 'c1', urlversion: '2' } ); + assert.deepEqual( + uriProcessor.getUriParametersFromModel(), + { + highlight: '1', + group1__filter1_color: 'c1' + }, + 'Highlight parameters in Uri query set highlight state in the model' + ); + } ); + + QUnit.test( 'getUpdatedUriQuery', function ( assert ) { + var uriProcessor, + filtersModel = new mw.rcfilters.dm.FiltersViewModel(); + + filtersModel.initializeFilters( mockFilterStructure ); + uriProcessor = new mw.rcfilters.UriProcessor( filtersModel ); + + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( {} ), + $.extend( true, { urlversion: '2' }, minimalDefaultParams ), + 'Initially empty URI query without urlversion=2 results with defaults plus urlversion=2' + ); + + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( { urlversion: '2' } ), + { urlversion: '2' }, + 'Initially empty URI query with urlversion=2 does not merge defaults' + ); + + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( { filter1: '0' } ), + { + filter1: '1', + filter4: '1', + urlversion: '2' + }, + 'When getting an updated URL (with merging defaults) we consider only filter state and not query params.' + ); + + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( { filter1: '1', urlversion: '2' } ), + { + filter1: '1', + urlversion: '2' + }, + 'When getting an updated URL (without merging defaults) we consider only filter state and not query params.' + ); + + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( { foo: 'bar', urlversion: '2' } ), + { + foo: 'bar', + urlversion: '2' + }, + 'URI retains all unrecognized parameters' + ); + + filtersModel.toggleFiltersSelected( { + // Setting filter1 as true is actually + // setting filter2 **parameter** as true + group1__filter1: true + } ); + + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( { urlversion: '2' } ), + { + filter2: '1', + urlversion: '2' + }, + 'When initializing the URL (version 2) we output parameters representing filter state.' + ); + assert.deepEqual( + uriProcessor.getUpdatedUriQuery( {} ), + { + filter1: '1', + filter2: '1', + filter4: '1', + urlversion: '2' + }, + 'When initializing the URL (not version 2) we output parameters representing filter state with defaults.' + ); + } ); + + QUnit.test( 'isNewState', function ( assert ) { + var uriProcessor, + filtersModel = new mw.rcfilters.dm.FiltersViewModel(), + cases = [ + { + states: { + curr: {}, + new: {} + }, + result: false, + message: 'Empty objects are not new state.' + }, + { + states: { + curr: { filter1: '1' }, + new: { filter1: '0' } + }, + result: true, + message: 'Nulified parameter is a new state' + }, + { + states: { + curr: { filter1: '1' }, + new: { filter1: '1', filter2: '1' } + }, + result: true, + message: 'Added parameters are a new state' + }, + { + states: { + curr: { filter1: '1' }, + new: { filter1: '1', filter2: '0' } + }, + result: false, + message: 'Added null parameters are not a new state (normalizing equals old state)' + }, + { + states: { + curr: { filter1: '1' }, + new: { filter1: '1', foo: 'bar' } + }, + result: false, + // Note that the RCFilters system can't add unrecognized parameters + // so this is a test for sanity check and to make sure that on initialization + // we recognize correct states of the url and not lose unrecognized params + // (See also testsgetUpdatedUriQuery for preservation of unrecognized params) + message: 'Added unrecognized parameters are not a new state' + } + ]; + + filtersModel.initializeFilters( mockFilterStructure ); + uriProcessor = new mw.rcfilters.UriProcessor( filtersModel ); + + cases.forEach( function ( testCase ) { + assert.equal( + uriProcessor.isNewState( testCase.states.curr, testCase.states.new ), + testCase.result, + testCase.message + ); + } ); + } ); + + QUnit.test( 'isQueryValidForLoad', function ( assert ) { + var uriProcessor, + filtersModel = new mw.rcfilters.dm.FiltersViewModel(), + cases = [ + { + query: {}, + result: false, + message: 'Empty query is not valid for load.' + }, + { + query: { highlight: '1' }, + result: false, + message: 'Highlight state alone is not valid for load' + }, + { + query: { urlversion: '2' }, + result: true, + message: 'urlversion=2 state alone is valid for load as an empty state' + }, + { + query: { filter1: '1', foo: 'bar' }, + result: true, + message: 'Existence of recognized parameters makes the query valid for load' + }, + { + query: { foo: 'bar', debug: true }, + result: false, + message: 'Only unrecognized parameters makes the query invalid for load' + } + ]; + + filtersModel.initializeFilters( mockFilterStructure ); + uriProcessor = new mw.rcfilters.UriProcessor( filtersModel ); + + cases.forEach( function ( testCase ) { + assert.equal( + uriProcessor.isQueryValidForLoad( testCase.query ), + testCase.result, + testCase.message + ); + } ); + } ); + + QUnit.test( 'getNormalizedQueryParams', function ( assert ) { + var uriProcessor, + filtersModel = new mw.rcfilters.dm.FiltersViewModel(), + cases = [ + { + query: {}, + result: $.extend( true, { urlversion: '2' }, minimalDefaultParams ), + message: 'Empty query returns defaults (urlversion 1).' + }, + { + query: { urlversion: '2' }, + result: { urlversion: '2' }, + message: 'Empty query returns empty (urlversion 2)' + }, + { + query: { filter1: '0' }, + result: { urlversion: '2', filter4: '1' }, + message: 'urlversion 1 returns query that overrides defaults' + }, + { + query: { filter3: '1' }, + result: { urlversion: '2', filter1: '1', filter4: '1', filter3: '1' }, + message: 'urlversion 1 with an extra param value returns query that is joined with defaults' + } + ]; + + filtersModel.initializeFilters( mockFilterStructure ); + uriProcessor = new mw.rcfilters.UriProcessor( filtersModel ); + + cases.forEach( function ( testCase ) { + assert.deepEqual( + uriProcessor.getNormalizedQueryParams( testCase.query ), + testCase.result, + testCase.message + ); + } ); + } ); + +}( mediaWiki, jQuery ) ); -- To view, visit https://gerrit.wikimedia.org/r/357124 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I066c33a01770b7d8026aa3e039af5fe5d9c4cdf9 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Mooeypoo <mor...@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits