Pablo Grass (WMDE) has uploaded a new change for review. (
https://gerrit.wikimedia.org/r/373035 )
Change subject: SearchModel: OO search option records - WIP
......................................................................
SearchModel: OO search option records - WIP
Change search model options to object format.
Separate individual search model behaviors into classes with one concern each.
Ensure file namespace can not be removed while filtype option selected.
Needs incorporation of: I9c1c6ca8c1b6c3fdb2d0c2fb100d8d1b4a38770d
Ticket: T165302
Change-Id: I16f9244fc5f77b71d4eba5bf3eceb1d8904a8ec9
---
M AdvancedSearch.hooks.php
M extension.json
A modules/dm/behavior/ext.advancedSearch.DefaultNamespace.js
A modules/dm/behavior/ext.advancedSearch.FiletypeDimensions.js
A modules/dm/behavior/ext.advancedSearch.FiletypeNamespace.js
M modules/dm/ext.advancedSearch.SearchModel.js
A modules/dm/ext.advancedSearch.SearchOptionList.js
A modules/dm/ext.advancedSearch.SearchOptionModel.js
M modules/ext.advancedSearch.init.js
M modules/ui/ext.advancedSearch.ArbitraryWordInput.js
M modules/ui/ext.advancedSearch.FileTypeSelection.js
M modules/ui/ext.advancedSearch.ImageDimensionInput.js
M modules/ui/ext.advancedSearch.SearchPreview.js
M modules/ui/ext.advancedSearch.TextInput.js
M tests/qunit/dm/SearchModel.test.js
A tests/qunit/dm/SearchOptionList.test.js
A tests/qunit/dm/SearchOptionModel.test.js
A tests/qunit/dm/behavior/DefaultNamespace.test.js
A tests/qunit/dm/behavior/FiletypeDimensions.test.js
A tests/qunit/dm/behavior/FiletypeNamespace.test.js
M tests/qunit/ui/SearchPreview.test.js
21 files changed, 742 insertions(+), 276 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/AdvancedSearch
refs/changes/35/373035/1
diff --git a/AdvancedSearch.hooks.php b/AdvancedSearch.hooks.php
index 98c6c95..f9d3a91 100644
--- a/AdvancedSearch.hooks.php
+++ b/AdvancedSearch.hooks.php
@@ -79,13 +79,23 @@
'tests/qunit/ui/NamespaceFilters.test.js',
'tests/qunit/ui/NamespacePresets.test.js',
'tests/qunit/ui/SearchPreview.test.js',
- 'tests/qunit/dm/SearchModel.test.js'
+ 'tests/qunit/dm/SearchModel.test.js',
+ 'tests/qunit/dm/SearchOptionList.test.js',
+ 'tests/qunit/dm/SearchOptionModel.test.js',
+
'tests/qunit/dm/behavior/DefaultNamespace.test.js',
+
'tests/qunit/dm/behavior/FiletypeDimensions.test.js',
+
'tests/qunit/dm/behavior/FiletypeNamespace.test.js'
],
'dependencies' => [
'ext.advancedSearch.ui.NamespaceFilters',
'ext.advancedSearch.ui.NamespacePresets',
'ext.advancedSearch.ui.SearchPreview',
'ext.advancedSearch.dm.SearchModel',
+ 'ext.advancedSearch.dm.SearchOptionList',
+ 'ext.advancedSearch.dm.SearchOptionModel',
+
'ext.advancedSearch.dm.behavior.DefaultNamespace',
+
'ext.advancedSearch.dm.behavior.FiletypeDimensions',
+
'ext.advancedSearch.dm.behavior.FiletypeNamespace',
'oojs-ui'
],
'localBasePath' => __DIR__,
diff --git a/extension.json b/extension.json
index 615a3e2..1f7966c 100644
--- a/extension.json
+++ b/extension.json
@@ -80,6 +80,11 @@
"oojs-ui.styles.icons-wikimedia",
"ext.advancedSearch.util",
"ext.advancedSearch.dm.SearchModel",
+ "ext.advancedSearch.dm.SearchOptionList",
+ "ext.advancedSearch.dm.SearchOptionModel",
+
"ext.advancedSearch.dm.behavior.DefaultNamespace",
+
"ext.advancedSearch.dm.behavior.FiletypeDimensions",
+
"ext.advancedSearch.dm.behavior.FiletypeNamespace",
"ext.advancedSearch.ui.ArbitraryWordInput",
"ext.advancedSearch.ui.FileTypeSelection",
"ext.advancedSearch.ui.FormState",
@@ -211,7 +216,52 @@
],
"dependencies": [
"oojs-ui",
- "ext.advancedSearch.util"
+ "ext.advancedSearch.util",
+ "ext.advancedSearch.dm.SearchOptionModel",
+ "ext.advancedSearch.dm.SearchOptionList",
+
"ext.advancedSearch.dm.behavior.DefaultNamespace",
+
"ext.advancedSearch.dm.behavior.FiletypeDimensions",
+
"ext.advancedSearch.dm.behavior.FiletypeNamespace"
+ ]
+ },
+ "ext.advancedSearch.dm.SearchOptionModel": {
+ "scripts": [
+
"modules/dm/ext.advancedSearch.SearchOptionModel.js"
+ ],
+ "dependencies": [
+ "oojs-ui"
+ ]
+ },
+ "ext.advancedSearch.dm.SearchOptionList": {
+ "scripts": [
+
"modules/dm/ext.advancedSearch.SearchOptionList.js"
+ ],
+ "dependencies": [
+ "oojs-ui"
+ ]
+ },
+ "ext.advancedSearch.dm.behavior.DefaultNamespace": {
+ "scripts": [
+
"modules/dm/behavior/ext.advancedSearch.DefaultNamespace.js"
+ ],
+ "dependencies": [
+ "oojs-ui"
+ ]
+ },
+ "ext.advancedSearch.dm.behavior.FiletypeDimensions": {
+ "scripts": [
+
"modules/dm/behavior/ext.advancedSearch.FiletypeDimensions.js"
+ ],
+ "dependencies": [
+ "oojs-ui"
+ ]
+ },
+ "ext.advancedSearch.dm.behavior.FiletypeNamespace": {
+ "scripts": [
+
"modules/dm/behavior/ext.advancedSearch.FiletypeNamespace.js"
+ ],
+ "dependencies": [
+ "oojs-ui"
]
},
"ext.advancedSearch.util": {
diff --git a/modules/dm/behavior/ext.advancedSearch.DefaultNamespace.js
b/modules/dm/behavior/ext.advancedSearch.DefaultNamespace.js
new file mode 100644
index 0000000..c19fde9
--- /dev/null
+++ b/modules/dm/behavior/ext.advancedSearch.DefaultNamespace.js
@@ -0,0 +1,27 @@
+( function ( mw ) {
+ 'use strict';
+
+ mw.libs = mw.libs || {};
+ mw.libs.advancedSearch = mw.libs.advancedSearch || {};
+ mw.libs.advancedSearch.dm = mw.libs.advancedSearch.dm || {};
+ mw.libs.advancedSearch.dm.behavior = mw.libs.advancedSearch.dm.behavior
|| {};
+
+ mw.libs.advancedSearch.dm.behavior.DefaultNamespace = function ( store
) {
+ this.store = store;
+
+ var filetypeOption = store.option( 'namespaces' );
+ filetypeOption.on( 'update', this.monitor, [ filetypeOption ],
this );
+ };
+
+ OO.initClass( mw.libs.advancedSearch.dm.behavior.DefaultNamespace );
+
+ /**
+ * @todo Implement & make namespaces a 'normal' SearchModel search
option, port code from SearchModel::setNamespaces()
+ *
+ * @param {mw.libs.advancedSearch.dm.SearchOptionModel} searchOption
+ */
+ mw.libs.advancedSearch.dm.behavior.DefaultNamespace.prototype.monitor =
function ( searchOption ) {
+ searchOption.get();
+ };
+
+}( mediaWiki ) );
diff --git a/modules/dm/behavior/ext.advancedSearch.FiletypeDimensions.js
b/modules/dm/behavior/ext.advancedSearch.FiletypeDimensions.js
new file mode 100644
index 0000000..cebadb9
--- /dev/null
+++ b/modules/dm/behavior/ext.advancedSearch.FiletypeDimensions.js
@@ -0,0 +1,58 @@
+( function ( mw ) {
+ 'use strict';
+
+ mw.libs = mw.libs || {};
+ mw.libs.advancedSearch = mw.libs.advancedSearch || {};
+ mw.libs.advancedSearch.dm = mw.libs.advancedSearch.dm || {};
+ mw.libs.advancedSearch.dm.behavior = mw.libs.advancedSearch.dm.behavior
|| {};
+
+ // Internal constants
+ var FILETYPES_WITH_DIMENSIONS = [
+ 'bitmap',
+ 'video',
+ 'jpeg',
+ 'tiff'
+ ];
+
+ mw.libs.advancedSearch.dm.behavior.FiletypeDimensions = function (
store ) {
+ this.store = store;
+
+ var filetypeOption = store.option( 'filetype' );
+ filetypeOption.on( 'update', this.monitor, [ filetypeOption ],
this );
+ };
+
+ OO.initClass( mw.libs.advancedSearch.dm.behavior.FiletypeDimensions );
+
+ /**
+ *
+ * @param {mw.libs.advancedSearch.dm.SearchOptionModel} searchOption
+ */
+ mw.libs.advancedSearch.dm.behavior.FiletypeDimensions.prototype.monitor
= function ( searchOption ) {
+ var value = searchOption.get();
+
+ if (
!mw.libs.advancedSearch.dm.behavior.FiletypeDimensions.static.filetypeSupportsDimensions(
value ) ) {
+ this.resetFileDimensionOptions();
+ }
+ };
+
+ /**
+ * Reset the file dimension search options
+ */
+
mw.libs.advancedSearch.dm.behavior.FiletypeDimensions.prototype.resetFileDimensionOptions
= function () {
+ this.store.option( 'filew' ).set( [ '>', '' ] );
+ this.store.option( 'fileh' ).set( [ '>', '' ] );
+ };
+
+ /**
+ * Check if the selected file type supports dimensions
+ *
+ * @static
+ * @method
+ * @param {string} filetype
+ * @return {boolean}
+ */
+
mw.libs.advancedSearch.dm.behavior.FiletypeDimensions.static.filetypeSupportsDimensions
= function ( filetype ) {
+ return FILETYPES_WITH_DIMENSIONS.indexOf( filetype ) > -1;
+ };
+
+}( mediaWiki ) );
diff --git a/modules/dm/behavior/ext.advancedSearch.FiletypeNamespace.js
b/modules/dm/behavior/ext.advancedSearch.FiletypeNamespace.js
new file mode 100644
index 0000000..eda0b98
--- /dev/null
+++ b/modules/dm/behavior/ext.advancedSearch.FiletypeNamespace.js
@@ -0,0 +1,42 @@
+( function ( mw ) {
+ 'use strict';
+
+ mw.libs = mw.libs || {};
+ mw.libs.advancedSearch = mw.libs.advancedSearch || {};
+ mw.libs.advancedSearch.dm = mw.libs.advancedSearch.dm || {};
+ mw.libs.advancedSearch.dm.behavior = mw.libs.advancedSearch.dm.behavior
|| {};
+
+ mw.libs.advancedSearch.dm.behavior.FiletypeNamespace = function ( store
) {
+ this.store = store;
+
+ var filetypeOption = store.option( 'filetype' );
+ filetypeOption.on( 'update', this.monitor, [ filetypeOption ],
this );
+ };
+
+ OO.initClass( mw.libs.advancedSearch.dm.behavior.FiletypeNamespace );
+
+ /**
+ * Namespace id of File namespace
+ * @type {string}
+ */
+ mw.libs.advancedSearch.dm.behavior.FiletypeNamespace.FILE_NAMESPACE =
'6';
+
+ /**
+ *
+ * @param {mw.libs.advancedSearch.dm.SearchOptionModel} searchOption
+ */
+ mw.libs.advancedSearch.dm.behavior.FiletypeNamespace.prototype.monitor
= function ( searchOption ) {
+ var value = searchOption.get();
+
+ if ( value === null ) {
+ return;
+ }
+
+ var namespaces = this.store.getNamespaces().slice( 0 ); //
clone to avoid inadvertently changing by reference
+ if ( namespaces.indexOf(
mw.libs.advancedSearch.dm.behavior.FiletypeNamespace.FILE_NAMESPACE ) === -1 ) {
+ namespaces.push(
mw.libs.advancedSearch.dm.behavior.FiletypeNamespace.FILE_NAMESPACE );
+ this.store.setNamespaces( namespaces );
+ }
+ };
+
+}( mediaWiki ) );
diff --git a/modules/dm/ext.advancedSearch.SearchModel.js
b/modules/dm/ext.advancedSearch.SearchModel.js
index a137045..1f779ba 100644
--- a/modules/dm/ext.advancedSearch.SearchModel.js
+++ b/modules/dm/ext.advancedSearch.SearchModel.js
@@ -5,22 +5,25 @@
mw.libs.advancedSearch = mw.libs.advancedSearch || {};
mw.libs.advancedSearch.dm = mw.libs.advancedSearch.dm || {};
- // Internal constants
- var FILETYPES_WITH_DIMENSIONS = [
- 'bitmap',
- 'video',
- 'jpeg',
- 'tiff'
- ];
-
/**
* @class
* @constructor
* @mixins OO.EventEmitter
*/
mw.libs.advancedSearch.dm.SearchModel = function () {
- this.searchOptions = {};
this.namespaces = [ '0' ]; // Always start with Article
namespace
+
+ /**
+ * List of options that constitutes the current search state
+ * @type {mw.libs.advancedSearch.dm.SearchOptionList}
+ */
+ this.searchOptions = null;
+ /**
+ * Dedicated listeners that organize the coexistence of
different search options (e.g. one influencing the other)
+ * @type {Array}
+ */
+ this.behaviors = [];
+ this.initializeSearchOptions();
// Mixin constructor
OO.EventEmitter.call( this );
@@ -47,94 +50,38 @@
*/
mw.libs.advancedSearch.dm.SearchModel.MAIN_NAMESPACE = '0';
- /**
- * Namespace id of File namespace
- * @type {string}
- */
- mw.libs.advancedSearch.dm.SearchModel.FILE_NAMESPACE = '6';
-
/* Methods */
/**
+ * Get the search option with the given id
*
* @param {string} optionId
- * @param {*} value
+ * @return {mw.libs.advancedSearch.dm.SearchOptionModel}
*/
- mw.libs.advancedSearch.dm.SearchModel.prototype.storeOption = function
( optionId, value ) {
- var namespaces;
+ mw.libs.advancedSearch.dm.SearchModel.prototype.option = function (
optionId ) {
- // TODO check for allowed options?
-
- if ( OO.compare( this.searchOptions[ optionId ], value ) ) {
- return;
+ // first time access
+ if ( !this.searchOptions.has( optionId ) ) {
+ this.searchOptions.set( optionId, new
mw.libs.advancedSearch.dm.SearchOptionModel( this ) );
}
- this.searchOptions[ optionId ] = value;
-
- if ( optionId === 'filetype' &&
!this.filetypeSupportsDimensions() ) {
- this.resetFileDimensionOptions();
- }
-
- namespaces = this.getNamespaces();
- if ( optionId === 'filetype' && namespaces.indexOf(
mw.libs.advancedSearch.dm.SearchModel.FILE_NAMESPACE ) === -1 ) {
- namespaces.push(
mw.libs.advancedSearch.dm.SearchModel.FILE_NAMESPACE );
- this.setNamespaces( namespaces );
- }
-
- this.emitUpdate();
+ return this.searchOptions.get( optionId );
};
/**
- * Retrieve value of option with given id
- *
- * @param {string} optionId
- * @return {*}
+ * (Re)Initialize this list of search options
*/
- mw.libs.advancedSearch.dm.SearchModel.prototype.getOption = function (
optionId ) {
- return this.searchOptions[ optionId ];
- };
+ mw.libs.advancedSearch.dm.SearchModel.prototype.initializeSearchOptions
= function () {
+ this.searchOptions = new
mw.libs.advancedSearch.dm.SearchOptionList( this );
+ this.searchOptions.on( 'update', function () {
+ this.emitUpdate();
+ }, [], this );
- /**
- * Remove option with given id
- *
- * @param {string} optionId
- */
- mw.libs.advancedSearch.dm.SearchModel.prototype.removeOption = function
( optionId ) {
-
- if ( this.searchOptions[ optionId ] === undefined ) {
- return;
- }
-
- delete this.searchOptions[ optionId ];
-
- if ( optionId === 'filetype' ) {
- this.resetFileDimensionOptions();
- }
-
- this.emitUpdate();
- };
-
- /**
- * Reset the file dimension search options
- */
-
mw.libs.advancedSearch.dm.SearchModel.prototype.resetFileDimensionOptions =
function () {
- this.searchOptions.filew = [ '>', '' ];
- this.searchOptions.fileh = [ '>', '' ];
- };
-
- /**
- * Get non-empty search options
- *
- * @return {Object}
- */
- mw.libs.advancedSearch.dm.SearchModel.prototype.getOptions = function
() {
- var options = {};
- $.each( this.searchOptions, function ( key, value ) {
- if ( !$.isEmptyObject( value ) ) {
- options[ key ] = value;
- }
- } );
- return options;
+ this.behaviors = [
+ new
mw.libs.advancedSearch.dm.behavior.DefaultNamespace( this ),
+ new
mw.libs.advancedSearch.dm.behavior.FiletypeDimensions( this ),
+ new
mw.libs.advancedSearch.dm.behavior.FiletypeNamespace( this )
+ ];
};
/**
@@ -144,7 +91,7 @@
*/
mw.libs.advancedSearch.dm.SearchModel.prototype.toJSON = function () {
return JSON.stringify( {
- options: this.searchOptions,
+ options: this.searchOptions.getOptions(),
namespaces: this.namespaces
} );
};
@@ -169,12 +116,13 @@
}
if ( typeof unserialized.options === 'object' ) {
- this.searchOptions = {};
+ this.initializeSearchOptions();
for ( var opt in unserialized.options ) {
- this.searchOptions[ opt ] =
unserialized.options[ opt ];
+ this.option( opt ).set( unserialized.options[
opt ] );
}
valuesChanged = true;
}
+
if ( $.isArray( unserialized.namespaces ) ) {
this.namespaces = unserialized.namespaces;
valuesChanged = true;
@@ -185,15 +133,6 @@
};
/**
- * Check if the selected file type supports dimensions
- *
- * @return {boolean}
- */
-
mw.libs.advancedSearch.dm.SearchModel.prototype.filetypeSupportsDimensions =
function () {
- return FILETYPES_WITH_DIMENSIONS.indexOf( this.getOption(
'filetype' ) ) > -1;
- };
-
- /**
* @return {Array}
*/
mw.libs.advancedSearch.dm.SearchModel.prototype.getNamespaces =
function () {
@@ -201,12 +140,17 @@
};
/**
+ * @todo Make namespaces a 'normal' SearchModel search option, handle
this via DefaultNamespace behavior
* @param {Array} namespaces
*/
mw.libs.advancedSearch.dm.SearchModel.prototype.setNamespaces =
function ( namespaces ) {
- var previousNamespaces = this.namespaces.slice( 0 );
- if ( this.getOption( 'filetype' ) && namespaces.indexOf(
mw.libs.advancedSearch.dm.SearchModel.FILE_NAMESPACE ) === -1 ) {
- namespaces.push(
mw.libs.advancedSearch.dm.SearchModel.FILE_NAMESPACE );
+ var previousNamespaces = this.namespaces.slice( 0 ),
+ manipulated = false;
+
+ // file namespace is enforced when filetype is set
+ if ( this.option( 'filetype' ).get() && namespaces.indexOf(
mw.libs.advancedSearch.dm.behavior.FiletypeNamespace.FILE_NAMESPACE ) === -1 ) {
+ namespaces.push(
mw.libs.advancedSearch.dm.behavior.FiletypeNamespace.FILE_NAMESPACE );
+ manipulated = true;
}
if ( namespaces.length ) {
@@ -215,7 +159,7 @@
this.namespaces = [
mw.libs.advancedSearch.dm.SearchModel.MAIN_NAMESPACE ];
}
- if ( !OO.compare( previousNamespaces, this.namespaces ) ) {
+ if ( !OO.compare( previousNamespaces, this.namespaces ) ||
manipulated ) {
this.emitUpdate();
}
};
diff --git a/modules/dm/ext.advancedSearch.SearchOptionList.js
b/modules/dm/ext.advancedSearch.SearchOptionList.js
new file mode 100644
index 0000000..a2bc1c4
--- /dev/null
+++ b/modules/dm/ext.advancedSearch.SearchOptionList.js
@@ -0,0 +1,62 @@
+( function ( mw, $ ) {
+ 'use strict';
+
+ mw.libs = mw.libs || {};
+ mw.libs.advancedSearch = mw.libs.advancedSearch || {};
+ mw.libs.advancedSearch.dm = mw.libs.advancedSearch.dm || {};
+
+ mw.libs.advancedSearch.dm.SearchOptionList = function () {
+ this.values = {};
+
+ OO.EventEmitter.call( this );
+ };
+
+ OO.initClass( mw.libs.advancedSearch.dm.SearchOptionList );
+ OO.mixinClass( mw.libs.advancedSearch.dm.SearchOptionList,
OO.EventEmitter );
+
+ /**
+ * @param {string} id
+ * @return {boolean}
+ */
+ mw.libs.advancedSearch.dm.SearchOptionList.prototype.has = function (
id ) {
+ return ( this.values[ id ] !== undefined );
+ };
+
+ /**
+ * @param {string} id
+ * @return {mw.libs.advancedSearch.dm.SearchOptionModel}
+ */
+ mw.libs.advancedSearch.dm.SearchOptionList.prototype.get = function (
id ) {
+ return this.values[ id ];
+ };
+
+ /**
+ *
+ * @param {string} id
+ * @param {mw.libs.advancedSearch.dm.SearchOptionModel} model
+ */
+ mw.libs.advancedSearch.dm.SearchOptionList.prototype.set = function (
id, model ) {
+ model.on( 'update', this.emit, [ 'update' ], this );
+
+ this.values[ id ] = model;
+ };
+
+ /**
+ * Get non-empty search options
+ *
+ * @return {Object}
+ */
+ mw.libs.advancedSearch.dm.SearchOptionList.prototype.getOptions =
function () {
+ var val,
+ options = {};
+ $.each( this.values, function ( optionId, model ) {
+ val = model.get();
+ if ( val === null || val === '' ) { // @todo check
here for null only, do "empty" assertion in ui (e.g. ImageDimesion)
+ return;
+ }
+ options[ optionId ] = val;
+ } );
+ return options;
+ };
+
+}( mediaWiki, jQuery ) );
diff --git a/modules/dm/ext.advancedSearch.SearchOptionModel.js
b/modules/dm/ext.advancedSearch.SearchOptionModel.js
new file mode 100644
index 0000000..63109e6
--- /dev/null
+++ b/modules/dm/ext.advancedSearch.SearchOptionModel.js
@@ -0,0 +1,31 @@
+( function ( mw ) {
+ 'use strict';
+
+ mw.libs = mw.libs || {};
+ mw.libs.advancedSearch = mw.libs.advancedSearch || {};
+ mw.libs.advancedSearch.dm = mw.libs.advancedSearch.dm || {};
+
+ mw.libs.advancedSearch.dm.SearchOptionModel = function () {
+ this.value = null;
+
+ OO.EventEmitter.call( this );
+ };
+
+ OO.initClass( mw.libs.advancedSearch.dm.SearchOptionModel );
+ OO.mixinClass( mw.libs.advancedSearch.dm.SearchOptionModel,
OO.EventEmitter );
+
+ mw.libs.advancedSearch.dm.SearchOptionModel.prototype.set = function (
value ) {
+ if ( OO.compare( this.value, value ) ) {
+ return;
+ }
+
+ this.value = value;
+
+ this.emit( 'update' );
+ };
+
+ mw.libs.advancedSearch.dm.SearchOptionModel.prototype.get = function ()
{
+ return this.value;
+ };
+
+}( mediaWiki ) );
diff --git a/modules/ext.advancedSearch.init.js
b/modules/ext.advancedSearch.init.js
index f34b979..30360ee 100644
--- a/modules/ext.advancedSearch.init.js
+++ b/modules/ext.advancedSearch.init.js
@@ -69,27 +69,6 @@
return prefix + val.join( '' );
}
- /**
- * @param {string} id
- * @return {Function}
- */
- function createMultiSelectChangeHandler( id ) {
- return function ( newValue ) {
-
- if ( typeof newValue !== 'object' ) {
- state.storeOption( id, newValue );
- return;
- }
-
- state.storeOption( id, $.map( newValue, function (
$valueObj ) {
- if ( typeof $valueObj === 'string' ) {
- return $valueObj;
- }
- return $valueObj.data;
- } ) );
- };
- }
-
function createOptionalFieldLayout( widget, option ) {
return new mw.libs.advancedSearch.ui.OptionalElementLayout(
state,
@@ -98,7 +77,7 @@
label: mw.msg( 'advancedsearch-field-' +
option.id ),
align: 'right',
checkVisibility: function () {
- return
state.filetypeSupportsDimensions();
+ return
mw.libs.advancedSearch.dm.behavior.FiletypeDimensions.static.filetypeSupportsDimensions(
state.option( 'filetype' ).get() );
}
}
);
@@ -136,12 +115,7 @@
}
return enforceQuotes( val );
},
- init: function () {
- return new
mw.libs.advancedSearch.ui.ArbitraryWordInput(
- state,
- { optionId: 'phrase' }
- );
- }
+ uiClass: mw.libs.advancedSearch.ui.ArbitraryWordInput
},
{
group: 'text',
@@ -161,13 +135,7 @@
}
return optionalQuotes( val );
},
- init: function () {
- var widget = new
mw.libs.advancedSearch.ui.ArbitraryWordInput(
- state,
- { optionId: 'or' }
- );
- return widget;
- }
+ uiClass: mw.libs.advancedSearch.ui.ArbitraryWordInput
},
{
@@ -239,16 +207,8 @@
}
return '';
},
- init: function () {
- return new
mw.libs.advancedSearch.ui.FileTypeSelection(
- state,
- {
- optionId: 'filetype',
- name:
'advancedSearchOption-filetype'
- }
- );
- },
- requiredNamespace: 6
+ requiredNamespace: 6,
+ uiClass: mw.libs.advancedSearch.ui.FileTypeSelection
},
{
group: 'files',
@@ -258,15 +218,8 @@
return formatSizeConstraint( 'filew:', val );
},
requiredNamespace: 6,
- init: function () {
- return new
mw.libs.advancedSearch.ui.ImageDimensionInput(
- state,
- {
- optionId: 'filew'
- }
- );
- },
- layout: createOptionalFieldLayout
+ layout: createOptionalFieldLayout,
+ uiClass: mw.libs.advancedSearch.ui.ImageDimensionInput
},
{
group: 'files',
@@ -276,15 +229,8 @@
return formatSizeConstraint( 'fileh:', val );
},
requiredNamespace: 6,
- init: function () {
- return new
mw.libs.advancedSearch.ui.ImageDimensionInput(
- state,
- {
- optionId: 'fileh'
- }
- );
- },
- layout: createOptionalFieldLayout
+ layout: createOptionalFieldLayout,
+ uiClass: mw.libs.advancedSearch.ui.ImageDimensionInput
}
// Ordering
@@ -305,7 +251,7 @@
greedyQuery = null;
advancedOptions.forEach( function ( option ) {
- var val = state.getOption( option.id ),
+ var val = state.option( option.id ).get(),
formattedQueryElement = val ? option.formatter(
val ) : '';
if ( !formattedQueryElement ) {
@@ -341,14 +287,14 @@
var paramName = 'advancedSearchOption-' + option.id;
- var widgetInit = option.init || function () {
- return new mw.libs.advancedSearch.ui.TextInput(
state, {
- id: paramName,
- optionId: option.id
- } );
- },
- widget = widgetInit();
- widget.on( 'change', createMultiSelectChangeHandler( option.id
) );
+ var UiClass = option.uiClass;
+ if ( UiClass === undefined ) {
+ UiClass = mw.libs.advancedSearch.ui.TextInput;
+ }
+ var widget = new UiClass( state.option( option.id ), {
+ id: paramName,
+ name: paramName // @todo Was only that way for filetype
+ } );
if ( !optionSets[ option.group ] ) {
optionSets[ option.group ] = new OO.ui.FieldsetLayout( {
@@ -435,4 +381,5 @@
$( '#mw-searchoptions' ).remove();
mw.loader.load(
'//de.wikipedia.org/w/index.php?title=MediaWiki:Gadget-DeepCat.js&action=raw&ctype=text/javascript'
);
+
}( mediaWiki, jQuery ) );
diff --git a/modules/ui/ext.advancedSearch.ArbitraryWordInput.js
b/modules/ui/ext.advancedSearch.ArbitraryWordInput.js
index caca982..4f4975b 100644
--- a/modules/ui/ext.advancedSearch.ArbitraryWordInput.js
+++ b/modules/ui/ext.advancedSearch.ArbitraryWordInput.js
@@ -10,19 +10,20 @@
* @extends {OO.ui.TagMultiselectWidget}
* @constructor
*
- * @param {ext.advancedSearch.dm.SearchModel} store
+ * @param {ext.advancedSearch.dm.SearchOptionModel} store
* @param {Object} config
*/
mw.libs.advancedSearch.ui.ArbitraryWordInput = function ( store, config
) {
var myConfig = $.extend( { allowArbitrary: true }, config || {}
);
this.store = store;
- this.optionId = config.optionId;
this.store.connect( this, { update: 'onStoreUpdate' } );
mw.libs.advancedSearch.ui.ArbitraryWordInput.parent.call( this,
myConfig );
this.populateFromStore();
+
+ this.connect( this, { change: 'onValueUpdate' } );
};
OO.inheritClass( mw.libs.advancedSearch.ui.ArbitraryWordInput,
OO.ui.TagMultiselectWidget );
@@ -61,12 +62,17 @@
return true;
}
+ mw.libs.advancedSearch.ui.ArbitraryWordInput.prototype.onValueUpdate =
function () {
+ // there was mapping of $valueObj.data to strings when using
the event payload. using the member method seems alright
+ this.store.set( this.getValue() );
+ };
+
mw.libs.advancedSearch.ui.ArbitraryWordInput.prototype.onStoreUpdate =
function () {
this.populateFromStore();
};
mw.libs.advancedSearch.ui.ArbitraryWordInput.prototype.populateFromStore =
function () {
- var val = this.store.getOption( this.optionId ) || [];
+ var val = this.store.get() || [];
if ( arrayEquals( this.getValue(), val ) ) {
return;
}
diff --git a/modules/ui/ext.advancedSearch.FileTypeSelection.js
b/modules/ui/ext.advancedSearch.FileTypeSelection.js
index fdeb7de..79b1b76 100644
--- a/modules/ui/ext.advancedSearch.FileTypeSelection.js
+++ b/modules/ui/ext.advancedSearch.FileTypeSelection.js
@@ -10,14 +10,14 @@
* @extends {OO.ui.DropdownInputWidget}
* @constructor
*
- * @param {ext.advancedSearch.dm.SearchModel} store
+ * @param {ext.advancedSearch.dm.SearchOptionModel} store
* @param {Object} config
*/
mw.libs.advancedSearch.ui.FileTypeSelection = function ( store, config
) {
var myConfig = $.extend( {
options: [
{
- data: '',
+ data: null,
label: ''
},
{
@@ -94,7 +94,6 @@
]
}, config );
this.store = store;
- this.optionId = config.optionId;
// Parent constructor
mw.libs.advancedSearch.ui.FileTypeSelection.parent.call( this,
myConfig );
@@ -102,22 +101,22 @@
store.connect( this, { update: 'onStoreUpdate' } );
this.setValueFromStore();
+
+ this.connect( this, { change: 'onValueUpdate' } );
};
OO.inheritClass( mw.libs.advancedSearch.ui.FileTypeSelection,
OO.ui.DropdownInputWidget );
+
+ mw.libs.advancedSearch.ui.FileTypeSelection.prototype.onValueUpdate =
function () {
+ this.store.set( this.getValue() );
+ };
mw.libs.advancedSearch.ui.FileTypeSelection.prototype.onStoreUpdate =
function () {
this.setValueFromStore();
};
mw.libs.advancedSearch.ui.FileTypeSelection.prototype.setValueFromStore
= function () {
- var storeValue = this.store.getOption( this.optionId ),
- selectedItem =
this.dropdownWidget.getMenu().getItemFromData( storeValue );
- // avoid setting invalid values and re-triggering
- if ( selectedItem === null || this.getValue() === storeValue ) {
- return;
- }
- this.setValue( storeValue );
+ this.setValue( this.store.get() );
};
}( mediaWiki, jQuery ) );
diff --git a/modules/ui/ext.advancedSearch.ImageDimensionInput.js
b/modules/ui/ext.advancedSearch.ImageDimensionInput.js
index 20e04a3..4e85bb8 100644
--- a/modules/ui/ext.advancedSearch.ImageDimensionInput.js
+++ b/modules/ui/ext.advancedSearch.ImageDimensionInput.js
@@ -10,11 +10,10 @@
* @extends {OO.ui.FieldLayout}
* @constructor
*
- * @param {ext.advancedSearch.dm.SearchModel} store
+ * @param {ext.advancedSearch.dm.SearchOptionModel} store
* @param {Object} config
*/
mw.libs.advancedSearch.ui.ImageDimensionInput = function ( store,
config ) {
- this.optionId = config.optionId;
this.store = store;
store.connect( this, { update: 'onStoreUpdate' } );
@@ -37,8 +36,8 @@
} );
this.valueInput = new OO.ui.TextInputWidget( { label: 'px' } );
- this.operatorInput.connect( this, { change: 'onInputChange' } );
- this.valueInput.connect( this, { change: 'onInputChange' } );
+ this.operatorInput.connect( this, { change: 'onValueUpdate' } );
+ this.valueInput.connect( this, { change: 'onValueUpdate' } );
this.$element.append( this.operatorInput.$element.wrap( '<div
class="operator-container"></div>' ).parent() );
this.$element.append( this.valueInput.$element.wrap( '<div
class="value-container"></div>' ).parent() );
@@ -49,9 +48,10 @@
OO.inheritClass( mw.libs.advancedSearch.ui.ImageDimensionInput,
OO.ui.Widget );
- mw.libs.advancedSearch.ui.ImageDimensionInput.prototype.onInputChange =
function () {
+ mw.libs.advancedSearch.ui.ImageDimensionInput.prototype.onValueUpdate =
function () {
this.data[ 0 ] = this.operatorInput.getValue();
this.data[ 1 ] = this.valueInput.getValue();
+ this.store.set( this.data );
this.emit( 'change', this.data );
};
@@ -60,7 +60,7 @@
};
mw.libs.advancedSearch.ui.ImageDimensionInput.prototype.setValuesFromStore =
function () {
- var newValue = this.store.getOption( this.optionId );
+ var newValue = this.store.get();
if ( !newValue || newValue === this.data ) {
return;
}
diff --git a/modules/ui/ext.advancedSearch.SearchPreview.js
b/modules/ui/ext.advancedSearch.SearchPreview.js
index e54a55f..26c118f 100644
--- a/modules/ui/ext.advancedSearch.SearchPreview.js
+++ b/modules/ui/ext.advancedSearch.SearchPreview.js
@@ -72,7 +72,7 @@
}
$.each( this.previewOptions, function ( index, optionId ) {
- var val = this.store.getOption( optionId );
+ var val = this.store.option( optionId ).get();
if ( this.skipOptionInPreview( optionId, val ) ) {
return;
@@ -129,7 +129,7 @@
tag.connect( this, {
remove: function () {
- this.store.removeOption( optionId );
+ this.store.option( optionId ).set( null );
}
} );
diff --git a/modules/ui/ext.advancedSearch.TextInput.js
b/modules/ui/ext.advancedSearch.TextInput.js
index e935076..d4793ef 100644
--- a/modules/ui/ext.advancedSearch.TextInput.js
+++ b/modules/ui/ext.advancedSearch.TextInput.js
@@ -10,29 +10,34 @@
* @extends {OO.ui.TextInputWidget}
* @constructor
*
- * @param {ext.advancedSearch.dm.SearchModel} store
+ * @param {ext.advancedSearch.dm.SearchOptionModel} store
* @param {Object} config
*/
mw.libs.advancedSearch.ui.TextInput = function ( store, config ) {
var myConfig = $.extend( {}, config || {} );
this.store = store;
- this.optionId = config.optionId;
this.store.connect( this, { update: 'onStoreUpdate' } );
mw.libs.advancedSearch.ui.TextInput.parent.call( this, myConfig
);
this.populateFromStore();
+
+ this.connect( this, { change: 'onValueUpdate' } );
};
OO.inheritClass( mw.libs.advancedSearch.ui.TextInput,
OO.ui.TextInputWidget );
+
+ mw.libs.advancedSearch.ui.TextInput.prototype.onValueUpdate = function
() {
+ this.store.set( this.getValue() );
+ };
mw.libs.advancedSearch.ui.TextInput.prototype.onStoreUpdate = function
() {
this.populateFromStore();
};
mw.libs.advancedSearch.ui.TextInput.prototype.populateFromStore =
function () {
- var val = this.store.getOption( this.optionId );
+ var val = this.store.get();
if ( this.getValue() === val ) {
return;
}
diff --git a/tests/qunit/dm/SearchModel.test.js
b/tests/qunit/dm/SearchModel.test.js
index eedb3b2..422ff20 100644
--- a/tests/qunit/dm/SearchModel.test.js
+++ b/tests/qunit/dm/SearchModel.test.js
@@ -13,9 +13,9 @@
QUnit.module( 'ext.advancedSearch.dm.SearchModel' );
- QUnit.test( 'Default model has no options', 1, function ( assert ) {
+ QUnit.test( 'Default JSON has no options and a default namespace', 1,
function ( assert ) {
var model = new SearchModel();
- assert.deepEqual( model.getOptions(), {} );
+ assert.equal( model.toJSON(),
'{"options":{},"namespaces":["0"]}' );
} );
QUnit.test( 'Default model has article namespace', 1, function ( assert
) {
@@ -25,48 +25,45 @@
QUnit.test( 'Options that were set can be retrieved', 1, function (
assert ) {
var model = new SearchModel();
- model.storeOption( 'not', 'octopi' );
- model.storeOption( 'prefix', 'Page' );
- assert.deepEqual( model.getOptions(), {
- not: 'octopi',
- prefix: 'Page'
- } );
+ model.option( 'not' ).set( 'octopi' );
+
+ assert.equal( model.option( 'not' ).get(), 'octopi' );
} );
function createModelWithValues() {
var model = new SearchModel();
- model.storeOption( 'not', 'octopi' );
- model.storeOption( 'prefix', 'Page' );
+ model.option( 'not' ).set( 'octopi' );
+ model.option( 'prefix' ).set( 'Page' );
model.setNamespaces( [ '1', '3' ] );
return model;
}
- QUnit.test( 'Setting empty JSON string does nothing', 2, function (
assert ) {
- var model = createModelWithValues(),
- expected = createModelWithValues();
+ QUnit.test( 'Setting empty JSON string does nothing', 3, function (
assert ) {
+ var model = createModelWithValues();
model.setAllFromJSON( '' );
- assert.deepEqual( model.getOptions(), expected.getOptions() );
- assert.deepEqual( model.getNamespaces(),
expected.getNamespaces() );
+ assert.deepEqual( model.option( 'not' ).get(), 'octopi' );
+ assert.deepEqual( model.option( 'prefix' ).get(), 'Page' );
+ assert.deepEqual( model.getNamespaces(), [ '1', '3' ] );
} );
- QUnit.test( 'Setting invalid JSON string does nothing', 2, function (
assert ) {
- var model = createModelWithValues(),
- expected = createModelWithValues();
+ QUnit.test( 'Setting invalid JSON string does nothing', 3, function (
assert ) {
+ var model = createModelWithValues();
model.setAllFromJSON( '{ "unclosed_string": "str }' );
- assert.deepEqual( model.getOptions(), expected.getOptions() );
- assert.deepEqual( model.getNamespaces(),
expected.getNamespaces() );
+ assert.deepEqual( model.option( 'not' ).get(), 'octopi' );
+ assert.deepEqual( model.option( 'prefix' ).get(), 'Page' );
+ assert.deepEqual( model.getNamespaces(), [ '1', '3' ] );
} );
- QUnit.test( 'Setting valid JSON overrides previous state', 2, function
( assert ) {
+ QUnit.test( 'Setting valid JSON overrides previous state', 4, function
( assert ) {
var model = createModelWithValues();
model.setAllFromJSON( '{"options":{"or":[ "fish", "turtle"
],"prefix":"Sea"},"namespaces":["0","2"]}' );
- assert.deepEqual( model.getOptions(), {
- or: [ 'fish', 'turtle' ],
- prefix: 'Sea'
- } );
+ assert.equal( model.option( 'not' ).get(), null );
+
+ assert.equal( model.option( 'prefix' ).get(), 'Sea' );
+ assert.deepEqual( model.option( 'or' ).get(), [ 'fish',
'turtle' ] );
assert.deepEqual( model.getNamespaces(), [ '0', '2' ] );
} );
@@ -84,60 +81,41 @@
QUnit.test( 'Adding filetype option forces file namespace', 1, function
( assert ) {
var model = new SearchModel();
- model.storeOption( 'filetype', 'image' );
+ model.option( 'filetype' ).set( 'image' );
- assert.deepEqual( model.getNamespaces(), [
SearchModel.MAIN_NAMESPACE, SearchModel.FILE_NAMESPACE ] );
+ assert.deepEqual( model.getNamespaces(), [ '0', '6' ] );
} );
QUnit.test( 'When filetype option is set, file namespace cannot be
removed', 1, function ( assert ) {
var model = new SearchModel();
- model.storeOption( 'filetype', 'image' );
+ model.option( 'filetype' ).set( 'image' );
model.setNamespaces( [] );
- assert.deepEqual( model.getNamespaces(), [
SearchModel.FILE_NAMESPACE ] );
+ assert.deepEqual( model.getNamespaces(), [ '6' ] );
} );
QUnit.test( 'File dimension data is reset on filetype change', 2,
function ( assert ) {
var model = new SearchModel();
- model.storeOption( 'filetype', 'jpeg' );
- model.storeOption( 'filew', [ '>', '1500' ] );
- model.storeOption( 'fileh', [ '', '800' ] );
+ model.option( 'filetype' ).set( 'jpeg' );
+ model.option( 'filew' ).set( [ '>', '1500' ] );
+ model.option( 'fileh' ).set( [ '', '800' ] );
- model.storeOption( 'filetype', 'random' );
+ model.option( 'filetype' ).set( 'random' );
- assert.deepEqual( model.getOption( 'filew' ), [ '>', '' ] );
- assert.deepEqual( model.getOption( 'fileh' ), [ '>', '' ] );
+ assert.deepEqual( model.option( 'filew' ).get(), [ '>', '' ] );
+ assert.deepEqual( model.option( 'fileh' ).get(), [ '>', '' ] );
} );
QUnit.test( 'File dimension data containers reset on filetype remove',
2, function ( assert ) {
var model = new SearchModel();
- model.storeOption( 'filetype', 'video' );
- model.storeOption( 'filew', [ '', '800' ] );
- model.storeOption( 'fileh', [ '', '600' ] );
+ model.option( 'filetype' ).set( 'video' );
+ model.option( 'filew' ).set( [ '', '800' ] );
+ model.option( 'fileh' ).set( [ '', '600' ] );
- model.removeOption( 'filetype' );
+ model.option( 'filetype' ).set( null );
- assert.deepEqual( model.getOption( 'filew' ), [ '>', '' ] );
- assert.deepEqual( model.getOption( 'fileh' ), [ '>', '' ] );
- } );
-
- QUnit.test( 'File types support dimensions configured', 5, function (
assert ) {
- var model = new SearchModel();
-
- model.storeOption( 'filetype', 'bitmap' );
- assert.ok( model.filetypeSupportsDimensions() );
-
- model.storeOption( 'filetype', 'video' );
- assert.ok( model.filetypeSupportsDimensions() );
-
- model.storeOption( 'filetype', 'jpeg' );
- assert.ok( model.filetypeSupportsDimensions() );
-
- model.storeOption( 'filetype', 'tiff' );
- assert.ok( model.filetypeSupportsDimensions() );
-
- model.storeOption( 'filetype', 'random' );
- assert.notOk( model.filetypeSupportsDimensions() );
+ assert.deepEqual( model.option( 'filew' ).get(), [ '>', '' ] );
+ assert.deepEqual( model.option( 'fileh' ).get(), [ '>', '' ] );
} );
QUnit.test( 'Setting namespace to existing value does not trigger
emitUpdate', 1, function ( assert ) {
@@ -165,51 +143,51 @@
var model = new SearchModel();
var updateSpy = sandbox.spy( model, 'emitUpdate' );
- model.storeOption( 'aaa', 'fff' );
+ model.option( 'aaa' ).set( 'fff' );
assert.ok( updateSpy.calledOnce );
} );
QUnit.test( 'Storing an option with the same scalar value does not
trigger emitUpdate', 1, function ( assert ) {
var model = new SearchModel();
- model.storeOption( 'lorem', 'ipsum' );
+ model.option( 'lorem' ).set( 'ipsum' );
var updateSpy = sandbox.spy( model, 'emitUpdate' );
- model.storeOption( 'lorem', 'ipsum' );
+ model.option( 'lorem' ).set( 'ipsum' );
assert.notOk( updateSpy.called );
} );
QUnit.test( 'Storing an option with the same array value does not
trigger emitUpdate', 1, function ( assert ) {
var model = new SearchModel();
- model.storeOption( 'lorem', [ 'hakuna', 'matata' ] );
+ model.option( 'lorem' ).set( [ 'hakuna', 'matata' ] );
var updateSpy = sandbox.spy( model, 'emitUpdate' );
- model.storeOption( 'lorem', [ 'hakuna', 'matata' ] );
+ model.option( 'lorem' ).set( [ 'hakuna', 'matata' ] );
assert.notOk( updateSpy.called );
} );
QUnit.test( 'Removing an option triggers emitUpdate', 1, function (
assert ) {
var model = new SearchModel();
- model.storeOption( 'lorem', 'ipsum' );
+ model.option( 'lorem' ).set( 'ipsum' );
var updateSpy = sandbox.spy( model, 'emitUpdate' );
- model.removeOption( 'lorem' );
+ model.option( 'lorem' ).set( null );
assert.ok( updateSpy.calledOnce );
} );
QUnit.test( 'Removing an unset option does not trigger emitUpdate', 1,
function ( assert ) {
var model = new SearchModel();
- model.storeOption( 'lorem', 'ipsum' );
+ model.option( 'lorem' ).set( 'ipsum' );
var updateSpy = sandbox.spy( model, 'emitUpdate' );
- model.removeOption( 'amet' );
+ model.option( 'amet' ).set( null );
assert.notOk( updateSpy.called );
} );
diff --git a/tests/qunit/dm/SearchOptionList.test.js
b/tests/qunit/dm/SearchOptionList.test.js
new file mode 100644
index 0000000..8319e27
--- /dev/null
+++ b/tests/qunit/dm/SearchOptionList.test.js
@@ -0,0 +1,83 @@
+( function ( mw ) {
+ var SearchOptionList,
+ SearchOptionModel,
+ sandbox;
+
+ QUnit.testStart( function () {
+ SearchOptionList = mw.libs.advancedSearch.dm.SearchOptionList;
+ SearchOptionModel = mw.libs.advancedSearch.dm.SearchOptionModel;
+ sandbox = sinon.sandbox.create();
+ } );
+
+ QUnit.testDone( function () {
+ sandbox.restore();
+ } );
+
+ QUnit.module( 'ext.advancedSearch.dm.SearchOptionList' );
+
+ QUnit.test( 'getValues on empty list', 1, function ( assert ) {
+ var list = new SearchOptionList();
+
+ assert.deepEqual( list.getOptions(), {} );
+ } );
+
+ QUnit.test( 'getValues skips empty values', 1, function ( assert ) {
+ var list = new SearchOptionList();
+ var modelOne = new SearchOptionModel();
+ modelOne.set( 'octopi' );
+ list.set( 'not', modelOne );
+ var modelTwo = new SearchOptionModel();
+ modelTwo.set( 'Page' );
+ list.set( 'prefix', modelTwo );
+ var modelThree = new SearchOptionModel();
+ list.set( 'somesome', modelThree );
+
+ assert.deepEqual( list.getOptions(), {
+ not: 'octopi',
+ prefix: 'Page'
+ } );
+ } );
+
+ QUnit.test( 'Retrieving of unknown option', 1, function ( assert ) {
+ var list = new SearchOptionList();
+
+ assert.equal( list.get( 'not set' ), undefined );
+ } );
+
+ QUnit.test( 'Check for unknown option', 1, function ( assert ) {
+ var list = new SearchOptionList();
+
+ assert.notOk( list.has( 'not here' ) );
+ } );
+
+ QUnit.test( 'Setting of option', 1, function ( assert ) {
+ var list = new SearchOptionList();
+ var model = new SearchOptionModel();
+ list.set( 'mykey', model );
+
+ assert.equal( list.get( 'mykey' ), model );
+ } );
+
+ QUnit.test( 'Setting multiple options', 2, function ( assert ) {
+ var list = new SearchOptionList();
+ var modelOne = new SearchOptionModel();
+ var modelTwo = new SearchOptionModel();
+ list.set( 'somesome', modelOne );
+ list.set( 'mykey', modelTwo );
+
+ assert.equal( list.get( 'somesome' ), modelOne );
+ assert.equal( list.get( 'mykey' ), modelTwo );
+ } );
+
+ QUnit.test( 'Model events are detected and bubbled', 2, function (
assert ) {
+ var list = new SearchOptionList();
+ var updateSpy = sandbox.spy( list, 'emit' );
+ var model = new SearchOptionModel();
+ list.set( 'mykey', model );
+ model.set( 'a nice value' );
+
+ assert.ok( updateSpy.calledOnce );
+ assert.equal( updateSpy.getCall( 0 ).args[ 0 ], 'update' );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/dm/SearchOptionModel.test.js
b/tests/qunit/dm/SearchOptionModel.test.js
new file mode 100644
index 0000000..5ceb250
--- /dev/null
+++ b/tests/qunit/dm/SearchOptionModel.test.js
@@ -0,0 +1,54 @@
+( function ( mw ) {
+ var SearchOptionModel,
+ sandbox;
+
+ QUnit.testStart( function () {
+ SearchOptionModel = mw.libs.advancedSearch.dm.SearchOptionModel;
+ sandbox = sinon.sandbox.create();
+ } );
+
+ QUnit.testDone( function () {
+ sandbox.restore();
+ } );
+
+ QUnit.module( 'ext.advancedSearch.dm.SearchOptionModel' );
+
+ QUnit.test( 'Test default value', 1, function ( assert ) {
+ var model = new SearchOptionModel();
+ assert.equal( model.get(), null );
+ } );
+
+ QUnit.test( 'Scalar gets stored', 1, function ( assert ) {
+ var model = new SearchOptionModel();
+ model.set( 'aaa' );
+
+ assert.equal( model.get(), 'aaa' );
+ } );
+
+ QUnit.test( 'Array gets stored', 1, function ( assert ) {
+ var model = new SearchOptionModel();
+ model.set( [ 'ggg', 'fff' ] );
+
+ assert.deepEqual( model.get(), [ 'ggg', 'fff' ] );
+ } );
+
+ QUnit.test( 'Update emits event', 1, function ( assert ) {
+ var model = new SearchOptionModel();
+ var emitSpy = sandbox.spy( model, 'emit' ).withArgs( 'update' );
+
+ model.set( 'aaa' );
+
+ assert.ok( emitSpy.calledOnce );
+ } );
+
+ QUnit.test( 'Set without change emits no event', 1, function ( assert )
{
+ var model = new SearchOptionModel();
+ model.set( [ 'hello', 'world ' ] );
+ var emitSpy = sandbox.spy( model, 'emit' ).withArgs( 'update' );
+
+ model.set( [ 'hello', 'world ' ] );
+
+ assert.notOk( emitSpy.called );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/dm/behavior/DefaultNamespace.test.js
b/tests/qunit/dm/behavior/DefaultNamespace.test.js
new file mode 100644
index 0000000..823bc76
--- /dev/null
+++ b/tests/qunit/dm/behavior/DefaultNamespace.test.js
@@ -0,0 +1,31 @@
+( function ( mw ) {
+ var DefaultNamespace,
+ sandbox,
+ store;
+
+ QUnit.testStart( function () {
+ DefaultNamespace =
mw.libs.advancedSearch.dm.behavior.DefaultNamespace;
+ sandbox = sinon.sandbox.create();
+ store = {
+ option: sandbox.stub()
+ };
+ } );
+
+ QUnit.testDone( function () {
+ sandbox.restore();
+ } );
+
+ QUnit.module( 'ext.advancedSearch.dm.behavior.DefaultNamespace' );
+
+ QUnit.test( 'Listens to store', 2, function ( assert ) {
+ var onSpy = sandbox.spy();
+ store.option.withArgs( 'namespaces' ).returns( {
+ on: onSpy
+ } );
+ var behavior = new DefaultNamespace( store );
+
+ assert.ok( behavior );
+ assert.ok( onSpy.calledOnce );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/dm/behavior/FiletypeDimensions.test.js
b/tests/qunit/dm/behavior/FiletypeDimensions.test.js
new file mode 100644
index 0000000..14310ae
--- /dev/null
+++ b/tests/qunit/dm/behavior/FiletypeDimensions.test.js
@@ -0,0 +1,53 @@
+( function ( mw ) {
+ var FiletypeDimensions,
+ sandbox,
+ store;
+
+ QUnit.testStart( function () {
+ FiletypeDimensions =
mw.libs.advancedSearch.dm.behavior.FiletypeDimensions;
+ sandbox = sinon.sandbox.create();
+ store = {};
+ } );
+
+ QUnit.testDone( function () {
+ sandbox.restore();
+ } );
+
+ QUnit.module( 'ext.advancedSearch.dm.behavior.FiletypeDimensions' );
+
+ QUnit.test( 'File types support dimensions configured', 6, function (
assert ) {
+ var staticMethod =
mw.libs.advancedSearch.dm.behavior.FiletypeDimensions.static.filetypeSupportsDimensions;
+ assert.ok( staticMethod( 'bitmap' ) );
+ assert.ok( staticMethod( 'video' ) );
+ assert.ok( staticMethod( 'jpeg' ) );
+ assert.ok( staticMethod( 'tiff' ) );
+
+ assert.notOk( staticMethod( 'random' ) );
+ assert.notOk( staticMethod( null ) );
+ } );
+
+ QUnit.test( 'Related options are correctly reset', 3, function ( assert
) {
+ var fileTypeOption = {
+ on: sandbox.spy()
+ },
+ fileWidthOption = {
+ set: sandbox.spy()
+ },
+ fileHeightOption = {
+ set: sandbox.spy()
+ };
+
+ store.option = sandbox.stub();
+ store.option.withArgs( 'filetype' ).returns( fileTypeOption );
+ store.option.withArgs( 'filew' ).returns( fileWidthOption );
+ store.option.withArgs( 'fileh' ).returns( fileHeightOption );
+
+ var behavior = new FiletypeDimensions( store );
+ behavior.resetFileDimensionOptions();
+
+ assert.ok( fileTypeOption.on.calledOnce );
+ assert.ok( fileWidthOption.set.withArgs( [ '>', '' ]
).calledOnce );
+ assert.ok( fileHeightOption.set.withArgs( [ '>', '' ]
).calledOnce );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/dm/behavior/FiletypeNamespace.test.js
b/tests/qunit/dm/behavior/FiletypeNamespace.test.js
new file mode 100644
index 0000000..9b74c9c
--- /dev/null
+++ b/tests/qunit/dm/behavior/FiletypeNamespace.test.js
@@ -0,0 +1,78 @@
+( function ( mw ) {
+ var FiletypeNamespace,
+ sandbox,
+ store;
+
+ QUnit.testStart( function () {
+ FiletypeNamespace =
mw.libs.advancedSearch.dm.behavior.FiletypeNamespace;
+ sandbox = sinon.sandbox.create();
+ store = {};
+ } );
+
+ QUnit.testDone( function () {
+ sandbox.restore();
+ } );
+
+ QUnit.module( 'ext.advancedSearch.dm.behavior.FiletypeNamespace' );
+
+ QUnit.test( 'Empty filetype value keeps namespaces untouched', 1,
function ( assert ) {
+ store.option = sandbox.stub();
+ store.option.withArgs( 'filetype' ).returns( {
+ on: sandbox.spy()
+ } );
+ store.setNamespaces = sandbox.spy();
+
+ var behavior = new FiletypeNamespace( store );
+
+ var fileTypeOption = {
+ get: sandbox.stub()
+ };
+
+ fileTypeOption.get.returns( null );
+ behavior.monitor( fileTypeOption );
+ assert.notOk( store.setNamespaces.called );
+ } );
+
+ QUnit.test( 'Set filetype value updates namespaces', 2, function (
assert ) {
+ store.option = sandbox.stub();
+ store.option.withArgs( 'filetype' ).returns( {
+ on: sandbox.spy()
+ } );
+ store.getNamespaces = sandbox.stub();
+ store.getNamespaces.returns( [] );
+ store.setNamespaces = sandbox.spy();
+
+ var behavior = new FiletypeNamespace( store );
+
+ var fileTypeOption = {
+ get: sandbox.stub()
+ };
+
+ fileTypeOption.get.returns( 'image' );
+ behavior.monitor( fileTypeOption );
+ assert.ok( store.getNamespaces.calledOnce );
+ assert.ok( store.setNamespaces.withArgs( [ '6' ] ).calledOnce );
+ } );
+
+ QUnit.test( 'Set filetype keeps existing file namespace', 2, function (
assert ) {
+ store.option = sandbox.stub();
+ store.option.withArgs( 'filetype' ).returns( {
+ on: sandbox.spy()
+ } );
+ store.getNamespaces = sandbox.stub();
+ store.getNamespaces.returns( [ '6' ] );
+ store.setNamespaces = sandbox.spy();
+
+ var behavior = new FiletypeNamespace( store );
+
+ var fileTypeOption = {
+ get: sandbox.stub()
+ };
+
+ fileTypeOption.get.returns( 'image' );
+ behavior.monitor( fileTypeOption );
+ assert.ok( store.getNamespaces.calledOnce );
+ assert.notOk( store.setNamespaces.called );
+ } );
+
+}( mediaWiki ) );
diff --git a/tests/qunit/ui/SearchPreview.test.js
b/tests/qunit/ui/SearchPreview.test.js
index 84c89dc..b08360f 100644
--- a/tests/qunit/ui/SearchPreview.test.js
+++ b/tests/qunit/ui/SearchPreview.test.js
@@ -11,8 +11,7 @@
sandbox = sinon.sandbox.create();
store = {
connect: sandbox.stub(),
- getOption: sandbox.stub(),
- removeOption: sandbox.stub()
+ option: sandbox.stub()
};
config = {};
} );
@@ -45,8 +44,8 @@
QUnit.test( 'Store state is reflected in preview', 5, function ( assert
) {
var generateTagSpy = sandbox.spy( SearchPreview.prototype,
'generateTag' );
- store.getOption.withArgs( 'somename' ).returns( 'field one
value' );
- store.getOption.withArgs( 'another' ).returns( 'field two
value' );
+ store.option.withArgs( 'somename' ).returns( { get: function ()
{ return 'field one value'; } } );
+ store.option.withArgs( 'another' ).returns( { get: function ()
{ return 'field two value'; } } );
config.previewOptions = [ 'somename', 'another' ];
@@ -55,7 +54,7 @@
var pills = $( '.mw-advancedSearch-searchPreview-previewPill',
searchPreview.$element );
assert.equal( pills.length, 2 );
- assert.ok( store.getOption.calledTwice );
+ assert.ok( store.option.calledTwice );
assert.ok( generateTagSpy.calledTwice );
assert.ok( generateTagSpy.withArgs( 'somename', 'field one
value' ).calledOnce );
@@ -120,18 +119,20 @@
} );
QUnit.test( 'Tag removals clears store', 1, function ( assert ) {
+ store.option.withArgs( 'somename' ).returns( { set: function (
value ) {
+ assert.equal( value, null );
+ } } );
var searchPreview = new SearchPreview( store, config );
var tag = searchPreview.generateTag( 'somename', 'my field
value' );
tag.remove();
- assert.ok( store.removeOption.withArgs( 'somename' ).calledOnce
);
} );
QUnit.test( 'Showing renders pills', 1, function ( assert ) {
config.previewOptions = [ 'one', 'two' ];
- store.getOption.withArgs( 'one' ).returns( 'field one value' );
- store.getOption.withArgs( 'two' ).returns( 'field two value' );
+ store.option.withArgs( 'one' ).returns( { get: function () {
return 'field one value'; } } );
+ store.option.withArgs( 'two' ).returns( { get: function () {
return 'field two value'; } } );
var searchPreview = new SearchPreview( store, config );
searchPreview.showPreview();
@@ -139,13 +140,20 @@
assert.equal( searchPreview.$element.find(
'.mw-advancedSearch-searchPreview-previewPill' ).length, 2 );
} );
- QUnit.test( 'Hiding removes pills', 1, function ( assert ) {
+ QUnit.test( 'Hiding removes pills', 2, function ( assert ) {
config.previewOptions = [ 'one', 'two' ];
+ store.option.withArgs( 'one' ).returns( { get: function () {
return 'irrelevant'; } } );
+ store.option.withArgs( 'two' ).returns( { get: function () {
return 'irrelevant'; } } );
+
var searchPreview = new SearchPreview( store, config );
+
+ store.option = sandbox.spy();
+
searchPreview.hidePreview();
- assert.equal( searchPreview.$element.find(
'.mw-advancedSearch-searchPreview-previewPill' ).length, 0 );
+ assert.equal( searchPreview.$element.find(
'.mw-advancedSearch-searchPreview-previewPill' ).length, 0, 'None shown' );
+ assert.notOk( store.option.called, 'Values not polled from
store as no preview generated when hidden' );
} );
QUnit.test( 'Scalar values get formatted well', 3, function ( assert ) {
--
To view, visit https://gerrit.wikimedia.org/r/373035
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I16f9244fc5f77b71d4eba5bf3eceb1d8904a8ec9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/AdvancedSearch
Gerrit-Branch: master
Gerrit-Owner: Pablo Grass (WMDE) <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits