diff --git a/includes/Minerva.hooks.php b/includes/Minerva.hooks.php index 1f37179..c65d939 100644 --- a/includes/Minerva.hooks.php +++ b/includes/Minerva.hooks.php @@ -49,12 +49,17 @@ class MinervaHooks { ) { $testModule = [ 'dependencies' => [ + 'mobile.startup', 'skins.minerva.notifications.badge' ], 'localBasePath' => dirname( __DIR__ ), 'remoteSkinPath' => 'MinervaNeue', 'targets' => [ 'mobile', 'desktop' ], 'scripts' => [ + // additional scaffolding (minus initialisation scripts) + 'resources/skins.minerva.scripts/DownloadIcon.js', + // test files + 'tests/qunit/skins.minerva.scripts/test_DownloadIcon.js', 'tests/qunit/skins.minerva.notifications.badge/test_NotificationBadge.js' ], ]; diff --git a/jsduck.json b/jsduck.json index a196292..f95af78 100644 --- a/jsduck.json +++ b/jsduck.json @@ -14,6 +14,7 @@ "jqXHR", "View", "Page", + "Skin", "File", "Router", "Icon", diff --git a/resources/skins.minerva.scripts/DownloadIcon.js b/resources/skins.minerva.scripts/DownloadIcon.js index b2969ad..7f38aeb 100644 --- a/resources/skins.minerva.scripts/DownloadIcon.js +++ b/resources/skins.minerva.scripts/DownloadIcon.js @@ -1,6 +1,7 @@ ( function ( M ) { var msg = mw.msg, + MAX_PRINT_TIMEOUT = 3000, Icon = M.require( 'mobile.startup/Icon' ); /** @@ -8,10 +9,12 @@ * @class DownloadIcon * @extends Icon * + * @param {Skin} skin * @constructor */ - function DownloadIcon() { + function DownloadIcon( skin ) { var options = {}; + this.skin = skin; options.tagName = 'li'; options.title = msg( 'minerva-download' ); options.name = 'download'; @@ -19,8 +22,47 @@ } OO.mfExtend( DownloadIcon, Icon, { + /** + * Replace download icon with a spinner + */ + showSpinner: function () { + this.options.name = 'spinner'; + this.render(); + }, + /** + * Restore download icon from spinner state + */ + hideSpinner: function () { + this.options.name = 'download'; + this.render(); + }, + isTemplateMode: false, + /** + * onClick handler for button that invokes print function + */ onClick: function () { - window.print(); + var self = this, + hideSpinner = this.hideSpinner.bind( this ); + + function doPrint() { + self.timeout = clearTimeout( self.timeout ); + hideSpinner(); + window.print(); + } + + // The click handler may be invoked multiple times so if a pending print is occurring + // do nothing. + if ( !this.timeout ) { + this.showSpinner(); + // If all image downloads are taking longer to load then the MAX_PRINT_TIMEOUT + // abort the spinner and print regardless. + this.timeout = setTimeout( doPrint, MAX_PRINT_TIMEOUT ); + this.skin.loadImagesList().always( function () { + if ( self.timeout ) { + doPrint(); + } + } ); + } }, events: { click: 'onClick' diff --git a/tests/qunit/skins.minerva.scripts/test_DownloadIcon.js b/tests/qunit/skins.minerva.scripts/test_DownloadIcon.js new file mode 100644 index 0000000..4ab4e60 --- /dev/null +++ b/tests/qunit/skins.minerva.scripts/test_DownloadIcon.js @@ -0,0 +1,67 @@ +( function ( M ) { + var Skin = M.require( 'mobile.startup/Skin' ), + Deferred = $.Deferred, + DownloadIcon = M.require( 'skins.minerva.scripts/DownloadIcon' ); + + QUnit.module( 'Minerva DownloadIcon', { + setup: function () { + this.skin = new Skin( {} ); + } + } ); + + QUnit.test( '#DownloadIcon (print after image download)', function ( assert ) { + var icon = new DownloadIcon( this.skin ), + d = Deferred(), + spy = this.sandbox.stub( window, 'print' ); + + this.sandbox.stub( this.skin, 'loadImagesList' ).returns( d.resolve() ); + + icon.onClick(); + d.then( function () { + assert.ok( spy.calledOnce, 'Print occurred.' ); + } ); + + return d; + } ); + + QUnit.test( '#DownloadIcon (print via timeout)', function ( assert ) { + var icon = new DownloadIcon( this.skin ), + d = Deferred(), + spy = this.sandbox.stub( window, 'print' ); + + this.sandbox.stub( this.skin, 'loadImagesList' ).returns( d ); + + window.setTimeout( function () { + d.resolve(); + }, 3400 ); + + icon.onClick(); + d.then( function () { + assert.ok( spy.calledOnce, + 'Print was called once despite loadImagesList resolving after MAX_PRINT_TIMEOUT' ); + } ); + + return d; + } ); + + QUnit.test( '#DownloadIcon (multiple clicks)', function ( assert ) { + var icon = new DownloadIcon( this.skin ), + d = Deferred(), + spy = this.sandbox.stub( window, 'print' ); + + this.sandbox.stub( this.skin, 'loadImagesList' ).returns( d ); + + window.setTimeout( function () { + d.resolve(); + }, 3400 ); + + icon.onClick(); + icon.onClick(); + d.then( function () { + assert.ok( spy.calledOnce, + 'Print was called once despite multiple clicks' ); + } ); + + return d; + } ); +}( mw.mobileFrontend ) );