Henning Snater has uploaded a new change for review.
https://gerrit.wikimedia.org/r/101876
Change subject: Implemented basic front-end value formatters
......................................................................
Implemented basic front-end value formatters
(bug 56261)
This change introduces a NullFormatter as well as a formatter for the string
data type to
the front-end. In order to keep the change set size small, applying the
formatters will be
dealt with in a subsequent change set.
Change-Id: I10481061ac0ee925cd70b5e321153ba433cd7c26
---
M .jshintrc
M DataValuesCommon/DataValuesCommon.mw.php
A DataValuesCommon/js/ValueFormatters.resources.php
A DataValuesCommon/js/src/ValueFormatters/formatters/NullFormatter.js
A DataValuesCommon/js/src/ValueFormatters/formatters/StringFormatter.js
A DataValuesCommon/js/src/ValueFormatters/formatters/ValueFormatter.js
A DataValuesCommon/js/src/ValueFormatters/valueFormatters.js
A DataValuesCommon/js/src/ValueFormatters/valueFormatters.util.js
A DataValuesCommon/js/tests/ValueFormatters/ValueFormatter.tests.js
A DataValuesCommon/js/tests/ValueFormatters/formatters/NullFormatter.tests.js
A DataValuesCommon/js/tests/ValueFormatters/formatters/StringFormatter.tests.js
11 files changed, 546 insertions(+), 1 deletion(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues
refs/changes/76/101876/6
diff --git a/.jshintrc b/.jshintrc
index e5706a3..d9e8170 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -30,6 +30,7 @@
"predef": [
"dataValues",
"dataTypes",
+ "valueFormatters",
"valueParsers",
"time",
"globeCoordinate",
diff --git a/DataValuesCommon/DataValuesCommon.mw.php
b/DataValuesCommon/DataValuesCommon.mw.php
index d2f6296..85b7f78 100644
--- a/DataValuesCommon/DataValuesCommon.mw.php
+++ b/DataValuesCommon/DataValuesCommon.mw.php
@@ -106,7 +106,51 @@
};
/**
- * Hook to add QUnit test cases.
+ * Adding valueFormatters QUnit tests.
+ * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderTestModules
+ * @since 0.1
+ *
+ * @param array &$testModules
+ * @param \ResourceLoader &$resourceLoader
+ * @return boolean
+ */
+$wgHooks['ResourceLoaderTestModules'][] = function(
+ array &$testModules,
+ \ResourceLoader &$resourceLoader
+) {
+ // @codeCoverageIgnoreStart
+ $moduleTemplate = array(
+ 'localBasePath' => __DIR__ . '/js/tests/ValueFormatters',
+ 'remoteExtPath' =>
'DataValues/DataValuesCommon/js/tests/ValueFormatters',
+ );
+
+ $testModules['qunit']['ext.valueFormatters.tests'] = $moduleTemplate +
array(
+ 'scripts' => array(
+ 'ValueFormatter.tests.js',
+ ),
+ 'dependencies' => array(
+ 'valueFormatters',
+ 'valueFormatters.ValueFormatter',
+ ),
+ );
+
+ $testModules['qunit']['ext.valueFormatters.formatters'] =
$moduleTemplate + array(
+ 'scripts' => array(
+ 'formatters/NullFormatter.tests.js',
+ 'formatters/StringFormatter.tests.js',
+ ),
+ 'dependencies' => array(
+ 'ext.valueFormatters.tests',
+ 'valueFormatters.formatters',
+ ),
+ );
+
+ return true;
+ // @codeCoverageIgnoreEnd
+};
+
+/**
+ * Adding valueParsers QUnit tests.
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderTestModules
* @since 0.1
*
@@ -164,5 +208,6 @@
// Resource Loader module registration
$GLOBALS['wgResourceModules'] = array_merge(
$GLOBALS['wgResourceModules'],
+ include( __DIR__ . '/js/ValueFormatters.resources.php' ),
include( __DIR__ . '/js/ValueParsers.resources.mw.php' )
);
diff --git a/DataValuesCommon/js/ValueFormatters.resources.php
b/DataValuesCommon/js/ValueFormatters.resources.php
new file mode 100644
index 0000000..d26c392
--- /dev/null
+++ b/DataValuesCommon/js/ValueFormatters.resources.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Definition of ResourceLoader modules of the ValueFormatters extension.
+ * When included this returns an array with all the modules introduced by
ValueFormatters.
+ *
+ * @since 0.1
+ *
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ *
+ * @codeCoverageIgnoreStart
+ */
+return call_user_func( function() {
+
+ $moduleTemplate = array(
+ 'localBasePath' => __DIR__ . '/src/ValueFormatters',
+ 'remoteExtPath' =>
'DataValues/DataValuesCommon/js/src/ValueFormatters',
+ );
+
+ return array(
+
+ 'valueFormatters' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'valueFormatters.js',
+ ),
+ ),
+
+ 'valueFormatters.ValueFormatter' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'formatters/ValueFormatter.js',
+ ),
+ 'dependencies' => array(
+ 'valueFormatters',
+ 'valueFormatters.util',
+ ),
+ ),
+
+ 'valueFormatters.formatters' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'formatters/NullFormatter.js',
+ 'formatters/StringFormatter.js',
+ ),
+ 'dependencies' => array(
+ 'dataValues.values',
+ 'valueFormatters.ValueFormatter',
+ ),
+ ),
+
+ 'valueFormatters.util' => $moduleTemplate + array(
+ 'scripts' => array(
+ 'valueFormatters.util.js',
+ ),
+ 'dependencies' => array(
+ 'dataValues.util',
+ 'valueFormatters',
+ ),
+ ),
+
+ );
+
+} );
diff --git
a/DataValuesCommon/js/src/ValueFormatters/formatters/NullFormatter.js
b/DataValuesCommon/js/src/ValueFormatters/formatters/NullFormatter.js
new file mode 100644
index 0000000..0cbc20d
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/formatters/NullFormatter.js
@@ -0,0 +1,47 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, vf, dv ) {
+ 'use strict';
+
+ var PARENT = vf.ValueFormatter;
+
+ /**
+ * Null Formatter
+ * The Null Formatter formats any DataValue instance and may be used as
a fallback for DataValue
+ * instances that cannot be identified (e.g. due to missing
implementation). The formatted value
+ * will be an UnknownValue DataValue, except if null or a DataValue got
passed in. In those
+ * cases, the value passed to the format function will be the format
result.
+ *
+ * @constructor
+ * @extends valueFormatters.ValueFormatter
+ * @since 0.1
+ */
+ vf.NullFormatter = vf.util.inherit( PARENT, function() {}, {
+ /**
+ * @see valueFormatters.ValueFormatter.format
+ */
+ format: function( dataValue ) {
+ var deferred = $.Deferred();
+
+ if( dataValue === null ) {
+ return deferred.resolve( null, null ).promise();
+ }
+
+ if( !( dataValue instanceof dv.DataValue ) ) {
+ dataValue = new dv.UnknownValue( dataValue );
+ }
+
+ var formatted = dataValue.toJSON();
+
+ if( formatted !== null ) {
+ formatted = String( formatted );
+ }
+
+ return deferred.resolve( formatted, dataValue
).promise();
+ }
+
+ } );
+
+}( jQuery, valueFormatters, dataValues ) );
diff --git
a/DataValuesCommon/js/src/ValueFormatters/formatters/StringFormatter.js
b/DataValuesCommon/js/src/ValueFormatters/formatters/StringFormatter.js
new file mode 100644
index 0000000..666d064
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/formatters/StringFormatter.js
@@ -0,0 +1,33 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, vf ) {
+ 'use strict';
+
+ var PARENT = vf.ValueFormatter;
+
+ /**
+ * String formatter
+ * @constructor
+ * @extends valueFormatters.ValueFormatter
+ * @since 0.1
+ */
+ vf.StringFormatter = vf.util.inherit( PARENT, {
+ /**
+ * @see valueFormatters.ValueFormatter.format
+ * @since 0.1
+ *
+ * @param {dataValues.StringValue} dataValue
+ * @return {$.Promise}
+ */
+ format: function( dataValue ) {
+ var deferred = $.Deferred();
+
+ deferred.resolve( dataValue.toJSON(), dataValue );
+
+ return deferred.promise();
+ }
+ } );
+
+}( jQuery, valueFormatters ) );
diff --git
a/DataValuesCommon/js/src/ValueFormatters/formatters/ValueFormatter.js
b/DataValuesCommon/js/src/ValueFormatters/formatters/ValueFormatter.js
new file mode 100644
index 0000000..e9707d1
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/formatters/ValueFormatter.js
@@ -0,0 +1,61 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, vf ) {
+ 'use strict';
+
+ /**
+ * Base constructor for objects representing a value formatter.
+ * @constructor
+ * @abstract
+ * @since 0.1
+ *
+ * @param {Object} options
+ */
+ var SELF = vf.ValueFormatter = function VpValueFormatter( options ) {
+ this._options = $.extend( {}, options || {} );
+ };
+
+ $.extend( SELF.prototype, {
+ /**
+ * Formatter options.
+ * @type {Object}
+ */
+ _options: null,
+
+ /**
+ * Returns the formatter's options as set in the constructor.
+ * @since 0.1
+ *
+ * @return {Object}
+ */
+ getOptions: function() {
+ return $.extend( {}, this._options );
+ },
+
+ /**
+ * Formats a value. Will return a jQuery.Promise which will be
resolved if formatting is
+ * successful or rejected if it fails. There are various
reasons why formatting could fail,
+ * e.g. the formatter is using the API and the API cannot be
reached. In case of success,
+ * the callbacks will be passed a dataValues.DataValue object.
In case of failure, the
+ * callback's parameter will be an error object of some sort
(not implemented yet!).
+ *
+ * TODO: Specify Error object for formatter failure. Consider
different error scenarios e.g.
+ * API can not be reached or real formatting issues.
+ * TODO: Think about introducing formatter warnings or a status
object in done() callbacks.
+ *
+ * @since 0.1
+ *
+ * @param {dataValues.DataValue} dataValue
+ *
+ * @return {$.Promise}
+ * Parameters:
+ * - {string|null} Formatted DataValue.
+ * - {dataValues.DataValue|null} DataValue object that
has been formatted.
+ */
+ format: vf.util.abstractMember
+
+ } );
+
+}( jQuery, valueFormatters ) );
diff --git a/DataValuesCommon/js/src/ValueFormatters/valueFormatters.js
b/DataValuesCommon/js/src/ValueFormatters/valueFormatters.js
new file mode 100644
index 0000000..130a480
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/valueFormatters.js
@@ -0,0 +1,11 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+
+/**
+ * Global 'valueFormatters' object
+ * @since 0.1
+ * @type {Object}
+ */
+this.valueFormatters = this.valueFormatters || {};
diff --git a/DataValuesCommon/js/src/ValueFormatters/valueFormatters.util.js
b/DataValuesCommon/js/src/ValueFormatters/valueFormatters.util.js
new file mode 100644
index 0000000..0f97e79
--- /dev/null
+++ b/DataValuesCommon/js/src/ValueFormatters/valueFormatters.util.js
@@ -0,0 +1,27 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, vf, dv ) {
+ 'use strict';
+
+ /**
+ * Module for utilities of the ValueFormatters extension.
+ * @since 0.1
+ * @type {Object}
+ */
+ vf.util = {};
+
+ /**
+ * @see dataValues.util.inherit
+ * @since 0.1
+ */
+ vf.util.inherit = dv.util.inherit;
+
+ /**
+ * @see dataValues.util.abstractMember
+ * @since 0.1
+ */
+ vf.util.abstractMember = dv.util.abstractMember;
+
+}( jQuery, valueFormatters, dataValues ) );
diff --git a/DataValuesCommon/js/tests/ValueFormatters/ValueFormatter.tests.js
b/DataValuesCommon/js/tests/ValueFormatters/ValueFormatter.tests.js
new file mode 100644
index 0000000..47afaf5
--- /dev/null
+++ b/DataValuesCommon/js/tests/ValueFormatters/ValueFormatter.tests.js
@@ -0,0 +1,164 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( $, QUnit, vf ) {
+ 'use strict';
+
+ vf.tests = {};
+
+ /**
+ * Base constructor for ValueFormatter object tests
+ *
+ * @constructor
+ * @abstract
+ * @since 0.1
+ */
+ vf.tests.ValueFormatterTest = function() {};
+ vf.tests.ValueFormatterTest.prototype = {
+
+ /**
+ * Data provider that provides valid format arguments.
+ * @since 0.1
+ *
+ * @return {*[]}
+ */
+ getFormatArguments: vf.util.abstractMember,
+
+ /**
+ * Returns the ValueFormatter constructor to be tested.
+ * @since 0.1
+ *
+ * @return {Function}
+ */
+ getConstructor: vf.util.abstractMember,
+
+ /**
+ * Returns the ValueFormatter instance to be tested.
+ * @since 0.1
+ *
+ * @param {*[]} constructorArguments
+ * @return {valueFormatters.ValueFormatter}
+ */
+ getInstance: function( constructorArguments ) {
+ constructorArguments = constructorArguments ||
this.getDefaultConstructorArgs();
+
+ var self = this;
+
+ var ValueFormatterConstructor = function(
constructorArguments ) {
+ self.getConstructor().apply( this,
constructorArguments );
+ };
+
+ ValueFormatterConstructor.prototype =
this.getConstructor().prototype;
+ return new ValueFormatterConstructor(
constructorArguments );
+ },
+
+ getDefaultConstructorArgs: function() {
+ return [];
+ },
+
+ /**
+ * Runs the tests.
+ * @since 0.1
+ *
+ * @param {string} moduleName
+ */
+ runTests: function( moduleName ) {
+ QUnit.module( moduleName );
+
+ var self = this;
+
+ $.each( this, function( property, value ) {
+ if( property.substring( 0, 4 ) === 'test' &&
$.isFunction( self[property] ) ) {
+ QUnit.test(
+ property,
+ function( assert ) {
+ self[property]( assert
);
+ }
+ );
+ }
+ } );
+ },
+
+ /**
+ * Tests the format method.
+ * @since 0.1
+ *
+ * @param {Function} assert
+ */
+ testFormat: function( assert ) {
+ var formatArguments = this.getFormatArguments(),
+ formatter = this.getInstance(),
+ requests = [];
+
+ // Prevent continuing with subsequent tests until all
formatter procedures have
+ // finished:
+ QUnit.stop();
+
+ $.each( formatArguments, function( i, args ) {
+ var formatInput = args[0],
+ expected = args[1],
+ expectedValue = expected,
+ expectedDataValue,
+ inputDetailMsg = typeof formatInput ===
'string'
+ ? 'for input "' + formatInput +
'" '
+ : '',
+ request;
+
+ if( $.isArray( expected ) ) {
+ expectedValue = expected[0];
+ expectedDataValue = expected[1];
+ } else {
+ expectedDataValue = formatInput;
+ }
+
+ request = formatter.format( formatInput )
+ .done( function( formattedValue, dataValue ) {
+ assert.ok( true, 'Formatting
succeeded.' );
+
+ if( formattedValue === null ) {
+ assert.strictEqual(
+ formattedValue,
+ null,
+ 'Formatting result is
null.'
+ );
+ } else {
+ assert.strictEqual(
+ typeof formattedValue,
+ 'string',
+ 'Formatting result is a
string: ' + formattedValue
+ );
+ }
+
+ assert.ok(
+ expectedValue ===
formattedValue,
+ 'Formatting result ' +
inputDetailMsg + 'matches the expected result.'
+ );
+
+ assert.ok(
+ dataValue === expectedDataValue
|| dataValue.equals( expectedDataValue ),
+ 'Returned DataValue ' +
inputDetailMsg + 'is equal to the expected '
+ + 'DataValue.'
+ );
+
+ } )
+ .fail( function( errorMessage ) {
+ assert.ok(
+ false,
+ 'Formatting ' + inputDetailMsg
+ 'failed: ' + errorMessage
+ );
+ } );
+
+ requests.push( request );
+ } );
+
+ // Only continue with next test after all formatter
procedures are finished:
+ $.when.apply( null, requests ).always( function() {
+ QUnit.start();
+ } );
+
+ }
+
+ };
+
+}( jQuery, QUnit, valueFormatters ) );
diff --git
a/DataValuesCommon/js/tests/ValueFormatters/formatters/NullFormatter.tests.js
b/DataValuesCommon/js/tests/ValueFormatters/formatters/NullFormatter.tests.js
new file mode 100644
index 0000000..094dcb9
--- /dev/null
+++
b/DataValuesCommon/js/tests/ValueFormatters/formatters/NullFormatter.tests.js
@@ -0,0 +1,51 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( vf, dv ) {
+ 'use strict';
+
+ var PARENT = vf.tests.ValueFormatterTest,
+ constructor = function() {
+ };
+
+ /**
+ * Constructor for creating a test object containing tests for the
NullFormatter.
+ *
+ * @constructor
+ * @extends valueFormatters.tests.ValueFormatterTest
+ * @since 0.1
+ */
+ vf.tests.NullParserTest = vf.util.inherit( PARENT, constructor, {
+
+ /**
+ * @see valueFormatters.tests.ValueFormatterTest.getConstructor
+ */
+ getConstructor: function() {
+ return vf.NullFormatter;
+ },
+
+ /**
+ * @see
valueFormatters.tests.ValueFormatterTest.getFormatArguments
+ */
+ getFormatArguments: function() {
+ var date = new Date(),
+ list = [ true, false, null ];
+
+ return [
+ [ new dv.UnknownValue( 'foo' ), 'foo' ],
+ [ null, null ],
+ [ 'plain string', [ 'plain string', new
dv.UnknownValue( 'plain string' ) ] ],
+ [ -99.9, [ '-99.9', new dv.UnknownValue( -99.9
) ] ],
+ [ date, [ String( date ), new dv.UnknownValue(
date ) ] ],
+ [ list, [ String( list ), new dv.UnknownValue(
list ) ] ]
+ ];
+ }
+
+ } );
+
+ var test = new vf.tests.NullParserTest();
+
+ test.runTests( 'valueFormatters.NullFormatter' );
+
+}( valueFormatters, dataValues ) );
diff --git
a/DataValuesCommon/js/tests/ValueFormatters/formatters/StringFormatter.tests.js
b/DataValuesCommon/js/tests/ValueFormatters/formatters/StringFormatter.tests.js
new file mode 100644
index 0000000..e428963
--- /dev/null
+++
b/DataValuesCommon/js/tests/ValueFormatters/formatters/StringFormatter.tests.js
@@ -0,0 +1,44 @@
+/**
+ * @licence GNU GPL v2+
+ * @author H. Snater < [email protected] >
+ */
+( function( vf, dv ) {
+ 'use strict';
+
+ var PARENT = vf.tests.ValueFormatterTest;
+
+ /**
+ * Constructor for creating a test object containing tests for the
StringFormatter.
+ *
+ * @constructor
+ * @extends valueFormatters.tests.ValueFormatterTest
+ * @since 0.1
+ */
+ vf.tests.StringFormatterTest = vf.util.inherit( PARENT, {
+
+ /**
+ * @see valueFormatters.tests.ValueFormatterTest.getObject
+ */
+ getConstructor: function() {
+ return vf.StringFormatter;
+ },
+
+ /**
+ * @see
valueFormatters.tests.ValueFormatterTest.getFormatArguments
+ */
+ getFormatArguments: function() {
+ return [
+ [ new dv.StringValue( 'some string' ), 'some
string' ],
+ [ new dv.StringValue( ' foo ' ), ' foo ' ],
+ [ new dv.StringValue( ' xXx' ), ' xXx' ],
+ [ new dv.StringValue( 'xXx ' ), 'xXx ' ]
+ ];
+ }
+
+ } );
+
+ var test = new vf.tests.StringFormatterTest();
+
+ test.runTests( 'valueFormatters.StringFormatter' );
+
+}( valueFormatters, dataValues ) );
--
To view, visit https://gerrit.wikimedia.org/r/101876
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I10481061ac0ee925cd70b5e321153ba433cd7c26
Gerrit-PatchSet: 6
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Henning Snater <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Daniel Werner <[email protected]>
Gerrit-Reviewer: Jeroen De Dauw <[email protected]>
Gerrit-Reviewer: Tobias Gritschacher <[email protected]>
Gerrit-Reviewer: jenkins-bot
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits