jenkins-bot has submitted this change and it was merged.

Change subject: Introduce wikibase.entityIdFormatter
......................................................................


Introduce wikibase.entityIdFormatter

This wraps entity fetching and calling the EntityId formatting functions from
`wikibase.utilities.ui` in new service classes and passes them to the views.

It is a necessary prerequisite for being able to swap out the EntityId
formatting logic with a different implementation. It also frees the views from
knowing how to format EntityIds and removes the duplicate EntityId formatting
code.

Change-Id: I4a2fdfbc3faa8a640683006111539812ab764faf
---
M .jshintrc
M view/resources/jquery/wikibase/jquery.wikibase.referenceview.js
M view/resources/jquery/wikibase/jquery.wikibase.snaklistview.js
M view/resources/jquery/wikibase/jquery.wikibase.statementgrouplistview.js
M view/resources/jquery/wikibase/jquery.wikibase.statementgroupview.js
M view/resources/jquery/wikibase/jquery.wikibase.statementlistview.js
M view/resources/jquery/wikibase/jquery.wikibase.statementview.js
M view/resources/jquery/wikibase/resources.php
M view/resources/jquery/wikibase/snakview/resources.php
M view/resources/jquery/wikibase/snakview/snakview.js
M view/resources/resources.php
A view/resources/wikibase/entityIdFormatter/EntityIdHtmlFormatter.js
A view/resources/wikibase/entityIdFormatter/EntityIdPlainFormatter.js
A view/resources/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.js
A view/resources/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.js
A view/resources/wikibase/entityIdFormatter/namespace.js
A view/resources/wikibase/entityIdFormatter/resources.php
M view/tests/qunit/jquery/wikibase/jquery.wikibase.itemview.tests.js
M view/tests/qunit/jquery/wikibase/jquery.wikibase.propertyview.tests.js
M view/tests/qunit/jquery/wikibase/jquery.wikibase.referenceview.tests.js
M view/tests/qunit/jquery/wikibase/jquery.wikibase.snaklistview.tests.js
M 
view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgrouplistview.tests.js
M view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgroupview.tests.js
M view/tests/qunit/jquery/wikibase/jquery.wikibase.statementlistview.tests.js
M view/tests/qunit/jquery/wikibase/jquery.wikibase.statementview.tests.js
M view/tests/qunit/jquery/wikibase/resources.php
M view/tests/qunit/jquery/wikibase/snakview/resources.php
M view/tests/qunit/jquery/wikibase/snakview/snakview.tests.js
M view/tests/qunit/resources.php
A 
view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.tests.js
A 
view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.tests.js
A view/tests/qunit/wikibase/entityIdFormatter/resources.php
A view/tests/qunit/wikibase/entityIdFormatter/testEntityIdHtmlFormatter.js
33 files changed, 659 insertions(+), 123 deletions(-)

Approvals:
  Hoo man: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/.jshintrc b/.jshintrc
index 988c787..eeade04 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -50,6 +50,11 @@
                        "globals": {
                                "QUnit": false
                        }
+               },
+               
"view/tests/qunit/wikibase/entityIdFormatter/testEntityIdHtmlFormatter.js": {
+                       "globals": {
+                               "QUnit": false
+                       }
                }
 
        }
diff --git a/view/resources/jquery/wikibase/jquery.wikibase.referenceview.js 
b/view/resources/jquery/wikibase/jquery.wikibase.referenceview.js
index 1a9ee33..07e9246 100644
--- a/view/resources/jquery/wikibase/jquery.wikibase.referenceview.js
+++ b/view/resources/jquery/wikibase/jquery.wikibase.referenceview.js
@@ -18,6 +18,10 @@
  * @param {wikibase.datamodel.Reference|null} options.value
  * @param {string} options.statementGuid
  *        The GUID of the `Statement` the `Reference` represented by the 
widget instance belongs to.
+ * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} 
options.entityIdHtmlFormatter
+ *        Required for dynamically rendering links to `Entity`s.
+ * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} 
options.entityIdPlainFormatter
+ *        Required for dynamically rendering plain text references to 
`Entity`s.
  * @param {wikibase.store.EntityStore} options.entityStore
  *        Required for dynamically gathering `Entity`/`Property` information.
  * @param {wikibase.ValueViewBuilder} options.valueViewBuilder
@@ -74,6 +78,8 @@
                },
                value: null,
                statementGuid: null,
+               entityIdHtmlFormatter: null,
+               entityIdPlainFormatter: null,
                entityStore: null,
                valueViewBuilder: null,
                referencesChanger: null,
@@ -113,6 +119,8 @@
                                                value: value || undefined,
                                                singleProperty: true,
                                                dataTypeStore: 
self.options.dataTypeStore,
+                                               entityIdHtmlFormatter: 
self.options.entityIdHtmlFormatter,
+                                               entityIdPlainFormatter: 
self.options.entityIdPlainFormatter,
                                                entityStore: 
self.options.entityStore,
                                                valueViewBuilder: 
self.options.valueViewBuilder
                                        };
diff --git a/view/resources/jquery/wikibase/jquery.wikibase.snaklistview.js 
b/view/resources/jquery/wikibase/jquery.wikibase.snaklistview.js
index 00a0283..a28cb3b 100644
--- a/view/resources/jquery/wikibase/jquery.wikibase.snaklistview.js
+++ b/view/resources/jquery/wikibase/jquery.wikibase.snaklistview.js
@@ -22,6 +22,10 @@
  * @param {boolean} [singleProperty=true]
  *        If `true`, it is assumed that the widget is filled with `Snak`s 
featuring a single common
  *        property.
+ * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} 
options.entityIdHtmlFormatter
+ *        Required for dynamically rendering links to `Entity`s.
+ * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} 
options.entityIdPlainFormatter
+ *        Required for dynamically rendering plain text references to 
`Entity`s.
  * @param {wikibase.store.EntityStore} options.entityStore
  *        Required for dynamically gathering `Entity`/`Property` information.
  * @param {wikibase.ValueViewBuilder} options.valueViewBuilder
@@ -74,6 +78,8 @@
                value: null,
                singleProperty: false,
                helpMessage: mw.msg( 'wikibase-claimview-snak-new-tooltip' ),
+               entityIdHtmlFormatter: null,
+               entityIdPlainFormatter: null,
                entityStore: null,
                valueViewBuilder: null,
                dataTypeStore: null
@@ -160,6 +166,8 @@
                                                        property: !!value
                                                },
                                                dataTypeStore: self.option( 
'dataTypeStore' ),
+                                               entityIdHtmlFormatter: 
self.options.entityIdHtmlFormatter,
+                                               entityIdPlainFormatter: 
self.options.entityIdPlainFormatter,
                                                entityStore: self.option( 
'entityStore' ),
                                                valueViewBuilder: self.option( 
'valueViewBuilder' )
                                        };
diff --git 
a/view/resources/jquery/wikibase/jquery.wikibase.statementgrouplistview.js 
b/view/resources/jquery/wikibase/jquery.wikibase.statementgrouplistview.js
index 45518c7..797a27e 100644
--- a/view/resources/jquery/wikibase/jquery.wikibase.statementgrouplistview.js
+++ b/view/resources/jquery/wikibase/jquery.wikibase.statementgrouplistview.js
@@ -115,6 +115,12 @@
                        $listview = $( '<div/>' ).appendTo( this.element );
                }
 
+               var entityIdHtmlFormatter = new 
wb.entityIdFormatter.SimpleEntityIdHtmlFormatter(
+                       this.options.entityStore
+               );
+               var entityIdPlainFormatter = new 
wb.entityIdFormatter.SimpleEntityIdPlainFormatter(
+                       this.options.entityStore
+               );
                $listview.listview( {
                        listItemAdapter: new 
$.wikibase.listview.ListItemAdapter( {
                                listItemWidget: $.wikibase.statementgroupview,
@@ -123,6 +129,8 @@
                                                value: value,
                                                claimGuidGenerator: 
self.options.claimGuidGenerator,
                                                dataTypeStore: 
self.options.dataTypeStore,
+                                               entityIdHtmlFormatter: 
entityIdHtmlFormatter,
+                                               entityIdPlainFormatter: 
entityIdPlainFormatter,
                                                entityStore: 
self.options.entityStore,
                                                valueViewBuilder: 
self.options.valueViewBuilder,
                                                entityChangersFactory: 
self.options.entityChangersFactory
diff --git 
a/view/resources/jquery/wikibase/jquery.wikibase.statementgroupview.js 
b/view/resources/jquery/wikibase/jquery.wikibase.statementgroupview.js
index 6104bca..9509ca2 100644
--- a/view/resources/jquery/wikibase/jquery.wikibase.statementgroupview.js
+++ b/view/resources/jquery/wikibase/jquery.wikibase.statementgroupview.js
@@ -24,6 +24,10 @@
  *        "add" button to add new `Statements`.
  * @param {wikibase.utilities.ClaimGuidGenerator} options.claimGuidGenerator
  *        Required for dynamically generating GUIDs for new `Statement`s.
+ * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} 
options.entityIdHtmlFormatter
+ *        Required for dynamically rendering links to `Entity`s.
+ * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} 
options.entityIdPlainFormatter
+ *        Required for dynamically rendering plain text references to 
`Entity`s.
  * @param {wikibase.store.EntityStore} options.entityStore
  *        Required for dynamically gathering `Entity`/`Property` information.
  * @param {wikibase.ValueViewBuilder} options.valueViewBuilder
@@ -60,6 +64,8 @@
                },
                value: null,
                claimGuidGenerator: null,
+               entityIdHtmlFormatter: null,
+               entityIdPlainFormatter: null,
                entityStore: null,
                valueViewBuilder: null,
                entityChangersFactory: null,
@@ -80,6 +86,7 @@
        _create: function() {
                if(
                        !this.options.claimGuidGenerator
+                       || !this.options.entityIdHtmlFormatter
                        || !this.options.entityStore
                        || !this.options.valueViewBuilder
                        || !this.options.entityChangersFactory
@@ -118,22 +125,8 @@
                var self = this,
                        propertyId = this.options.value.getKey();
 
-               this.options.entityStore.get( propertyId ).done( function( 
property ) {
-                       var $title;
-
-                       if( property ) {
-                               $title = wb.utilities.ui.buildLinkToEntityPage(
-                                       property.getContent(),
-                                       property.getTitle()
-                               );
-                       } else {
-                               $title = wb.utilities.ui.buildMissingEntityInfo(
-                                       propertyId,
-                                       wb.datamodel.Property
-                               );
-                       }
-
-                       self.$propertyLabel.append( $title );
+               this.options.entityIdHtmlFormatter.format( propertyId ).done( 
function( title ) {
+                       self.$propertyLabel.append( title );
                } );
        },
 
@@ -155,6 +148,8 @@
                                ? this.options.value.getItemContainer()
                                : new wb.datamodel.StatementList(),
                        claimGuidGenerator: this.options.claimGuidGenerator,
+                       entityIdHtmlFormatter: 
this.options.entityIdHtmlFormatter,
+                       entityIdPlainFormatter: 
this.options.entityIdPlainFormatter,
                        entityStore: this.options.entityStore,
                        valueViewBuilder: this.options.valueViewBuilder,
                        entityChangersFactory: 
this.options.entityChangersFactory,
diff --git 
a/view/resources/jquery/wikibase/jquery.wikibase.statementlistview.js 
b/view/resources/jquery/wikibase/jquery.wikibase.statementlistview.js
index f23ceb3..2fd2ebe 100644
--- a/view/resources/jquery/wikibase/jquery.wikibase.statementlistview.js
+++ b/view/resources/jquery/wikibase/jquery.wikibase.statementlistview.js
@@ -28,6 +28,10 @@
  *        with edit mode being started.
  * @param {wikibase.utilities.ClaimGuidGenerator} options.claimGuidGenerator
  *        Required for dynamically generating GUIDs for new `Statement`s.
+ * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} 
options.entityIdHtmlFormatter
+ *        Required for dynamically rendering links to `Entity`s.
+ * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} 
options.entityIdPlainFormatter
+ *        Required for dynamically rendering plain text references to 
`Entity`s.
  * @param {wikibase.store.EntityStore} options.entityStore
  *        Required for dynamically gathering `Entity`/`Property` information.
  * @param {wikibase.ValueViewBuilder} options.valueViewBuilder
@@ -81,6 +85,8 @@
                },
                value: null,
                claimGuidGenerator: null,
+               entityIdHtmlFormatter: null,
+               entityIdPlainFormatter: null,
                entityStore: null,
                valueViewBuilder: null,
                entityChangersFactory: null,
@@ -197,6 +203,8 @@
                                                        }
                                                },
                                                dataTypeStore: 
self.options.dataTypeStore,
+                                               entityIdHtmlFormatter: 
self.options.entityIdHtmlFormatter,
+                                               entityIdPlainFormatter: 
self.options.entityIdPlainFormatter,
                                                entityStore: 
self.options.entityStore,
                                                valueViewBuilder: 
self.options.valueViewBuilder,
                                                claimsChanger: 
self._claimsChanger,
diff --git a/view/resources/jquery/wikibase/jquery.wikibase.statementview.js 
b/view/resources/jquery/wikibase/jquery.wikibase.statementview.js
index ee574da..4bca237 100644
--- a/view/resources/jquery/wikibase/jquery.wikibase.statementview.js
+++ b/view/resources/jquery/wikibase/jquery.wikibase.statementview.js
@@ -37,6 +37,10 @@
  *        Required for dynamically generating GUIDs for new `Statement`s.
  * @param {wikibase.entityChangers.ClaimsChanger} options.claimsChanger
  *        Required to store the view's `Statement`.
+ * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} 
options.entityIdHtmlFormatter
+ *        Required for dynamically rendering links to `Entity`s.
+ * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} 
options.entityIdPlainFormatter
+ *        Required for dynamically rendering plain text references to 
`Entity`s.
  * @param {wikibase.entityChangers.ReferencesChanger} options.referencesChanger
  *        Required to store the `Reference`s gathered from the 
`referenceview`s aggregated by the
  *        `statementview`.
@@ -102,6 +106,8 @@
                claimsChanger: null,
                referencesChanger: null,
                dataTypeStore: null,
+               entityIdHtmlFormatter: null,
+               entityIdPlainFormatter: null,
                predefined: {
                        mainSnak: false
                },
@@ -209,6 +215,8 @@
                        locked: this.options.locked.mainSnak,
                        autoStartEditing: false,
                        dataTypeStore: this.options.dataTypeStore,
+                       entityIdHtmlFormatter: 
this.options.entityIdHtmlFormatter,
+                       entityIdPlainFormatter: 
this.options.entityIdPlainFormatter,
                        entityStore: this.options.entityStore,
                        valueViewBuilder: this.options.valueViewBuilder,
                        encapsulatedBy: ':' + this.widgetFullName.toLowerCase()
@@ -253,6 +261,8 @@
                                                value: value || undefined,
                                                singleProperty: true,
                                                dataTypeStore: 
self.options.dataTypeStore,
+                                               entityIdHtmlFormatter: 
self.options.entityIdHtmlFormatter,
+                                               entityIdPlainFormatter: 
self.options.entityIdPlainFormatter,
                                                entityStore: 
self.options.entityStore,
                                                valueViewBuilder: 
self.options.valueViewBuilder
                                        };
@@ -314,6 +324,8 @@
                                                        ? 
self.options.value.getClaim().getGuid()
                                                        : null,
                                                dataTypeStore: 
self.options.dataTypeStore,
+                                               entityIdHtmlFormatter: 
self.options.entityIdHtmlFormatter,
+                                               entityIdPlainFormatter: 
self.options.entityIdPlainFormatter,
                                                entityStore: 
self.options.entityStore,
                                                valueViewBuilder: 
self.options.valueViewBuilder,
                                                referencesChanger: 
self.options.referencesChanger
@@ -396,14 +408,8 @@
                                : this.options.predefined.mainSnak.property;
 
                        if( property ) {
-                               this.options.entityStore.get( property ).done( 
function( fetchedProperty ) {
-                                       if( fetchedProperty ) {
-                                               helpMessage = mw.msg(
-                                                       
'wikibase-claimview-snak-tooltip',
-                                                       
wb.utilities.ui.buildPrettyEntityLabelText( fetchedProperty.getContent() )
-                                               );
-                                       }
-                                       deferred.resolve( helpMessage );
+                               this.options.entityIdPlainFormatter.format( 
property ).done( function( formattedEntityId ) {
+                                       deferred.resolve( mw.msg( 
'wikibase-claimview-snak-tooltip', formattedEntityId ) );
                                } );
                        } else {
                                deferred.resolve( helpMessage );
diff --git a/view/resources/jquery/wikibase/resources.php 
b/view/resources/jquery/wikibase/resources.php
index 72180f2..316a31b 100644
--- a/view/resources/jquery/wikibase/resources.php
+++ b/view/resources/jquery/wikibase/resources.php
@@ -71,6 +71,8 @@
                                'jquery.wikibase.listview',
                                'wikibase.datamodel.Item',
                                'wikibase.datamodel.StatementGroupSet',
+                               
'wikibase.entityIdFormatter.SimpleEntityIdHtmlFormatter',
+                               
'wikibase.entityIdFormatter.SimpleEntityIdPlainFormatter',
                        ),
                ),
 
@@ -87,7 +89,7 @@
                                'wikibase.datamodel.Property',
                                'wikibase.datamodel.StatementGroup',
                                'wikibase.datamodel.StatementList',
-                               'wikibase.utilities',
+                               'wikibase.utilities.ClaimGuidGenerator',
                        ),
                ),
 
@@ -356,7 +358,6 @@
                                'mediawiki.jqueryMsg', // for {{plural}} and 
{{gender}} support in messages
                                'wikibase.buildErrorOutput',
                                'wikibase.sites',
-                               'wikibase.utilities',
                        ),
                ),
 
diff --git a/view/resources/jquery/wikibase/snakview/resources.php 
b/view/resources/jquery/wikibase/snakview/resources.php
index f6f391b..5328ec0 100644
--- a/view/resources/jquery/wikibase/snakview/resources.php
+++ b/view/resources/jquery/wikibase/snakview/resources.php
@@ -40,7 +40,6 @@
                                'wikibase.datamodel',
                                'wikibase.serialization.SnakDeserializer',
                                'wikibase.serialization.SnakSerializer',
-                               'wikibase.utilities',
                        ),
                        'messages' => array(
                                'wikibase-snakview-property-input-placeholder',
diff --git a/view/resources/jquery/wikibase/snakview/snakview.js 
b/view/resources/jquery/wikibase/snakview/snakview.js
index 4bd21cd..439ac06 100644
--- a/view/resources/jquery/wikibase/snakview/snakview.js
+++ b/view/resources/jquery/wikibase/snakview/snakview.js
@@ -35,6 +35,10 @@
  * @param {boolean} [options.autoStartEditing=true]
  *        Whether the `snakview` should switch to edit mode automatically upon 
initialization if its
  *        initial value is empty.
+ * @param {wikibase.entityIdFormatter.EntityIdHtmlFormatter} 
options.entityIdHtmlFormatter
+ *        Required for dynamically rendering links to `Entity`s.
+ * @param {wikibase.entityIdFormatter.EntityIdPlainFormatter} 
options.entityIdPlainFormatter
+ *        Required for dynamically rendering plain text references to 
`Entity`s.
  * @param {wikibase.store.EntityStore} options.entityStore
  *        Required for dynamically gathering `Entity`/`Property` information.
  * @param {wikibase.ValueViewBuilder} options.valueViewBuilder
@@ -90,6 +94,8 @@
                        snaktype: false
                },
                autoStartEditing: true,
+               entityIdPlainFormatter: null,
+               entityIdHtmlFormatter: null,
                entityStore: null,
                valueViewBuilder: null,
                dataTypeStore: null,
@@ -132,6 +138,14 @@
         * @protected
         */
        _create: function() {
+               if( this.options.locked === true || 
this.options.locked.property === true ) {
+                       if( !(
+                               this.options.value instanceof wb.datamodel.Snak 
|| ( this.options.value && this.options.value.property )
+                       ) ) {
+                               mw.log.warn( 'You cannot lock the property 
without specifying a property' );
+                       }
+               }
+
                PARENT.prototype._create.call( this );
 
                this._cachedValues = {};
@@ -728,66 +742,56 @@
         */
        _getPropertyDOM: function( propertyId ) {
                var self = this,
-                       deferred = $.Deferred();
+                       deferred = $.Deferred(),
+                       editable = !this.options.locked.property && 
this.isInEditMode();
 
-               if( propertyId ) {
-                       this.options.entityStore.get( propertyId )
-                       .done( function( fetchedProperty ) {
-                               deferred.resolve( self._createPropertyDOM(
-                                       fetchedProperty ? 
fetchedProperty.getContent() : propertyId,
-                                       fetchedProperty ? 
fetchedProperty.getTitle() : undefined
-                               ) );
-                       } )
-                       .fail( deferred.reject );
+               if( !propertyId ) {
+                       if( editable ) {
+                               deferred.resolve( this._createPropertyDOM( '' ) 
);
+                       } else {
+                               deferred.resolve( '' );
+                       }
                } else {
-                       deferred.resolve( this._createPropertyDOM() );
+                       if( editable ) {
+                               this.options.entityIdPlainFormatter.format( 
propertyId ).done( function( propertyLabel ) {
+                                       deferred.resolve( 
self._createPropertyDOM( propertyLabel ) );
+                               } );
+                       } else {
+                               // Property is set already and cannot be 
changed, display label only:
+                               return 
this.options.entityIdHtmlFormatter.format( propertyId );
+                       }
                }
-
                return deferred.promise();
        },
 
        /**
-        * Creates the DOM structure specific for a `Property`-`Title` 
combination, a generic DOM
-        * structure or an input element if parameters are omitted.
+        * Creates the DOM structure specific for a `Property`, a generic DOM
+        * structure or an input element.
         * @private
         *
-        * @param {wikibase.datamodel.Property|string} [property] `Property` 
object or the property's
-        * ID string. Returns generic DOM structure if omitted.
-        * @param {mediawiki.Title} [title]
-        * @return {jQuery}
+        * @param {string} propertyLabel Rendered label for the `Property`
+        * @return {jQuery|null}
         */
-       _createPropertyDOM: function( property, title ) {
+       _createPropertyDOM: function( propertyLabel ) {
                var $propertyDom;
 
-               if( this.options.locked.property || !this.isInEditMode() ) {
-                       // Property is set already and cannot be changed, 
display label only:
-                       $propertyDom = property instanceof wb.datamodel.Property
-                               ? wb.utilities.ui.buildLinkToEntityPage( 
property, title )
-                               // shouldn't usually happen, only in non-edit 
mode, while no Snak is set:
-                               : wb.utilities.ui.buildMissingEntityInfo( 
property, wb.datamodel.Property );
-               } else {
-                       // No Property set for this Snak, serve edit view to 
specify it:
-                       var propertySelector = this._getPropertySelector(),
-                               propertyLabel = property instanceof 
wb.datamodel.Property
-                                       ? 
wb.utilities.ui.buildPrettyEntityLabelText( property )
-                                       : property;
+               // No Property set for this Snak, serve edit view to specify it:
+               var propertySelector = this._getPropertySelector();
 
-                       // TODO: use selectedEntity() or other command to set 
selected entity in both cases!
-                       if( propertySelector ) {
-                               // property selector in DOM already, just 
replace current value
-                               var currentValue = 
propertySelector.widget().val();
-                               // Impose case-insensitivity:
-                               if( propertyLabel.toLowerCase() !== 
currentValue.toLocaleLowerCase() ) {
-                                       propertySelector.widget().val( 
propertyLabel );
-                               }
-                       } else {
-                               $propertyDom = 
this._buildPropertySelector().val( propertyLabel );
-
-                               // propagate snakview state:
-                               $propertyDom.data( 'entityselector' ).option( 
'disabled', this.options.disabled );
+               // TODO: use selectedEntity() or other command to set selected 
entity in both cases!
+               if( propertySelector ) {
+                       // property selector in DOM already, just replace 
current value
+                       var currentValue = propertySelector.widget().val();
+                       // Impose case-insensitivity:
+                       if( propertyLabel.toLowerCase() !== 
currentValue.toLocaleLowerCase() ) {
+                               propertySelector.widget().val( propertyLabel );
                        }
-               }
+               } else {
+                       $propertyDom = this._buildPropertySelector().val( 
propertyLabel );
 
+                       // propagate snakview state:
+                       $propertyDom.data( 'entityselector' ).option( 
'disabled', this.options.disabled );
+               }
                return $propertyDom;
        },
 
diff --git a/view/resources/resources.php b/view/resources/resources.php
index 1ead957..0c1132b 100644
--- a/view/resources/resources.php
+++ b/view/resources/resources.php
@@ -11,6 +11,7 @@
                include __DIR__ . '/jquery/wikibase/resources.php',
                include __DIR__ . '/wikibase/resources.php',
                include __DIR__ . '/wikibase/entityChangers/resources.php',
+               include __DIR__ . '/wikibase/entityIdFormatter/resources.php',
                include __DIR__ . '/wikibase/store/resources.php',
                include __DIR__ . '/wikibase/utilities/resources.php',
                include __DIR__ . '/wikibase/view/resources.php'
diff --git a/view/resources/wikibase/entityIdFormatter/EntityIdHtmlFormatter.js 
b/view/resources/wikibase/entityIdFormatter/EntityIdHtmlFormatter.js
new file mode 100644
index 0000000..8cb24b2
--- /dev/null
+++ b/view/resources/wikibase/entityIdFormatter/EntityIdHtmlFormatter.js
@@ -0,0 +1,9 @@
+( function( wb ) {
+       'use strict';
+
+       var SELF = wb.entityIdFormatter.EntityIdHtmlFormatter = function() {
+       };
+
+       SELF.prototype.format = util.abstractMember;
+
+}( wikibase ) );
diff --git 
a/view/resources/wikibase/entityIdFormatter/EntityIdPlainFormatter.js 
b/view/resources/wikibase/entityIdFormatter/EntityIdPlainFormatter.js
new file mode 100644
index 0000000..c45f1be
--- /dev/null
+++ b/view/resources/wikibase/entityIdFormatter/EntityIdPlainFormatter.js
@@ -0,0 +1,9 @@
+( function( wb ) {
+       'use strict';
+
+       var SELF = wb.entityIdFormatter.EntityIdPlainFormatter = function() {
+       };
+
+       SELF.prototype.format = util.abstractMember;
+
+}( wikibase ) );
diff --git 
a/view/resources/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.js 
b/view/resources/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.js
new file mode 100644
index 0000000..ae675b8
--- /dev/null
+++ b/view/resources/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.js
@@ -0,0 +1,39 @@
+( function( $, wb, util ) {
+       'use strict';
+
+       /**
+        * @param {wikibase.store.EntityStore} entityStore
+        */
+       wb.entityIdFormatter.SimpleEntityIdHtmlFormatter = util.inherit(
+               'SimpleEntityIdHtmlFormatter',
+               wb.entityIdFormatter.EntityIdHtmlFormatter,
+               function( entityStore ) {
+                       if( !entityStore || !( entityStore instanceof 
wb.store.EntityStore ) ) {
+                               throw new Error( 'Required EntityStore instance 
not passed' );
+                       }
+                       this._entityStore = entityStore;
+               },
+               {
+                       _entityStore: null,
+
+                       format: function( entityId ) {
+                               var deferred = $.Deferred();
+                               this._entityStore.get( entityId ).done( 
function( response ) {
+                                       var res;
+                                       if( response ) {
+                                               res = 
wb.utilities.ui.buildLinkToEntityPage(
+                                                       response.getContent(),
+                                                       response.getTitle()
+                                               );
+                                       } else {
+                                               res = 
wb.utilities.ui.buildMissingEntityInfo( entityId, entityId[0] === 'P' ? 
'property' : 'item' );
+                                       }
+                                       deferred.resolve( $( 
document.createElement( 'span' ) ).html( res ).html() );
+                               } ).fail( function() {
+                                       // FIXME: check fail
+                               } );
+                               return deferred.promise();
+                       }
+               }
+       );
+}( jQuery, wikibase, util ) );
diff --git 
a/view/resources/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.js 
b/view/resources/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.js
new file mode 100644
index 0000000..86f3bb9
--- /dev/null
+++ b/view/resources/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.js
@@ -0,0 +1,37 @@
+( function( $, wb, util ) {
+       'use strict';
+
+       /**
+        * @param {wikibase.store.EntityStore} entityStore
+        */
+       wb.entityIdFormatter.SimpleEntityIdPlainFormatter = util.inherit(
+               'SimpleEntityIdPlainFormatter',
+               wb.entityIdFormatter.EntityIdPlainFormatter,
+               function( entityStore ) {
+                       if( !entityStore || !( entityStore instanceof 
wb.store.EntityStore ) ) {
+                               throw new Error( 'Required EntityStore instance 
not passed' );
+                       }
+                       this._entityStore = entityStore;
+               },
+               {
+                       _entityStore: null,
+
+                       format: function( entityId ) {
+                               var deferred = $.Deferred();
+                               this._entityStore.get( entityId ).done( 
function( response ) {
+                                       var res;
+                                       if( response ) {
+                                               res = 
wb.utilities.ui.buildPrettyEntityLabelText( response.getContent() );
+                                       } else {
+                                               res = entityId;
+                                       }
+                                       deferred.resolve( res );
+                               } ).fail( function() {
+                                       // FIXME: check fail
+                               } );
+                               return deferred.promise();
+                       }
+
+               }
+       );
+}( jQuery, wikibase, util ) );
diff --git a/view/resources/wikibase/entityIdFormatter/namespace.js 
b/view/resources/wikibase/entityIdFormatter/namespace.js
new file mode 100644
index 0000000..96ff9e6
--- /dev/null
+++ b/view/resources/wikibase/entityIdFormatter/namespace.js
@@ -0,0 +1,5 @@
+( function( wb ) {
+       'use strict';
+
+       wb.entityIdFormatter = wb.entityIdFormatter || {};
+}( wikibase ) );
diff --git a/view/resources/wikibase/entityIdFormatter/resources.php 
b/view/resources/wikibase/entityIdFormatter/resources.php
new file mode 100644
index 0000000..95e1b7e
--- /dev/null
+++ b/view/resources/wikibase/entityIdFormatter/resources.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @license GNU GPL v2+
+ * @author Adrian Heine < adrian.he...@wikimedia.de >
+ */
+return call_user_func( function() {
+       preg_match( '+' . preg_quote( DIRECTORY_SEPARATOR ) . 
'(?:vendor|extensions)'
+               . preg_quote( DIRECTORY_SEPARATOR ) . '.*+', __DIR__, 
$remoteExtPath );
+
+       $moduleTemplate = array(
+               'localBasePath' => __DIR__,
+               'remoteExtPath' => '..' . $remoteExtPath[0]
+       );
+
+       $modules = array(
+               'wikibase.entityIdFormatter.__namespace' => $moduleTemplate + 
array(
+                       'scripts' => array(
+                               'namespace.js'
+                       ),
+                       'dependencies' => array(
+                               'wikibase.view.__namespace',
+                       )
+               ),
+               'wikibase.entityIdFormatter.EntityIdHtmlFormatter' => 
$moduleTemplate + array(
+                       'scripts' => array(
+                               'EntityIdHtmlFormatter.js'
+                       ),
+                       'dependencies' => array(
+                               'util.inherit',
+                               'wikibase.entityIdFormatter.__namespace',
+                       )
+               ),
+               'wikibase.entityIdFormatter.EntityIdPlainFormatter' => 
$moduleTemplate + array(
+                       'scripts' => array(
+                               'EntityIdPlainFormatter.js'
+                       ),
+                       'dependencies' => array(
+                               'util.inherit',
+                               'wikibase.entityIdFormatter.__namespace',
+                       )
+               ),
+               'wikibase.entityIdFormatter.SimpleEntityIdHtmlFormatter' => 
$moduleTemplate + array(
+                       'scripts' => array(
+                               'SimpleEntityIdHtmlFormatter.js'
+                       ),
+                       'dependencies' => array(
+                               'util.inherit',
+                               'wikibase.store.EntityStore',
+                               'wikibase.utilities',
+                               'wikibase.entityIdFormatter.__namespace',
+                               
'wikibase.entityIdFormatter.EntityIdHtmlFormatter',
+                       )
+               ),
+               'wikibase.entityIdFormatter.SimpleEntityIdPlainFormatter' => 
$moduleTemplate + array(
+                       'scripts' => array(
+                               'SimpleEntityIdPlainFormatter.js'
+                       ),
+                       'dependencies' => array(
+                               'util.inherit',
+                               'wikibase.store.EntityStore',
+                               'wikibase.utilities',
+                               'wikibase.entityIdFormatter.__namespace',
+                               
'wikibase.entityIdFormatter.EntityIdPlainFormatter',
+                       )
+               ),
+       );
+
+       return $modules;
+} );
diff --git a/view/tests/qunit/jquery/wikibase/jquery.wikibase.itemview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.itemview.tests.js
index 45572cd..ea54e9a 100644
--- a/view/tests/qunit/jquery/wikibase/jquery.wikibase.itemview.tests.js
+++ b/view/tests/qunit/jquery/wikibase/jquery.wikibase.itemview.tests.js
@@ -12,7 +12,7 @@
  */
 var createItemview = function( options, $node ) {
        options = $.extend( {
-               entityStore: 'I am an EntityStore',
+               entityStore: new wb.store.EntityStore(),
                entityChangersFactory: {
                        getAliasesChanger: function() { return 'I am an 
AliasesChanger'; },
                        getDescriptionsChanger: function() { return 'I am a 
DescriptionsChanger'; },
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.propertyview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.propertyview.tests.js
index 2ec5edd..03c556e 100644
--- a/view/tests/qunit/jquery/wikibase/jquery.wikibase.propertyview.tests.js
+++ b/view/tests/qunit/jquery/wikibase/jquery.wikibase.propertyview.tests.js
@@ -12,7 +12,7 @@
  */
 var createPropertyview = function( options, $node ) {
        options = $.extend( {
-               entityStore: 'I am an EntityStore',
+               entityStore: new wb.store.EntityStore(),
                entityChangersFactory: {
                        getAliasesChanger: function() { return 'I am an 
AliasesChanger'; },
                        getDescriptionsChanger: function() { return 'I am a 
DescriptionsChanger'; },
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.referenceview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.referenceview.tests.js
index a43c47d..98e3922 100644
--- a/view/tests/qunit/jquery/wikibase/jquery.wikibase.referenceview.tests.js
+++ b/view/tests/qunit/jquery/wikibase/jquery.wikibase.referenceview.tests.js
@@ -1,26 +1,9 @@
 /**
  * @licence GNU GPL v2+
- * @author Adrian Lang < adrian.l...@wikimedia.de >
+ * @author Adrian Heine < adrian.he...@wikimedia.de >
  */
-( function( $, mw, wb, vv, vf, QUnit ) {
+( function( $, wb, vv, vf, QUnit ) {
        'use strict';
-
-       // We need an entity store for the instances of 
jquery.wikibase.referenceview
-       // and jquery.wikibase.snakview created by 
jquery.wikibase.referenceview.
-       var entityStore = {
-               get: function() {
-                       return $.Deferred().resolve( new 
wb.store.FetchedContent( {
-                               title: new mw.Title( 'Property:P1' ),
-                               content: new wb.datamodel.Property(
-                                       'P1',
-                                       'string',
-                                       new wb.datamodel.Fingerprint( new 
wb.datamodel.TermMap( [
-                                               new wb.datamodel.Term( 'en', 
'P1' )
-                                       ] ) )
-                               )
-                       } ) );
-               }
-       };
 
        var valueViewBuilder = new wb.ValueViewBuilder(
                new vv.ExpertStore(),
@@ -38,7 +21,17 @@
        function createReferenceview( options ) {
                options = $.extend( {
                        statementGuid: 'testGuid',
-                       entityStore: entityStore,
+                       entityIdHtmlFormatter: {
+                               format: function() {
+                                       return $.Deferred().resolve( 'P1' 
).promise();
+                               }
+                       },
+                       entityIdPlainFormatter: {
+                               format: function() {
+                                       return $.Deferred().resolve( 'P1' 
).promise();
+                               }
+                       },
+                       entityStore: 'I am an EntityStore',
                        valueViewBuilder: valueViewBuilder,
                        referencesChanger: 'I am a ReferencesChanger',
                        dataTypeStore: 'I am a DataTypeStore'
@@ -191,4 +184,4 @@
                );
        } );
 
-} )( jQuery, mediaWiki, wikibase, jQuery.valueview, valueFormatters, QUnit );
+} )( jQuery, wikibase, jQuery.valueview, valueFormatters, QUnit );
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.snaklistview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.snaklistview.tests.js
index 08a50ae..748d5b5 100644
--- a/view/tests/qunit/jquery/wikibase/jquery.wikibase.snaklistview.tests.js
+++ b/view/tests/qunit/jquery/wikibase/jquery.wikibase.snaklistview.tests.js
@@ -73,6 +73,16 @@
                        dataTypeStore: {
                                getDataType: function() {}
                        },
+                       entityIdHtmlFormatter: {
+                               format: function() {
+                                       return $.Deferred().resolve( 'P1' 
).promise();
+                               }
+                       },
+                       entityIdPlainFormatter: {
+                               format: function( entityId ) {
+                                       return $.Deferred().resolve( entityId 
).promise();
+                               }
+                       },
                        entityStore: entityStore,
                        valueViewBuilder: valueViewBuilder
                } );
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgrouplistview.tests.js
 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgrouplistview.tests.js
index 33d4999..cd0e87b 100644
--- 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgrouplistview.tests.js
+++ 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgrouplistview.tests.js
@@ -6,6 +6,11 @@
 ( function( $, wb, QUnit ) {
 'use strict';
 
+var entityStore = new wb.store.EntityStore();
+entityStore.get = function () {
+       return $.Deferred().resolve().promise();
+};
+
 /**
  * @param {Object} [options={}]
  * @param {jQuery} [$node]
@@ -14,11 +19,7 @@
 var createStatementgrouplistview = function( options, $node ) {
        options = $.extend( {
                claimGuidGenerator: 'I am a ClaimGuidGenerator',
-               entityStore: {
-                       get: function () {
-                               return $.Deferred().resolve().promise();
-                       }
-               },
+               entityStore: entityStore,
                valueViewBuilder: 'I am a ValueViewBuilder',
                entityChangersFactory: {
                        getClaimsChanger: function() {
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgroupview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgroupview.tests.js
index edf8227..d8bcbf7 100644
--- 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgroupview.tests.js
+++ 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementgroupview.tests.js
@@ -18,6 +18,16 @@
                                return $.Deferred().resolve().promise();
                        }
                },
+               entityIdHtmlFormatter: {
+                       format: function ( entityId ) {
+                               return $.Deferred().resolve( 'Link to entity' 
).promise();
+                       }
+               },
+               entityIdPlainFormatter: {
+                       format: function ( entityId ) {
+                               return $.Deferred().resolve( entityId 
).promise();
+                       }
+               },
                valueViewBuilder: 'I am a ValueViewBuilder',
                entityChangersFactory: {
                        getClaimsChanger: function() {
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementlistview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementlistview.tests.js
index be6726c..3d27a96 100644
--- 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementlistview.tests.js
+++ 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementlistview.tests.js
@@ -13,6 +13,11 @@
 var createStatementlistview = function( options, $node ) {
        options = $.extend( {
                claimGuidGenerator: 'I am a ClaimGuidGenerator',
+               entityIdPlainFormatter: {
+                       format: function ( entityId ) {
+                               return $.Deferred().resolve( entityId 
).promise();
+                       }
+               },
                entityStore: {
                        get: function () {
                                return $.Deferred().resolve().promise();
diff --git 
a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementview.tests.js 
b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementview.tests.js
index fdf1b96..31a36a7 100644
--- a/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementview.tests.js
+++ b/view/tests/qunit/jquery/wikibase/jquery.wikibase.statementview.tests.js
@@ -43,6 +43,16 @@
 var createStatementview = function( options, $node ) {
        options = $.extend( {
                entityStore: entityStore,
+               entityIdHtmlFormatter: {
+                       format: function( entityId ) {
+                               return $.Deferred().resolve( entityId 
).promise();
+                       }
+               },
+               entityIdPlainFormatter: {
+                       format: function( entityId ) {
+                               return $.Deferred().resolve( entityId 
).promise();
+                       }
+               },
                valueViewBuilder: 'I am a valueview builder',
                claimsChanger: 'I am a ClaimsChanger',
                referencesChanger: 'I am a ReferencesChanger',
diff --git a/view/tests/qunit/jquery/wikibase/resources.php 
b/view/tests/qunit/jquery/wikibase/resources.php
index c4a3b3a..6c3d534 100644
--- a/view/tests/qunit/jquery/wikibase/resources.php
+++ b/view/tests/qunit/jquery/wikibase/resources.php
@@ -61,6 +61,7 @@
                                'wikibase.datamodel.StatementGroup',
                                'wikibase.datamodel.StatementGroupSet',
                                'wikibase.datamodel.StatementList',
+                               'wikibase.store.EntityStore',
                        ),
                ),
 
@@ -159,6 +160,7 @@
                        'dependencies' => array(
                                'jquery.wikibase.itemview',
                                'wikibase.datamodel.Item',
+                               'wikibase.store.EntityStore',
                        ),
                ),
 
@@ -197,6 +199,7 @@
                        'dependencies' => array(
                                'jquery.wikibase.propertyview',
                                'wikibase.datamodel.Property',
+                               'wikibase.store.EntityStore',
                        ),
                ),
 
@@ -207,9 +210,7 @@
                        'dependencies' => array(
                                'jquery.valueview.ExpertStore',
                                'jquery.wikibase.referenceview',
-                               'mediawiki.Title',
                                'wikibase.datamodel',
-                               'wikibase.store.FetchedContent',
                                'wikibase.ValueViewBuilder',
                                'valueFormatters'
                        ),
diff --git a/view/tests/qunit/jquery/wikibase/snakview/resources.php 
b/view/tests/qunit/jquery/wikibase/snakview/resources.php
index fd7e6f5..a939151 100644
--- a/view/tests/qunit/jquery/wikibase/snakview/resources.php
+++ b/view/tests/qunit/jquery/wikibase/snakview/resources.php
@@ -24,17 +24,12 @@
                        'dependencies' => array(
                                'dataTypes.DataTypeStore',
                                'jquery.wikibase.snakview',
-                               'mediawiki.Title',
-                               'wikibase.datamodel.Fingerprint',
                                'wikibase.datamodel.Property',
                                'wikibase.datamodel.PropertyNoValueSnak',
                                'wikibase.datamodel.PropertySomeValueSnak',
                                'wikibase.datamodel.PropertyValueSnak',
-                               'wikibase.datamodel.Term',
-                               'wikibase.datamodel.TermMap',
                                'wikibase.serialization.SnakDeserializer',
                                'wikibase.serialization.SnakSerializer',
-                               'wikibase.store.FetchedContent',
                        ),
                ),
 
diff --git a/view/tests/qunit/jquery/wikibase/snakview/snakview.tests.js 
b/view/tests/qunit/jquery/wikibase/snakview/snakview.tests.js
index 02c9d9d..a745679 100644
--- a/view/tests/qunit/jquery/wikibase/snakview/snakview.tests.js
+++ b/view/tests/qunit/jquery/wikibase/snakview/snakview.tests.js
@@ -20,21 +20,6 @@
        }
 } ) );
 
-var entityStore = {
-       get: function() {
-               return $.Deferred().resolve( new wb.store.FetchedContent( {
-                       title: new mw.Title( 'Property:P1' ),
-                       content: new wb.datamodel.Property(
-                               'P1',
-                               'string',
-                               new wb.datamodel.Fingerprint( new 
wb.datamodel.TermMap( [
-                                       new wb.datamodel.Term( 'en', 'P1' )
-                               ] ) )
-                       )
-               } ) );
-       }
-};
-
 var snakSerializer = new wb.serialization.SnakSerializer(),
        snakDeserializer = new wb.serialization.SnakDeserializer();
 
@@ -46,7 +31,21 @@
 var createSnakview = function( options, $node ) {
        options = $.extend( {
                autoStartEditing: false,
-               entityStore: entityStore,
+               entityIdHtmlFormatter: {
+                       format: function() {
+                               return $.Deferred().resolve( 'Label' 
).promise();
+                       }
+               },
+               entityIdPlainFormatter: {
+                       format: function( entityId ) {
+                               return $.Deferred().resolve( entityId 
).promise();
+                       }
+               },
+               entityStore: {
+                       get: function( entityId ) {
+                               return $.Deferred().resolve().promise();
+                       }
+               },
                valueViewBuilder: 'I am a ValueViewBuilder',
                dataTypeStore: new dt.DataTypeStore()
        }, options || {} );
diff --git a/view/tests/qunit/resources.php b/view/tests/qunit/resources.php
index 269fa54..2bfd599 100644
--- a/view/tests/qunit/resources.php
+++ b/view/tests/qunit/resources.php
@@ -10,6 +10,7 @@
        include __DIR__ . '/jquery/wikibase/resources.php',
        include __DIR__ . '/wikibase/resources.php',
        include __DIR__ . '/wikibase/entityChangers/resources.php',
+       include __DIR__ . '/wikibase/entityIdFormatter/resources.php',
        include __DIR__ . '/wikibase/store/resources.php',
        include __DIR__ . '/wikibase/utilities/resources.php',
        include __DIR__ . '/wikibase/view/resources.php'
diff --git 
a/view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.tests.js
 
b/view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.tests.js
new file mode 100644
index 0000000..26729e7
--- /dev/null
+++ 
b/view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdHtmlFormatter.tests.js
@@ -0,0 +1,106 @@
+( function( $, sinon, QUnit, wb, mw ) {
+       'use strict';
+
+       var MODULE = wb.entityIdFormatter;
+
+       QUnit.module( 'wikibase.entityIdFormatter.SimpleEntityIdHtmlFormatter' 
);
+
+       function newFormatterGetter( entityStoreType ) {
+               var entityStore = new wb.store.EntityStore();
+               if( entityStoreType === 'empty' ) {
+                       entityStore.get = function() { return 
$.Deferred().resolve().promise(); };
+               } else if( entityStoreType === 'nolabels' ) {
+                       entityStore.get = function( entityId ) { return 
$.Deferred().resolve( new wb.store.FetchedContent( {
+                               title: $.extend( new mw.Title( 'title' ), {
+                                       namespace: 0,
+                                       title: entityId,
+                                       ext: null,
+                                       fragment: null
+                               } ),
+                               content: new wb.datamodel.Item( entityId )
+                       } ) ); };
+               } else if( entityStoreType === 'withlabels' ) {
+                       entityStore.get = function( entityId ) {
+                               var item = new wb.datamodel.Item( entityId );
+                               item.getFingerprint().setLabel( mw.config.get( 
'wgUserLanguage' ), new wb.datamodel.Term( mw.config.get( 'wgUserLanguage' ), 
'Label' ) );
+                               return $.Deferred().resolve( new 
wb.store.FetchedContent( {
+                                       title: $.extend( new mw.Title( 'title' 
), {
+                                               namespace: 0,
+                                               title: entityId,
+                                               ext: null,
+                                               fragment: null
+                                       } ),
+                                       content: item
+                               } ) );
+                       };
+               }
+               return function() {
+                       return new 
wb.entityIdFormatter.SimpleEntityIdHtmlFormatter( entityStore );
+               };
+       }
+
+       MODULE.testEntityIdHtmlFormatter.all( 
MODULE.SimpleEntityIdHtmlFormatter, newFormatterGetter( 'empty' ) );
+       MODULE.testEntityIdHtmlFormatter.all( 
MODULE.SimpleEntityIdHtmlFormatter, newFormatterGetter( 'nolabels' ) );
+       MODULE.testEntityIdHtmlFormatter.all( 
MODULE.SimpleEntityIdHtmlFormatter, newFormatterGetter( 'withlabels' ) );
+
+       QUnit.test( 'constructor throws error if no entity store is passed', 
function( assert ) {
+               assert.throws( function() {
+                       return new 
wb.entityIdFormatter.SimpleEntityIdHtmlFormatter();
+               } );
+       } );
+
+       QUnit.test( 'constructor throws error if entity store is not instance 
of EntityStore', function( assert ) {
+               assert.throws( function() {
+                       return new 
wb.entityIdFormatter.SimpleEntityIdHtmlFormatter( {} );
+               } );
+       } );
+
+       QUnit.test( 'format links to entity when it exists', function( assert ) 
{
+               var formatter = newFormatterGetter( 'nolabels' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       var $res = $( document.createElement( 'span' ) ).html( 
res ).children();
+                       assert.ok( $res.is( 'a' ) );
+                       assert.ok( $res.attr( 'title' ).match( /Q1/ ) );
+                       assert.ok( $res.attr( 'href' ).match( /Q1/ ) );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'format uses label when it exists', function( assert ) {
+               var formatter = newFormatterGetter( 'withlabels' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.equal( $( document.createElement( 'span' ) 
).html( res ).find( 'a' ).text(), 'Label' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'format adds missing label hint', function( assert ) {
+               var formatter = newFormatterGetter( 'nolabels' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.ok( res.match( /class="wb-entity-undefinedinfo"/ 
) );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'format falls back to the entity id when entity does not 
exist', function( assert ) {
+               var formatter = newFormatterGetter( 'empty' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.ok( $( document.createElement( 'span' ) ).html( 
res ).text().match( /^Q1/ ) );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'format adds hint if entity is missing', function( assert ) 
{
+               var formatter = newFormatterGetter( 'empty' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.ok( res.match( /class="wb-entity-undefinedinfo"/ 
) );
+                       done();
+               } );
+       } );
+
+}( jQuery, sinon, QUnit, wikibase, mediaWiki ) );
diff --git 
a/view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.tests.js
 
b/view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.tests.js
new file mode 100644
index 0000000..ebf93f3
--- /dev/null
+++ 
b/view/tests/qunit/wikibase/entityIdFormatter/SimpleEntityIdPlainFormatter.tests.js
@@ -0,0 +1,79 @@
+( function( $, sinon, QUnit, wb, mw ) {
+       'use strict';
+
+       QUnit.module( 'wikibase.entityIdFormatter.SimpleEntityIdPlainFormatter' 
);
+
+       function newFormatterGetter( entityStoreType ) {
+               var entityStore = new wb.store.EntityStore();
+               if( entityStoreType === 'empty' ) {
+                       entityStore.get = function() { return 
$.Deferred().resolve().promise(); };
+               } else if( entityStoreType === 'nolabels' ) {
+                       entityStore.get = function( entityId ) { return 
$.Deferred().resolve( new wb.store.FetchedContent( {
+                               title: $.extend( new mw.Title( 'title' ), {
+                                       namespace: 0,
+                                       title: entityId,
+                                       ext: null,
+                                       fragment: null
+                               } ),
+                               content: new wb.datamodel.Item( entityId )
+                       } ) ); };
+               } else if( entityStoreType === 'withlabels' ) {
+                       entityStore.get = function( entityId ) {
+                               var item = new wb.datamodel.Item( entityId );
+                               item.getFingerprint().setLabel( mw.config.get( 
'wgUserLanguage' ), new wb.datamodel.Term( mw.config.get( 'wgUserLanguage' ), 
'Label' ) );
+                               return $.Deferred().resolve( new 
wb.store.FetchedContent( {
+                                       title: $.extend( new mw.Title( 'title' 
), {
+                                               namespace: 0,
+                                               title: entityId,
+                                               ext: null,
+                                               fragment: null
+                                       } ),
+                                       content: item
+                               } ) );
+                       };
+               }
+               return function() {
+                       return new 
wb.entityIdFormatter.SimpleEntityIdPlainFormatter( entityStore );
+               };
+       }
+
+       QUnit.test( 'constructor throws error if no entity store is passed', 
function( assert ) {
+               assert.throws( function() {
+                       return new 
wb.entityIdFormatter.SimpleEntityIdPlainFormatter();
+               } );
+       } );
+
+       QUnit.test( 'constructor throws error if entity store is not instance 
of EntityStore', function( assert ) {
+               assert.throws( function() {
+                       return new 
wb.entityIdFormatter.SimpleEntityIdPlainFormatter( {} );
+               } );
+       } );
+
+       QUnit.test( 'format uses label when it exists', function( assert ) {
+               var formatter = newFormatterGetter( 'withlabels' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.equal( res, 'Label' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'format falls back to the entity id when no label exists', 
function( assert ) {
+               var formatter = newFormatterGetter( 'nolabels' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.equal( res, 'Q1' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'format falls back to the entity id when entity does not 
exist', function( assert ) {
+               var formatter = newFormatterGetter( 'empty' )();
+               var done = assert.async();
+               formatter.format( 'Q1' ).done( function( res ) {
+                       assert.equal( res, 'Q1' );
+                       done();
+               } );
+       } );
+
+}( jQuery, sinon, QUnit, wikibase, mediaWiki ) );
diff --git a/view/tests/qunit/wikibase/entityIdFormatter/resources.php 
b/view/tests/qunit/wikibase/entityIdFormatter/resources.php
new file mode 100644
index 0000000..6065a08
--- /dev/null
+++ b/view/tests/qunit/wikibase/entityIdFormatter/resources.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @license GNU GPL v2+
+ * @author Adrian Heine < adrian.he...@wikimedia.de >
+ */
+return call_user_func( function() {
+       preg_match( '+' . preg_quote( DIRECTORY_SEPARATOR ) . 
'(?:vendor|extensions)'
+               . preg_quote( DIRECTORY_SEPARATOR ) . '.*+', __DIR__, 
$remoteExtPath );
+
+       $moduleTemplate = array(
+               'localBasePath' => __DIR__,
+               'remoteExtPath' => '..' . $remoteExtPath[0]
+       );
+
+       $modules = array(
+
+               'wikibase.entityIdFormatter.testEntityIdHtmlFormatter' => 
$moduleTemplate + array(
+                       'scripts' => array(
+                               'testEntityIdHtmlFormatter.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.entityIdFormatter.__namespace',
+                               
'wikibase.entityIdFormatter.EntityIdHtmlFormatter',
+                       ),
+               ),
+
+               'wikibase.entityIdFormatter.SimpleEntityIdHtmlFormatter.tests' 
=> $moduleTemplate + array(
+                       'scripts' => array(
+                               'SimpleEntityIdHtmlFormatter.tests.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.datamodel.Item',
+                               'wikibase.datamodel.Term',
+                               'wikibase.store.FetchedContent',
+                               
'wikibase.entityIdFormatter.SimpleEntityIdHtmlFormatter',
+                               
'wikibase.entityIdFormatter.testEntityIdHtmlFormatter',
+                       ),
+               ),
+
+               'wikibase.entityIdFormatter.SimpleEntityIdPlainFormatter.tests' 
=> $moduleTemplate + array(
+                       'scripts' => array(
+                               'SimpleEntityIdPlainFormatter.tests.js',
+                       ),
+                       'dependencies' => array(
+                               'wikibase.datamodel.Item',
+                               'wikibase.datamodel.Term',
+                               'wikibase.store.FetchedContent',
+                               
'wikibase.entityIdFormatter.SimpleEntityIdPlainFormatter',
+                       ),
+               ),
+
+       );
+
+       return $modules;
+
+} );
diff --git 
a/view/tests/qunit/wikibase/entityIdFormatter/testEntityIdHtmlFormatter.js 
b/view/tests/qunit/wikibase/entityIdFormatter/testEntityIdHtmlFormatter.js
new file mode 100644
index 0000000..685a563
--- /dev/null
+++ b/view/tests/qunit/wikibase/entityIdFormatter/testEntityIdHtmlFormatter.js
@@ -0,0 +1,57 @@
+( function( $, QUnit, wb ) {
+       'use strict';
+
+       wb.entityIdFormatter.testEntityIdHtmlFormatter = {
+               all: function( constructor, getInstance ) {
+                       this.constructorTests( constructor, getInstance );
+                       this.formatTests( getInstance );
+               },
+
+               constructorTests: function( constructor, getInstance ) {
+                       QUnit.test( 'Constructor', function( assert ) {
+                               var instance = getInstance();
+
+                               assert.ok(
+                                       instance instanceof constructor,
+                                       'Instantiated.'
+                               );
+
+                               assert.ok(
+                                       instance instanceof 
wb.entityIdFormatter.EntityIdHtmlFormatter,
+                                       'Instance of EntityIdHtmlFormatter'
+                               );
+                       } );
+               },
+
+               formatTests: function( getInstance ) {
+                       QUnit.test( 'format returns some non-empty string', 
function( assert ) {
+                               var instance = getInstance();
+                               var done = assert.async();
+
+                               instance.format( 'Q1' ).done( function( res ) {
+                                       assert.equal( typeof res, 'string' );
+                                       assert.notEqual( res, '' );
+                                       done();
+                               } );
+                       } );
+                       QUnit.test( 'format correctly escapes ampersands in the 
entity id', function( assert ) {
+                               var instance = getInstance();
+                               var done = assert.async();
+
+                               instance.format( '&' ).done( function( res ) {
+                                       assert.equal( res.match( /&($|[^a])/ ), 
null );
+                                       done();
+                               } );
+                       } );
+                       QUnit.test( 'format correctly escapes HTML in the 
entity id', function( assert ) {
+                               var instance = getInstance();
+                               var done = assert.async();
+
+                               instance.format( '<script>' ).done( function( 
res ) {
+                                       assert.equal( $( 
document.createElement( 'span' ) ).html( res ).find( 'script' ).length, 0 );
+                                       done();
+                               } );
+                       } );
+               }
+       };
+}( jQuery, QUnit, wikibase ) );

-- 
To view, visit https://gerrit.wikimedia.org/r/199295
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I4a2fdfbc3faa8a640683006111539812ab764faf
Gerrit-PatchSet: 14
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: Adrian Lang <adrian.he...@wikimedia.de>
Gerrit-Reviewer: Adrian Lang <adrian.he...@wikimedia.de>
Gerrit-Reviewer: Aude <aude.w...@gmail.com>
Gerrit-Reviewer: Bene <benestar.wikime...@gmail.com>
Gerrit-Reviewer: Hoo man <h...@online.de>
Gerrit-Reviewer: Jonas Kress (WMDE) <jonas.kr...@wikimedia.de>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to