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

Change subject: Allow lazy loading references via mobileview API
......................................................................


Allow lazy loading references via mobileview API

While we discuss the internals of a generic references API we
should make use of the mobileview API we already have to source
references.

Changes:
* Introduce 2 ReferencesGateway classes extending the interface class;
* Make it safe to load `mobile.references` without side effects - move
  the initialisation code into skins.minerva.scripts;
* Separate tests for all 2 gateways;
* Clicking a nested reference no longer causes scrolling to the reference

Bug: T130551
Bug: T129182
Change-Id: Iec812faf12182f80f5a2193c2bc4b9b95ea86bbe
---
M extension.json
M includes/MobileFrontend.hooks.php
A resources/mobile.references.gateway/ReferencesGateway.js
A resources/mobile.references.gateway/ReferencesHtmlScraperGateway.js
A resources/mobile.references.gateway/ReferencesMobileViewGateway.js
M resources/mobile.references/ReferencesDrawer.js
D resources/mobile.references/references.js
M resources/skins.minerva.scripts/init.js
M resources/skins.minerva.scripts/preInit.js
A resources/skins.minerva.scripts/references.js
A tests/qunit/mobile.references.gateway/test_ReferencesHtmlScraperGateway.js
A tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
D tests/qunit/mobile.references/test_references.js
A tests/qunit/tests.mobilefrontend/pageWithStrippedRefs.html
A tests/qunit/tests.mobilefrontend/refSection.html
15 files changed, 458 insertions(+), 244 deletions(-)

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



diff --git a/extension.json b/extension.json
index 0e22c35..d1d5946 100644
--- a/extension.json
+++ b/extension.json
@@ -1077,6 +1077,7 @@
                                "desktop"
                        ],
                        "dependencies": [
+                               "mobile.references.gateway",
                                "mobile.drawers",
                                "mobile.editor.api",
                                "mobile.references.images"
@@ -1092,8 +1093,22 @@
                                "Drawer.hogan": 
"resources/mobile.references/ReferencesDrawer.hogan"
                        },
                        "scripts": [
-                               
"resources/mobile.references/ReferencesDrawer.js",
-                               "resources/mobile.references/references.js"
+                               
"resources/mobile.references/ReferencesDrawer.js"
+                       ]
+               },
+               "mobile.references.gateway": {
+                       "targets": [
+                               "mobile",
+                               "desktop"
+                       ],
+                       "dependencies": [
+                               "mobile.startup",
+                               "mobile.oo"
+                       ],
+                       "scripts": [
+                               
"resources/mobile.references.gateway/ReferencesGateway.js",
+                               
"resources/mobile.references.gateway/ReferencesHtmlScraperGateway.js",
+                               
"resources/mobile.references.gateway/ReferencesMobileViewGateway.js"
                        ]
                },
                "mobile.toggle": {
@@ -1593,6 +1608,7 @@
                                "resources/skins.minerva.scripts/init.js",
                                
"resources/skins.minerva.scripts/initLogging.js",
                                
"resources/skins.minerva.scripts/mobileRedirect.js",
+                               "resources/skins.minerva.scripts/references.js",
                                "resources/skins.minerva.scripts/search.js"
                        ]
                },
diff --git a/includes/MobileFrontend.hooks.php 
b/includes/MobileFrontend.hooks.php
index 559019c..8af7310 100644
--- a/includes/MobileFrontend.hooks.php
+++ b/includes/MobileFrontend.hooks.php
@@ -326,7 +326,9 @@
                                'issues.hogan' => 
'tests/qunit/tests.mobilefrontend/issues.hogan',
                                'page.html' => 
'tests/qunit/tests.mobilefrontend/page.html',
                                'page2.html' => 
'tests/qunit/tests.mobilefrontend/page2.html',
+                               'pageWithStrippedRefs.html' => 
'tests/qunit/tests.mobilefrontend/pageWithStrippedRefs.html',
                                'references.html' => 
'tests/qunit/tests.mobilefrontend/references.html',
+                               'refSection.html' => 
'tests/qunit/tests.mobilefrontend/refSection.html',
                        ),
                        'localBasePath' => $localBasePath,
                        'remoteExtPath' => 'MobileFrontend',
diff --git a/resources/mobile.references.gateway/ReferencesGateway.js 
b/resources/mobile.references.gateway/ReferencesGateway.js
new file mode 100644
index 0000000..a2e2fcf
--- /dev/null
+++ b/resources/mobile.references.gateway/ReferencesGateway.js
@@ -0,0 +1,46 @@
+( function ( M ) {
+       /**
+        * Abstract base class
+        * Gateway for retrieving references
+        *
+        * @class ReferencesGateway
+        * @param {mw.Api} api
+        */
+       function ReferencesGateway( api ) {
+               this.api = api;
+       }
+
+       OO.mfExtend( ReferencesGateway, {
+               /**
+                * Escapes reference id to remove CSS selector meta characters
+                *
+                * @param {String} id of a DOM element in the page
+                * @returns {String}
+                */
+               getEscapedId: function ( id ) {
+                       var
+                               // Escape (almost) all CSS selector meta 
characters
+                               // see 
http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+                               meta = /[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g;
+
+                       id = id.replace( meta, '\\$&' );
+                       return id.substr( 1, id.length );
+               },
+               // jscs:disable
+               /**
+                * Return the matched reference via API or DOM query
+                *
+                * @method
+                * @param {String} id CSS selector
+                * @param {Page} page to find reference for
+                * @returns {jQuery.Promise} resolves with an Object 
representing reference with a `text` property
+                         or false if the reference does not exist
+                */
+               getReference: function () {
+                       throw new Error( 'Method unimplemented' );
+               }
+               // jscs:enable
+       } );
+
+       M.define( 'mobile.references.gateway/ReferencesGateway', 
ReferencesGateway );
+}( mw.mobileFrontend ) );
diff --git 
a/resources/mobile.references.gateway/ReferencesHtmlScraperGateway.js 
b/resources/mobile.references.gateway/ReferencesHtmlScraperGateway.js
new file mode 100644
index 0000000..bff3368
--- /dev/null
+++ b/resources/mobile.references.gateway/ReferencesHtmlScraperGateway.js
@@ -0,0 +1,35 @@
+( function ( M, $ ) {
+       var ReferencesGateway = M.require( 
'mobile.references.gateway/ReferencesGateway' );
+
+       /**
+        * Gateway for retrieving references via the content of the Page
+        *
+        * @class ReferencesHtmlScraperGateway
+        * @extends ReferencesGateway
+        * @inheritdoc
+        */
+       function ReferencesHtmlScraperGateway() {
+               ReferencesGateway.apply( this, arguments );
+       }
+
+       OO.mfExtend( ReferencesHtmlScraperGateway, ReferencesGateway, {
+               /**
+                * @inheritdoc
+                */
+               getReference: function ( id, page ) {
+                       var ref,
+                               $refs = page.$( 'ol.references' ),
+                               $el = $refs.find( '#' + this.getEscapedId( id ) 
);
+
+                       ref = $el.length ?
+                               {
+                                       text: $el.html()
+                               } : false;
+
+                       return $.Deferred().resolve( ref ).promise();
+               }
+       } );
+
+       M.define( 'mobile.references.gateway/ReferencesHtmlScraperGateway',
+               ReferencesHtmlScraperGateway );
+}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/mobile.references.gateway/ReferencesMobileViewGateway.js 
b/resources/mobile.references.gateway/ReferencesMobileViewGateway.js
new file mode 100644
index 0000000..a8bd459
--- /dev/null
+++ b/resources/mobile.references.gateway/ReferencesMobileViewGateway.js
@@ -0,0 +1,84 @@
+( function ( M, $ ) {
+       var ReferencesHtmlScraperGateway = M.require(
+                       
'mobile.references.gateway/ReferencesHtmlScraperGateway' ),
+               Page = M.require( 'mobile.startup/Page' );
+
+       /**
+        * Gateway for retrieving references via the MobileView API
+        *
+        * @class ReferencesMobileViewGateway
+        * @extends ReferencesHtmlScraperGateway
+        * @inheritdoc
+        */
+       function ReferencesMobileViewGateway() {
+               ReferencesHtmlScraperGateway.apply( this, arguments );
+       }
+
+       OO.mfExtend( ReferencesMobileViewGateway, ReferencesHtmlScraperGateway, 
{
+               /**
+                * Retrieve references for a given page
+                *
+                * @method
+                * @param {Page} page
+                * @return {jQuery.Promise} passed an instance of the 
jQuery.object
+                *  representing all the sections in the page
+                */
+               getReferencesElements: function ( page ) {
+                       var self = this;
+
+                       if ( this.$references ) {
+                               return $.Deferred().resolve( self.$references 
).promise();
+                       } else if ( this.pendingMobileViewApi ) {
+                               // avoid ever making more than one api request
+                               return this.pendingMobileViewApi;
+                       }
+
+                       this.pendingMobileViewApi = this.api.get( {
+                               action: 'mobileview',
+                               page: page.getTitle(),
+                               sections: 'references',
+                               prop: 'text',
+                               revision: page.getRevisionId()
+                       } ).then( function ( data ) {
+                               var sections = data.mobileview.sections,
+                                       refs = [];
+
+                               if ( sections ) {
+                                       // There could be multiple <references> 
tags in the page.
+                                       $.each( sections, function ( i, section 
) {
+                                               // skip the section header, 
just get the references
+                                               refs.push( $( '<div>' ).html( 
section.text ).find( '.references' ).eq( 0 ) );
+                                       } );
+                               }
+
+                               // cache
+                               self.$references = $( refs );
+
+                               return self.$references;
+                       } );
+                       return this.pendingMobileViewApi;
+               },
+               /**
+                * @inheritdoc
+                */
+               getReference: function ( id, page ) {
+                       var self = this,
+                               parentGetReference = 
ReferencesHtmlScraperGateway.prototype.getReference;
+
+                       return this.getReferencesElements( page ).then( 
function ( $refSections ) {
+                               // append to a new page to avoid side effects 
on the passed Page object.
+                               var refPage = new Page( page.options );
+
+                               $refSections.each( function () {
+                                       // With each replace the matched 
element is removed from the list.
+                                       // That's why we always replace the 
first matched element.
+                                       refPage.$( 
'.mf-lazy-references-placeholder' ).eq( 0 ).replaceWith( this );
+                               } );
+                               return parentGetReference.call( self, id, 
refPage );
+                       } );
+               }
+       } );
+
+       M.define( 'mobile.references.gateway/ReferencesMobileViewGateway',
+               ReferencesMobileViewGateway );
+}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/mobile.references/ReferencesDrawer.js 
b/resources/mobile.references/ReferencesDrawer.js
index 9d8f362..aa28872 100644
--- a/resources/mobile.references/ReferencesDrawer.js
+++ b/resources/mobile.references/ReferencesDrawer.js
@@ -76,20 +76,38 @@
                },
                /**
                 * Fetch and render nested reference upon click
+                * @param {String} id of the reference to be retrieved
+                * @param {Page} page to locate reference for
+                * @param {String} refNumber the number it identifies as in the 
page
+                */
+               showReference: function ( id, page, refNumber ) {
+                       var drawer = this;
+
+                       // Save the page in case we have to show a nested 
reference.
+                       this.options.page = page;
+                       this.options.gateway.getReference( id, page ).done( 
function ( reference ) {
+                               drawer.render( {
+                                       title: refNumber,
+                                       text: reference.text
+                               } );
+                       } ).fail( function () {
+                               drawer.render( {
+                                       error: true,
+                                       title: refNumber,
+                                       text: mw.msg( 
'mobile-frontend-references-citation-error' )
+                               } );
+                       } );
+               },
+               /**
+                * Fetch and render nested reference upon click
                 * @param {jQuery.Event} ev
                 */
                showNestedReference: function ( ev ) {
-                       var $dest = $( ev.target ),
-                               href = $dest.attr( 'href' );
+                       var $dest = $( ev.target );
 
-                       mw.track( 'mf.showReference', {
-                               href: href,
-                               title: $dest.text(),
-                               page: this.options.page
-                       } );
-
-                       // Don't hide the already shown drawer
-                       ev.stopPropagation();
+                       this.showReference( $dest.attr( 'href' ), 
this.options.page, $dest.text() );
+                       // Don't hide the already shown drawer via propagation 
and stop default scroll behaviour.
+                       return false;
                }
        } );
 
diff --git a/resources/mobile.references/references.js 
b/resources/mobile.references/references.js
deleted file mode 100644
index 718e014..0000000
--- a/resources/mobile.references/references.js
+++ /dev/null
@@ -1,172 +0,0 @@
-( function ( M, $ ) {
-       var drawer, referencesData,
-               context = M.require( 'mobile.context/context' ),
-               isBeta = context.isBetaGroupMember(),
-               ReferencesDrawer = M.require( 
'mobile.references/ReferencesDrawer' );
-
-       mw.trackSubscribe( 'mf.showReference', function ( topic, data ) {
-               getReference( data.href, data.page ).done( function ( reference 
) {
-                       drawer.render( {
-                               title: data.title,
-                               text: reference.text
-                       } );
-               } ).fail( function () {
-                       drawer.render( {
-                               error: true,
-                               title: data.title,
-                               text: mw.msg( 
'mobile-frontend-references-citation-error' )
-                       } );
-               } );
-       } );
-
-       /**
-        * Return a data structure indexing all references in the given page.
-        * @method
-        * @ignore
-        * @param {Page} page to retrieve references for
-        * @returns {jQuery.Deferred} resolving with an Object indexing all 
references
-        */
-       function getReferenceData( page ) {
-               var api,
-                       d = $.Deferred();
-
-               if ( referencesData ) {
-                       d.resolve( referencesData );
-               } else {
-                       api = new mw.Api();
-                       api.get( {
-                               action: 'query',
-                               prop: 'references',
-                               formatversion: 2,
-                               titles: [ page.getTitle() ]
-                       } ).then( function ( data ) {
-                               if ( data && data.query && data.query.pages && 
data.query.pages.length ) {
-                                       referencesData = 
data.query.pages[0].references;
-                               } else {
-                                       referencesData = {};
-                               }
-                               d.resolve( referencesData );
-                       } ).fail( $.proxy( d, 'reject' ) );
-               }
-               return d;
-       }
-
-       /**
-        * Return the matched reference among the children of ol.references
-        * @method
-        * @ignore
-        * @param {String} id CSS selector
-        * @param {Page} page to retrieve reference for
-        * @returns {jQuery.Deferred} resolves with an Object representing 
reference
-        */
-       function getReference( id, page ) {
-               var $el,
-                       config = mw.config.get( 'wgMFLazyLoadReferences' ),
-                       EditorGateway = M.require( 
'mobile.editor.api/EditorGateway' ),
-                       editorGateway = new EditorGateway( {
-                               api: new mw.Api(),
-                               title: page.getTitle()
-                       } ),
-                       d = $.Deferred(),
-                       // Escape (almost) all CSS selector meta characters
-                       // see 
http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
-                       meta = /[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g;
-
-               id = id.replace( meta, '\\$&' );
-               if ( config.base || ( isBeta && config.beta ) ) {
-                       id = id.substr( 1, id.length );
-                       // FIXME: Use a gateway for this (?)
-                       getReferenceData( page ).then( function ( references ) {
-                               var ref = references[id];
-                               if ( !ref ) {
-                                       d.reject();
-                               } else if ( ref.html ) {
-                                       // use cached version to avoid lookup
-                                       d.resolve( {
-                                               text: ref.html
-                                       } );
-                               } else {
-                                       // reference was provided raw so we now 
need to parse it.
-                                       editorGateway.getPreview( {
-                                               text: ref.text
-                                       } ).done( function ( parsedText ) {
-                                               // cache for later use
-                                               ref.html = parsedText;
-                                               d.resolve( {
-                                                       text: parsedText
-                                               } );
-                                       } ).fail( $.proxy( d, 'reject' ) );
-                               }
-                       } ).fail( $.proxy( d, 'reject' ) );
-               } else {
-                       // Use find rather than string concatenation
-                       $el = page.$( 'ol.references' ).find( id );
-                       if ( $el.length ) {
-                               d.resolve( {
-                                       text: $el.html()
-                               } );
-                       } else {
-                               d.reject();
-                       }
-               }
-               return d;
-       }
-
-       /**
-        * Event handler to show reference when a reference link is clicked
-        * @method
-        * @ignore
-        * @param {jQuery.Event} ev Event object
-        */
-       function showReference( ev ) {
-               var $dest = $( this ),
-                       href = $dest.attr( 'href' ),
-                       page = $dest.data( 'page' );
-
-               if ( !drawer ) {
-                       // Note we only initialise here to avoid adding to DOM 
unnecessarily
-                       // (Drawer currently auto appends within the postRender 
function )
-                       drawer = new ReferencesDrawer( {
-                               page: page
-                       } );
-               }
-
-               mw.track( 'mf.showReference', {
-                       href: href,
-                       page: page,
-                       title: $dest.text()
-               } );
-
-               ev.preventDefault();
-               // don't hide drawer (stop propagation of click) if it is 
already shown (e.g. click another reference)
-               if ( drawer.isVisible() ) {
-                       ev.stopPropagation();
-               } else {
-                       // flush any existing reference information
-                       drawer.render( {
-                               text: undefined
-                       } );
-                       // use setTimeout so that browser calculates dimensions 
before show()
-                       setTimeout( $.proxy( drawer, 'show' ), 0 );
-               }
-       }
-
-       /**
-        * Make references clickable and show a drawer when clicked on.
-        * @method
-        * @ignore
-        * @param {Page} page Defaults to $( '#bodyContent' )
-        */
-       function setup( page ) {
-               page.$( 'sup a' ).off( 'click' )
-                       .data( 'page', page )
-                       .on( 'click', showReference );
-               page.$( '.mw-cite-backlink a' ).off( 'click' );
-       }
-
-       M.define( 'mobile.references/references', {
-               getReference: getReference,
-               setup: setup
-       } );
-
-}( mw.mobileFrontend, jQuery ) );
diff --git a/resources/skins.minerva.scripts/init.js 
b/resources/skins.minerva.scripts/init.js
index 1543bde..54520a4 100644
--- a/resources/skins.minerva.scripts/init.js
+++ b/resources/skins.minerva.scripts/init.js
@@ -7,8 +7,6 @@
                loader = M.require( 'mobile.overlays/moduleLoader' ),
                router = M.require( 'mobile.startup/router' ),
                context = M.require( 'mobile.context/context' ),
-               // FIXME: Don't pull in the mobile.references library on 
startup. Lazy load it when needed
-               references = M.require( 'mobile.references/references' ),
                cleanuptemplates = M.require( 'mobile.issues/cleanuptemplates' 
),
                useNewMediaViewer = context.isBetaGroupMember(),
                overlayManager = M.require( 'mobile.startup/overlayManager' ),
@@ -207,7 +205,6 @@
        $( function () {
                initButton();
                initMediaViewer();
-               references.setup( page );
        } );
 
        // Access the beta optin experiment if available.
diff --git a/resources/skins.minerva.scripts/preInit.js 
b/resources/skins.minerva.scripts/preInit.js
index e53bc63..1190168 100644
--- a/resources/skins.minerva.scripts/preInit.js
+++ b/resources/skins.minerva.scripts/preInit.js
@@ -58,6 +58,7 @@
                        protection: {
                                edit: permissions
                        },
+                       revId: mw.config.get( 'wgRevisionId' ),
                        isMainPage: mw.config.get( 'wgIsMainPage' ),
                        isWatched: $( '#ca-watch' ).hasClass( 'watched' ),
                        sections: gateway.getSectionsFromHTML( $content ),
diff --git a/resources/skins.minerva.scripts/references.js 
b/resources/skins.minerva.scripts/references.js
new file mode 100644
index 0000000..4e58287
--- /dev/null
+++ b/resources/skins.minerva.scripts/references.js
@@ -0,0 +1,100 @@
+( function ( M, $ ) {
+       var drawer,
+               page = M.getCurrentPage(),
+               context = M.require( 'mobile.context/context' ),
+               isBeta = context.isBetaGroupMember();
+
+       /**
+        * Retrieves the references gateway module info to be used on the page 
from config
+        *
+        * @method
+        * @ignore
+        * @returns {String} name of the class implementing ReferenceGateway to 
use
+        */
+       function getReferenceGatewayClassName() {
+               var config = mw.config.get( 'wgMFLazyLoadReferences', {} );
+
+               return config.base || ( isBeta && config.beta ) ?
+                       'ReferencesMobileViewGateway' : 
'ReferencesHtmlScraperGateway';
+       }
+
+       /**
+        * Creates a ReferenceDrawer based on the currently available 
ReferenceGateway
+        *
+        * @ignore
+        * @returns {ReferencesDrawer}
+        */
+       function referenceDrawerFactory() {
+               var gatewayClassName = getReferenceGatewayClassName(),
+                       ReferencesGateway = M.require( 
'mobile.references.gateway/' + gatewayClassName ),
+                       ReferencesDrawer = M.require( 
'mobile.references/ReferencesDrawer' );
+
+               return new ReferencesDrawer( {
+                       gateway: new ReferencesGateway( new mw.Api() )
+               } );
+       }
+
+       /**
+        * Event handler to show reference when a reference link is clicked
+        * @ignore
+        * @param {jQuery.Event} ev Click event of the reference element
+        * @param {ReferencesDrawer} drawer to show the reference in
+        */
+       function showReference( ev, drawer ) {
+               var $dest = $( ev.target ),
+                       href = $dest.attr( 'href' );
+
+               ev.preventDefault();
+
+               drawer.showReference( href, page, $dest.text() );
+
+               // don't hide drawer (stop propagation of click) if it is 
already shown (e.g. click another reference)
+               if ( drawer.isVisible() ) {
+                       ev.stopPropagation();
+               } else {
+                       // flush any existing reference information
+                       drawer.render( {
+                               text: undefined
+                       } );
+                       // use setTimeout so that browser calculates dimensions 
before show()
+                       setTimeout( $.proxy( drawer, 'show' ), 0 );
+               }
+       }
+
+       /**
+        * Event handler to show reference when a reference link is clicked.
+        * Delegates to `showReference` once the references drawer is ready.
+        *
+        * @ignore
+        * @param {jQuery.Event} ev Click event of the reference element
+        */
+       function onClickReference( ev ) {
+               if ( !drawer ) {
+                       drawer = referenceDrawerFactory();
+               }
+               showReference( ev, drawer );
+       }
+
+       /**
+        * Make references clickable and show a drawer when clicked on.
+        * @ignore
+        * @param {Page} page
+        */
+       function setup( page ) {
+               var $refs = page.$( 'sup a' );
+
+               if ( $refs.length ) {
+                       $refs
+                               .off( 'click' )
+                               .on( 'click', onClickReference );
+                       page.$( '.mw-cite-backlink a' )
+                               .off( 'click' );
+               }
+       }
+
+       // Setup
+       $( function () {
+               setup( page );
+       } );
+
+}( mw.mobileFrontend, jQuery ) );
diff --git 
a/tests/qunit/mobile.references.gateway/test_ReferencesHtmlScraperGateway.js 
b/tests/qunit/mobile.references.gateway/test_ReferencesHtmlScraperGateway.js
new file mode 100644
index 0000000..b4b464a
--- /dev/null
+++ b/tests/qunit/mobile.references.gateway/test_ReferencesHtmlScraperGateway.js
@@ -0,0 +1,37 @@
+( function ( $, M ) {
+
+       var ReferencesHtmlScraperGateway = M.require(
+                       
'mobile.references.gateway/ReferencesHtmlScraperGateway' ),
+               Page = M.require( 'mobile.startup/Page' );
+
+       QUnit.module( 'MobileFrontend: htmlScraper references gateway', {
+               setup: function () {
+                       this.$container = mw.template.get( 
'tests.mobilefrontend', 'references.html' )
+                               .render().appendTo( '#qunit-fixture' );
+                       this.page = new Page( {
+                               el: this.$container,
+                               title: 'Reftest'
+                       } );
+                       this.referencesGateway = new 
ReferencesHtmlScraperGateway( new mw.Api() );
+                       // we use Page object which calls getUrl which uses 
config variables.
+                       this.sandbox.stub( mw.util, 'getUrl' ).returns( 
'/wiki/Reftest' );
+               }
+       } );
+
+       QUnit.test( 'checking good reference', 1, function ( assert ) {
+               var done = assert.async( 1 );
+               this.referencesGateway.getReference( '#cite_note-1', this.page 
).done( function ( ref ) {
+                       assert.strictEqual( $( '<div>' ).html( ref.text ).find( 
'.reference-text' ).text(), 'hello' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'checking bad reference', 1, function ( assert ) {
+               var done = assert.async( 1 );
+               this.referencesGateway.getReference( '#cite_note-bad', 
this.page ).done( function ( ref ) {
+                       assert.ok( ref === false, 'When bad id given false 
returned.' );
+                       done();
+               } );
+       } );
+
+} )( jQuery, mw.mobileFrontend );
diff --git 
a/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js 
b/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
new file mode 100644
index 0000000..9a37d6d
--- /dev/null
+++ b/tests/qunit/mobile.references.gateway/test_ReferencesMobileViewGateway.js
@@ -0,0 +1,95 @@
+( function ( $, M ) {
+
+       var ReferencesMobileViewGateway = M.require(
+                       'mobile.references.gateway/ReferencesMobileViewGateway' 
),
+               Page = M.require( 'mobile.startup/Page' );
+
+       QUnit.module( 'MobileFrontend: mobileView references gateway', {
+               setup: function () {
+                       this.$container = mw.template.get( 
'tests.mobilefrontend', 'pageWithStrippedRefs.html' )
+                               .render().appendTo( '#qunit-fixture' );
+
+                       this.page = new Page( {
+                               el: this.$container,
+                               title: 'Reftest'
+                       } );
+
+                       this.api = new mw.Api();
+                       this.sandbox.stub( this.api, 'get' ).returns(
+                               $.Deferred().resolve( {
+                                       mobileview: {
+                                               sections: []
+                                       }
+                               } )
+                       );
+                       this.gatewayHitsApi = new ReferencesMobileViewGateway( 
this.api );
+
+                       this.referencesGateway = new 
ReferencesMobileViewGateway( new mw.Api() );
+                       // we use Page object which calls getUrl which uses 
config variables.
+                       this.sandbox.stub( mw.util, 'getUrl' ).returns( 
'/wiki/Reftest' );
+                       this.sandbox.stub( this.referencesGateway, 
'getReferencesElements' ).returns(
+                               $.Deferred().resolve(
+                                       mw.template.get( 
'tests.mobilefrontend', 'refSection.html' ).render()
+                               ).promise()
+                       );
+                       this.referencesGatewayEmpty = new 
ReferencesMobileViewGateway( new mw.Api() );
+                       this.sandbox.stub( this.referencesGatewayEmpty, 
'getReferencesElements' ).returns(
+                               $.Deferred().resolve( $() ).promise()
+                       );
+                       this.referencesGatewayRejector = new 
ReferencesMobileViewGateway( new mw.Api() );
+                       this.sandbox.stub( this.referencesGatewayRejector, 
'getReferencesElements' ).returns(
+                               $.Deferred().reject().promise()
+                       );
+               }
+       } );
+
+       QUnit.test( 'Gateway only hits api once despite multiple calls', 1, 
function ( assert ) {
+               this.gatewayHitsApi.getReferencesElements( this.page );
+               this.gatewayHitsApi.getReferencesElements( this.page );
+               this.gatewayHitsApi.getReferencesElements( this.page );
+               assert.strictEqual( this.api.get.calledOnce, true, 'The API 
should only ever be hit once.' );
+       } );
+
+       QUnit.test( 'checking good reference', 1, function ( assert ) {
+               var done = assert.async( 1 );
+               this.referencesGateway.getReference( '#cite_note-1', this.page 
).done( function ( ref ) {
+                       assert.strictEqual( ref.text, 'real lazy' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'checking good reference (subsequent calls)', 1, function ( 
assert ) {
+               var done = assert.async( 1 );
+               this.referencesGateway.getReference( '#cite_note-1', this.page 
);
+               this.referencesGateway.getReference( '#cite_note-2', this.page 
).done( function ( ref ) {
+                       assert.strictEqual( ref.text, 'real lazy 2' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'checking bad reference', 1, function ( assert ) {
+               var done = assert.async( 1 );
+               this.referencesGateway.getReference( '#cite_note-bad', 
this.page ).done( function ( ref ) {
+                       assert.ok( ref === false, 'When bad id given false 
returned.' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'checking reference on non-existent page', 1, function ( 
assert ) {
+               var done = assert.async( 1 );
+               this.referencesGatewayEmpty.getReference( '#cite_note-bad', 
this.page ).done( function ( ref ) {
+                       assert.ok( ref === false,
+                               'When getReferencesElement returns empty list 
of elements reference is false.' );
+                       done();
+               } );
+       } );
+
+       QUnit.test( 'checking reference when gateway rejects', 1, function ( 
assert ) {
+               var done = assert.async( 1 );
+               this.referencesGatewayRejector.getReference( '#cite_note-bad', 
this.page ).fail( function () {
+                       assert.ok( true, 'getReference is rejected if API query 
fails' );
+                       done();
+               } );
+       } );
+
+} )( jQuery, mw.mobileFrontend );
diff --git a/tests/qunit/mobile.references/test_references.js 
b/tests/qunit/mobile.references/test_references.js
deleted file mode 100644
index 7a4d3c5..0000000
--- a/tests/qunit/mobile.references/test_references.js
+++ /dev/null
@@ -1,57 +0,0 @@
-( function ( $, M ) {
-
-       var R = mw.mobileFrontend.require( 'mobile.references/references' ),
-               Page = M.require( 'mobile.startup/Page' );
-
-       QUnit.module( 'MobileFrontend references.js', {
-               setup: function () {
-                       this.$container = mw.template.get( 
'tests.mobilefrontend', 'references.html' )
-                               .render().appendTo( '#qunit-fixture' );
-                       this.page = new Page( {
-                               el: this.$container,
-                               title: 'Reftest'
-                       } );
-                       // we use Page object which calls getUrl which uses 
config variables.
-                       this.sandbox.stub( mw.util, 'getUrl' ).returns( 
'/wiki/Reftest' );
-               }
-       } );
-
-       QUnit.test( 'Standard', 1, function ( assert ) {
-               this.sandbox.stub( mw.config, 'get' ).withArgs( 
'wgMFLazyLoadReferences' ).returns( {
-                       beta: false,
-                       base: false
-               } );
-               R.getReference( '#cite_note-1', this.page ).done( function ( 
ref ) {
-                       assert.strictEqual( $( '<div>' ).html( ref.text ).find( 
'.reference-text' ).text(), 'hello' );
-               } );
-       } );
-
-       QUnit.test( 'Lazy loaded', 1, function ( assert ) {
-               this.sandbox.stub( mw.Api.prototype, 'get' ).returns(
-                       $.Deferred().resolve( {
-                               query: {
-                                       pages: [
-                                               {
-                                                       references: {
-                                                               'cite_note-1': {
-                                                                       key: 1,
-                                                                       // 
include html to avoid hitting EditorGateway
-                                                                       html: 
'<i>so lazy</i>',
-                                                                       text: 
'\'\'so lazy\'\''
-                                                               }
-                                                       }
-                                               }
-                                       ]
-                               }
-                       } )
-               );
-               this.sandbox.stub( mw.config, 'get' ).withArgs( 
'wgMFLazyLoadReferences' ).returns( {
-                       beta: true,
-                       base: true
-               } );
-               R.getReference( '#cite_note-1', this.page ).done( function ( 
ref ) {
-                       assert.strictEqual( ref.text, '<i>so lazy</i>' );
-               } );
-       } );
-
-} )( jQuery, mw.mobileFrontend );
diff --git a/tests/qunit/tests.mobilefrontend/pageWithStrippedRefs.html 
b/tests/qunit/tests.mobilefrontend/pageWithStrippedRefs.html
new file mode 100644
index 0000000..7cba1d4
--- /dev/null
+++ b/tests/qunit/tests.mobilefrontend/pageWithStrippedRefs.html
@@ -0,0 +1,8 @@
+<div>
+       <h2><span class="mw-headline" id="1.0">A1</span></h1>
+       <div>text</div>
+       <h2>references</h2>
+       <div>
+               <a class="mf-lazy-references-placeholder">View citations</a>
+       </div>
+</div>
\ No newline at end of file
diff --git a/tests/qunit/tests.mobilefrontend/refSection.html 
b/tests/qunit/tests.mobilefrontend/refSection.html
new file mode 100644
index 0000000..594baa6
--- /dev/null
+++ b/tests/qunit/tests.mobilefrontend/refSection.html
@@ -0,0 +1,4 @@
+<ol class="references">
+       <li id="cite_note-1">real lazy</li>
+       <li id="cite_note-2">real lazy 2</li>
+</ul>
\ No newline at end of file

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

Gerrit-MessageType: merged
Gerrit-Change-Id: Iec812faf12182f80f5a2193c2bc4b9b95ea86bbe
Gerrit-PatchSet: 29
Gerrit-Project: mediawiki/extensions/MobileFrontend
Gerrit-Branch: master
Gerrit-Owner: Jdlrobson <jrob...@wikimedia.org>
Gerrit-Reviewer: Bmansurov <bmansu...@wikimedia.org>
Gerrit-Reviewer: Jdlrobson <jrob...@wikimedia.org>
Gerrit-Reviewer: Jhobs <jhob...@wikimedia.org>
Gerrit-Reviewer: Phuedx <g...@samsmith.io>
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