Gilles has uploaded a new change for review.
https://gerrit.wikimedia.org/r/179918
Change subject: Track the most recent upload time for performance events
......................................................................
Track the most recent upload time for performance events
Change-Id: I673f9487deea15dc148452a3a4d6b91563a2c417
Bug: T76035
---
M MultimediaViewer.php
M resources/mmv/logging/mmv.logging.PerformanceLogger.js
M resources/mmv/mmv.js
M resources/mmv/mmv.lightboximage.js
M resources/mmv/model/mmv.model.Image.js
M resources/mmv/provider/mmv.provider.Image.js
M tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
M tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
M tests/qunit/mmv/mmv.test.js
M tests/qunit/mmv/model/mmv.model.Image.test.js
M tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js
11 files changed, 181 insertions(+), 86 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MultimediaViewer
refs/changes/18/179918/1
diff --git a/MultimediaViewer.php b/MultimediaViewer.php
index f69e4ae..e8f8628 100644
--- a/MultimediaViewer.php
+++ b/MultimediaViewer.php
@@ -1063,7 +1063,7 @@
$wgHooks['EventLoggingRegisterSchemas'][] = function( array &$schemas ) {
$schemas += array(
'MediaViewer' => 10536413,
- 'MultimediaViewerNetworkPerformance' => 7917896,
+ 'MultimediaViewerNetworkPerformance' => 10596581,
'MultimediaViewerDuration' => 10427980,
'MultimediaViewerAttribution' => 9758179,
'MultimediaViewerDimensions' => 10014238,
diff --git a/resources/mmv/logging/mmv.logging.PerformanceLogger.js
b/resources/mmv/logging/mmv.logging.PerformanceLogger.js
index bfde462..2ac6e1b 100644
--- a/resources/mmv/logging/mmv.logging.PerformanceLogger.js
+++ b/resources/mmv/logging/mmv.logging.PerformanceLogger.js
@@ -62,9 +62,10 @@
* cached by the browser, as it will consume unnecessary bandwidth for
the user.
* @param {string} type the type of request to be measured
* @param {string} url URL to be measured
+ * @param {jQuery.Deferred.<string>} [extraStatsDeferred] A promise
which resolves to the extra stats.
* @returns {jQuery.Promise} A promise that resolves when the contents
of the URL have been fetched
*/
- PL.record = function ( type, url ) {
+ PL.record = function ( type, url, extraStatsDeferred ) {
var deferred = $.Deferred(),
request,
perf = this,
@@ -89,7 +90,7 @@
if ( request.readyState === 4 ) {
deferred.notify( request.response, 100
);
deferred.resolve( request.response );
- perf.recordEntryDelayed( type, total,
url, request );
+ perf.recordEntryDelayed( type, total,
url, request, extraStatsDeferred );
}
};
@@ -111,9 +112,11 @@
* @param {number} total the total load time tracked with a basic
technique
* @param {string} url URL of that was measured
* @param {XMLHttpRequest} request HTTP request that just completed
+ * @param {jQuery.Deferred.<string>} [extraStatsDeferred] A promise
which resolves to extra stats to be included.
*/
- PL.recordEntry = function ( type, total, url, request ) {
+ PL.recordEntry = function ( type, total, url, request,
extraStatsDeferred ) {
var matches,
+ logger = this,
stats = { type: type,
contentHost: window.location.host,
isHttps: window.location.protocol === 'https:',
@@ -165,7 +168,11 @@
}
}
- this.log( stats );
+ ( extraStatsDeferred || $.Deferred().reject() ).done( function
( extraStats ) {
+ stats = $.extend( stats, extraStats );
+ } ).always( function () {
+ logger.log( stats );
+ } );
};
/**
@@ -305,14 +312,15 @@
* @param {number} total the total load time tracked with a basic
technique
* @param {string} url URL of that was measured
* @param {XMLHttpRequest} request HTTP request that just completed
+ * @param {jQuery.Promise.<string>} extraStatsDeferred A promise which
resolves to extra stats.
*/
- PL.recordEntryDelayed = function ( type, total, url, request ) {
+ PL.recordEntryDelayed = function ( type, total, url, request,
extraStatsDeferred ) {
var perf = this;
// The timeout is necessary because if there's an entry in
window.performance,
// it hasn't been added yet at this point
setTimeout( function() {
- perf.recordEntry( type, total, url, request );
+ perf.recordEntry( type, total, url, request,
extraStatsDeferred );
}, 0 );
};
@@ -402,6 +410,15 @@
return new XMLHttpRequest();
};
+ /**
+ * @override
+ * @inheritdoc
+ */
+ PL.log = function ( data ) {
+ mw.log( 'mw.mmv.logging.PerformanceLogger', data );
+ return mw.mmv.logging.Logger.prototype.log.call( this, data );
+ };
+
new PerformanceLogger().init();
mw.mmv.logging.PerformanceLogger = PerformanceLogger;
diff --git a/resources/mmv/mmv.js b/resources/mmv/mmv.js
index 99ebb92..00ed4af 100644
--- a/resources/mmv/mmv.js
+++ b/resources/mmv/mmv.js
@@ -157,6 +157,8 @@
thumb.thumb,
thumb.caption
);
+
+ thumb.extraStatsDeferred = $.Deferred();
}
};
@@ -249,9 +251,9 @@
imagePromise,
metadataPromise,
start,
- uploadTimestamp,
viewer = this,
- $initialImage = $( initialImage );
+ $initialImage = $( initialImage ),
+ extraStatsDeferred = $.Deferred();
this.currentIndex = image.index;
@@ -282,7 +284,8 @@
start = $.now();
mw.mmv.dimensionLogger.logDimensions( imageWidths,
canvasDimensions, 'show' );
- imagePromise = this.fetchThumbnailForLightboxImage( image,
imageWidths.real );
+
+ imagePromise = this.fetchThumbnailForLightboxImage( image,
imageWidths.real, extraStatsDeferred );
this.resetBlurredThumbnailStates();
if ( imagePromise.state() === 'pending' ) {
@@ -302,19 +305,12 @@
mw.mmv.durationLogger.stop(
'click-to-first-image' );
metadataPromise.done( function ( imageInfo ) {
- if ( !imageInfo ||
!imageInfo.uploadDateTime ) {
+ if ( !imageInfo ||
!imageInfo.anonymizedUploadDateTime ) {
return;
}
- uploadTimestamp =
imageInfo.uploadDateTime.toString();
- // Convert to "timestamp" format
commonly used in EventLogging
- uploadTimestamp =
uploadTimestamp.replace( /[:\s]/g, '' );
- // Anonymise the timestamp to avoid
making the file identifiable
- // We only need to know the day
- uploadTimestamp =
uploadTimestamp.substr( 0, uploadTimestamp.length - 6 ) + '000000';
-
mw.mmv.durationLogger.record(
'click-to-first-image', {
- uploadTimestamp: uploadTimestamp
+ uploadTimestamp:
imageInfo.anonymizedUploadDateTime
} );
} );
}
@@ -324,6 +320,8 @@
} );
metadataPromise.done( function ( imageInfo, repoInfo, userInfo
) {
+ extraStatsDeferred.resolve( { uploadTimestamp:
imageInfo.anonymizedUploadDateTime } );
+
if ( viewer.currentIndex !== image.index ) {
return;
}
@@ -337,6 +335,8 @@
// File reuse steals a bunch of information from the
DOM, so do it last
viewer.ui.setFileReuseData( imageInfo, repoInfo,
image.caption );
} ).fail( function ( error ) {
+ extraStatsDeferred.reject();
+
if ( viewer.currentIndex !== image.index ) {
return;
}
@@ -579,13 +579,15 @@
if ( this.currentIndex + i < this.thumbs.length ) {
callback(
this.currentIndex + i,
- this.thumbs[ this.currentIndex + i
].image
+ this.thumbs[ this.currentIndex + i
].image,
+ this.thumbs[ this.currentIndex + i
].extraStatsDeferred
);
}
if ( i && this.currentIndex - i >= 0 ) { // skip
duplicate for i==0
callback(
this.currentIndex - i,
- this.thumbs[ this.currentIndex - i
].image
+ this.thumbs[ this.currentIndex - i
].image,
+ this.thumbs[ this.currentIndex - i
].extraStatsDeferred
);
}
}
@@ -601,8 +603,8 @@
MMVP.pushLightboxImagesIntoQueue = function( taskFactory ) {
var queue = new mw.mmv.model.TaskQueue();
- this.eachPrealoadableLightboxIndex( function( i, lightboxImage
) {
- queue.push( taskFactory( lightboxImage ) );
+ this.eachPrealoadableLightboxIndex( function( i, lightboxImage,
extraStatsDeferred ) {
+ queue.push( taskFactory( lightboxImage,
extraStatsDeferred ) );
} );
return queue;
@@ -636,9 +638,15 @@
this.cancelImageMetadataPreloading();
- this.metadataPreloadQueue = this.pushLightboxImagesIntoQueue(
function( lightboxImage ) {
+ this.metadataPreloadQueue = this.pushLightboxImagesIntoQueue(
function( lightboxImage, extraStatsDeferred ) {
return function() {
- return viewer.fetchSizeIndependentLightboxInfo(
lightboxImage.filePageTitle );
+ var metadatapromise =
viewer.fetchSizeIndependentLightboxInfo( lightboxImage.filePageTitle );
+ metadatapromise.done( function ( imageInfo ) {
+ extraStatsDeferred.resolve( {
uploadTimestamp: imageInfo.anonymizedUploadDateTime } );
+ } ).fail( function () {
+ extraStatsDeferred.reject();
+ } );
+ return metadatapromise;
};
} );
@@ -655,7 +663,7 @@
this.cancelThumbnailsPreloading();
- this.thumbnailPreloadQueue = this.pushLightboxImagesIntoQueue(
function( lightboxImage ) {
+ this.thumbnailPreloadQueue = this.pushLightboxImagesIntoQueue(
function( lightboxImage, extraStatsDeferred ) {
return function() {
var imageWidths, canvasDimensions;
@@ -670,7 +678,7 @@
mw.mmv.dimensionLogger.logDimensions(
imageWidths, canvasDimensions, 'preload' );
- return viewer.fetchThumbnailForLightboxImage(
lightboxImage, imageWidths.real );
+ return viewer.fetchThumbnailForLightboxImage(
lightboxImage, imageWidths.real, extraStatsDeferred );
};
} );
@@ -721,15 +729,17 @@
* Loads size-dependent components of a lightbox - the thumbnail model
and the image itself.
* @param {mw.mmv.LightboxImage} image
* @param {number} width the width of the requested thumbnail
+ * @param {jQuery.Deferred.<string>} [extraStatsDeferred] Promise that
resolves to the image's upload timestamp when the metadata is loaded
* @returns {jQuery.Promise.<mw.mmv.model.Thumbnail, HTMLImageElement>}
*/
- MMVP.fetchThumbnailForLightboxImage = function ( image, width ) {
+ MMVP.fetchThumbnailForLightboxImage = function ( image, width,
extraStatsDeferred ) {
return this.fetchThumbnail(
image.filePageTitle,
width,
image.src,
image.originalWidth,
- image.originalHeight
+ image.originalHeight,
+ extraStatsDeferred
);
};
@@ -740,11 +750,12 @@
* @param {string} [sampleUrl] a thumbnail URL for the same file (but
with different size) (might be missing)
* @param {number} [originalWidth] the width of the original,
full-sized file (might be missing)
* @param {number} [originalHeight] the height of the original,
full-sized file (might be missing)
+ * @param {jQuery.Deferred.<string>} [extraStatsDeferred] Promise that
resolves to the image's upload timestamp when the metadata is loaded
* @returns {jQuery.Promise.<mw.mmv.model.Thumbnail, HTMLImageElement>}
A promise resolving to
* a thumbnail model and an <img> element. It might or might not have
progress events which
* return a single number.
*/
- MMVP.fetchThumbnail = function ( fileTitle, width, sampleUrl,
originalWidth, originalHeight ) {
+ MMVP.fetchThumbnail = function ( fileTitle, width, sampleUrl,
originalWidth, originalHeight, extraStatsDeferred ) {
var viewer = this,
guessing = false,
thumbnailPromise,
@@ -771,7 +782,7 @@
}
imagePromise = thumbnailPromise.then( function ( thumbnail ) {
- return viewer.imageProvider.get( thumbnail.url );
+ return viewer.imageProvider.get( thumbnail.url,
extraStatsDeferred );
} );
if ( guessing ) {
@@ -780,7 +791,7 @@
// because thumbnailInfoProvider.get is already called
above when guessedThumbnailInfoProvider.get fails.
imagePromise = imagePromise.then( null, function () {
return viewer.thumbnailInfoProvider.get(
fileTitle, width ).then( function ( thumbnail ) {
- return viewer.imageProvider.get(
thumbnail.url );
+ return viewer.imageProvider.get(
thumbnail.url, extraStatsDeferred );
} );
} );
}
diff --git a/resources/mmv/mmv.lightboximage.js
b/resources/mmv/mmv.lightboximage.js
index 0916720..ddb1418 100644
--- a/resources/mmv/mmv.lightboximage.js
+++ b/resources/mmv/mmv.lightboximage.js
@@ -54,23 +54,5 @@
this.originalHeight = undefined;
}
- var LIP = LightboxImage.prototype;
-
- /**
- * The URL of the image (in the size we intend use to display the it in
the lightbox)
- * @type {String}
- * @protected
- */
- LIP.src = null;
-
- /**
- * The URL of a placeholder while the image loads. Typically a smaller
version of the image, which is already
- * loaded in the browser.
- * @type {String}
- * @return {jQuery.Promise.<mw.mmv.LightboxImage, HTMLImageElement>}
- * @protected
- */
- LIP.initialSrc = null;
-
mw.mmv.LightboxImage = LightboxImage;
}( mediaWiki, jQuery ) );
diff --git a/resources/mmv/model/mmv.model.Image.js
b/resources/mmv/model/mmv.model.Image.js
index 59382ed..c5c3b06 100644
--- a/resources/mmv/model/mmv.model.Image.js
+++ b/resources/mmv/model/mmv.model.Image.js
@@ -33,6 +33,7 @@
* @param {string} repo The repository this image belongs to
* @param {string} lastUploader The last person to upload a version of
this image.
* @param {string} uploadDateTime The time and date the last upload
occurred
+ * @param {string} anonymizedUploadDateTime Anonymized and EL-friendly
version of uploadDateTime
* @param {string} creationDateTime The time and date the original
upload occurred
* @param {string} description
* @param {string} source
@@ -55,6 +56,7 @@
repo,
lastUploader,
uploadDateTime,
+ anonymizedUploadDateTime,
creationDateTime,
description,
source,
@@ -97,6 +99,9 @@
/** @property {string} uploadDateTime The date and time of the
last upload */
this.uploadDateTime = uploadDateTime;
+
+ /** @property {string} anonymizedUploadDateTime The anonymized
date and time of the last upload */
+ this.anonymizedUploadDateTime = anonymizedUploadDateTime;
/** @property {string} creationDateTime The date and time that
the image was created */
this.creationDateTime = creationDateTime;
@@ -144,7 +149,7 @@
* @returns {mw.mmv.model.Image}
*/
Image.newFromImageInfo = function ( title, imageInfo ) {
- var name, uploadDateTime, creationDateTime, imageData,
+ var name, uploadDateTime, anonymizedUploadDateTime,
creationDateTime, imageData,
description, source, author, authorCount, license,
permission,
latitude, longitude,
innerInfo = imageInfo.imageinfo[0],
@@ -152,7 +157,15 @@
if ( extmeta ) {
creationDateTime = this.parseExtmeta(
extmeta.DateTimeOriginal, 'plaintext' );
- uploadDateTime = this.parseExtmeta( extmeta.DateTime,
'plaintext' );
+ uploadDateTime = this.parseExtmeta( extmeta.DateTime,
'plaintext' ).toString();
+
+ // Convert to "timestamp" format commonly used in
EventLogging
+ anonymizedUploadDateTime = uploadDateTime.replace(
/[^\d]/g, '' );
+
+ // Anonymise the timestamp to avoid making the file
identifiable
+ // We only need to know the day
+ anonymizedUploadDateTime =
anonymizedUploadDateTime.substr( 0, anonymizedUploadDateTime.length - 6 ) +
'000000';
+
name = this.parseExtmeta( extmeta.ObjectName,
'plaintext' );
description = this.parseExtmeta(
extmeta.ImageDescription, 'string' );
@@ -172,6 +185,7 @@
name = title.getNameText();
}
+
imageData = new Image(
title,
name,
@@ -184,6 +198,7 @@
imageInfo.imagerepository,
innerInfo.user,
uploadDateTime,
+ anonymizedUploadDateTime,
creationDateTime,
description,
source,
diff --git a/resources/mmv/provider/mmv.provider.Image.js
b/resources/mmv/provider/mmv.provider.Image.js
index 2eccd43..fcd3ddf 100644
--- a/resources/mmv/provider/mmv.provider.Image.js
+++ b/resources/mmv/provider/mmv.provider.Image.js
@@ -44,11 +44,12 @@
* Loads an image and returns it. Includes performance metrics via
mw.mmv.logging.PerformanceLogger.
* When the browser supports it, the image is loaded as an AJAX request.
* @param {string} url
+ * @param {jQuery.Deferred.<string>} extraStatsDeferred A promise which
resolves to extra statistics.
* @return {jQuery.Promise.<HTMLImageElement>} A promise which resolves
to the image object.
* When loaded via AJAX, it has progress events, which return an array
with the content loaded
* so far and with the progress as a floating-point number between 0
and 100.
*/
- Image.prototype.get = function ( url ) {
+ Image.prototype.get = function ( url, extraStatsDeferred ) {
var provider = this,
cacheKey = url,
extraParam = {},
@@ -65,12 +66,12 @@
if ( !this.cache[cacheKey] ) {
if ( this.imagePreloadingSupported() ) {
rawGet = $.proxy( provider.rawGet, provider,
url, true );
- this.cache[cacheKey] = this.performance.record(
'image', url ).then( rawGet, rawGet );
+ this.cache[cacheKey] = this.performance.record(
'image', url, extraStatsDeferred ).then( rawGet, rawGet );
} else {
start = $.now();
this.cache[cacheKey] = this.rawGet( url );
this.cache[cacheKey].always( function () {
- provider.performance.recordEntry(
'image', $.now() - start, url );
+ provider.performance.recordEntry(
'image', $.now() - start, url, undefined, extraStatsDeferred );
} );
}
this.cache[cacheKey].fail( function ( error ) {
diff --git a/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
b/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
index be38879..3d97453 100644
--- a/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
+++ b/tests/qunit/mmv/logging/mmv.logging.PerformanceLogger.test.js
@@ -186,6 +186,49 @@
assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1
].bandwidth, Math.round( bandwidth ), 'bandwidth is correct' );
} );
+ QUnit.test( 'recordEntry: with async extra stats', 11, function (
assert ) {
+ var performance = new mw.mmv.logging.PerformanceLogger(),
+ fakeEventLog = { logEvent: this.sandbox.stub() },
+ type = 'gender',
+ total = 100,
+ overriddenType = 'image',
+ foo = 'bar',
+ extraStatsPromise = $.Deferred();
+
+ this.sandbox.stub( performance, 'loadDependencies' ).returns(
$.Deferred().resolve() );
+ this.sandbox.stub( performance, 'isInSample' );
+ performance.setEventLog( fakeEventLog );
+
+ performance.isInSample.returns( true );
+
+ performance.recordEntry( type, total, 'URL1', undefined,
extraStatsPromise );
+
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 0, 'Stats
should not be logged if the promise hasn\'t completed yet' );
+
+ extraStatsPromise.reject();
+
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 1, 'Stats
should be logged' );
+
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 0
], 'MultimediaViewerNetworkPerformance', 'EventLogging schema is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1
].type, type, 'type is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 0 ).args[ 1
].total, total, 'total is correct' );
+
+ extraStatsPromise = $.Deferred();
+
+ performance.recordEntry( type, total, 'URL2', undefined,
extraStatsPromise );
+
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 1, 'Stats
should not be logged if the promise hasn\'t been resolved yet' );
+
+ extraStatsPromise.resolve( { type: overriddenType, foo: foo } );
+
+ assert.strictEqual( fakeEventLog.logEvent.callCount, 2, 'Stats
should be logged' );
+
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 0
], 'MultimediaViewerNetworkPerformance', 'EventLogging schema is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 1
].type, overriddenType, 'type is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 1
].total, total, 'total is correct' );
+ assert.strictEqual( fakeEventLog.logEvent.getCall( 1 ).args[ 1
].foo, foo, 'extra stat is correct' );
+ } );
+
QUnit.test( 'parseVarnishXCacheHeader', 15, function ( assert ) {
var varnish1 = 'cp1061',
varnish2 = 'cp3006',
diff --git a/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
b/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
index 10d6aca..b7875a0 100644
--- a/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
+++ b/tests/qunit/mmv/mmv.EmbedFileFormatter.test.js
@@ -6,7 +6,7 @@
options.licenseInternalName,
options.licenseLongName, options.licenseUrl ) : undefined,
imageInfo = new mw.mmv.model.Image( options.title,
options.title.getNameText(), undefined,
undefined, undefined, undefined,
options.imgUrl, options.filePageUrl, 'repo', undefined,
- undefined, undefined, undefined,
options.source, options.author, options.authorCount, license ),
+ undefined, undefined, undefined, undefined,
options.source, options.author, options.authorCount, license ),
repoInfo = { displayName: options.siteName, getSiteLink:
function () { return options.siteUrl; } };
diff --git a/tests/qunit/mmv/mmv.test.js b/tests/qunit/mmv/mmv.test.js
index 50b1e09..28019f9 100644
--- a/tests/qunit/mmv/mmv.test.js
+++ b/tests/qunit/mmv/mmv.test.js
@@ -102,21 +102,25 @@
QUnit.test( 'Progress', 4, function ( assert ) {
var imageDeferred = $.Deferred(),
- viewer = new mw.mmv.MultimediaViewer( { get : $.noop }
);
+ viewer = new mw.mmv.MultimediaViewer( { get : $.noop }
),
+ fakeImage = {
+ filePageTitle: new mw.Title( 'File:Stuff.jpg' ),
+ extraStatsDeferred: $.Deferred().reject()
+ };
viewer.thumbs = [];
viewer.displayPlaceholderThumbnail = $.noop;
viewer.setImage = $.noop;
viewer.scroll = $.noop;
viewer.preloadFullscreenThumbnail = $.noop;
- viewer.fetchSizeIndependentLightboxInfo = function () { return
$.Deferred().resolve(); };
+ viewer.fetchSizeIndependentLightboxInfo = function () { return
$.Deferred().resolve( {} ); };
viewer.ui = {
setFileReuseData: $.noop,
setupForLoad: $.noop,
- canvas: { set : $.noop,
+ canvas: { set: $.noop,
unblurWithAnimation: $.noop,
unblur: $.noop,
- getCurrentImageWidths: function () { return {
real : 0 }; },
+ getCurrentImageWidths: function () { return {
real: 0 }; },
getDimensions: function () { return {}; }
},
panel: {
@@ -132,10 +136,10 @@
open: $.noop };
viewer.imageProvider.get = function() { return
imageDeferred.promise(); };
- viewer.imageInfoProvider.get = function() { return
$.Deferred().resolve(); };
+ viewer.imageInfoProvider.get = function() { return
$.Deferred().resolve( {} ); };
viewer.thumbnailInfoProvider.get = function() { return
$.Deferred().resolve( {} ); };
- viewer.loadImage( { filePageTitle : new mw.Title(
'File:Stuff.jpg' ) }, new Image() );
+ viewer.loadImage( fakeImage, new Image() );
assert.ok(
viewer.ui.panel.progressBar.jumpTo.lastCall.calledWith( 0 ),
'Percentage correctly reset by loadImage' );
@@ -156,8 +160,16 @@
QUnit.test( 'Progress when switching images', 11, function ( assert ) {
var firstImageDeferred = $.Deferred(),
secondImageDeferred = $.Deferred(),
- firstImage = { index: 1, filePageTitle : new mw.Title(
'File:First.jpg' ) },
- secondImage = { index: 2, filePageTitle : new mw.Title(
'File:Second.jpg' ) },
+ firstImage = {
+ index: 1,
+ filePageTitle: new mw.Title( 'File:First.jpg' ),
+ extraStatsDeferred: $.Deferred().reject()
+ },
+ secondImage = {
+ index: 2,
+ filePageTitle: new mw.Title( 'File:Second.jpg'
),
+ extraStatsDeferred: $.Deferred().reject()
+ },
viewer = new mw.mmv.MultimediaViewer( { get : $.noop }
);
viewer.thumbs = [];
@@ -167,7 +179,7 @@
viewer.preloadFullscreenThumbnail = $.noop;
viewer.preloadImagesMetadata = $.noop;
viewer.preloadThumbnails = $.noop;
- viewer.fetchSizeIndependentLightboxInfo = function () { return
$.Deferred().resolve(); };
+ viewer.fetchSizeIndependentLightboxInfo = function () { return
$.Deferred().resolve( {} ); };
viewer.ui = {
setFileReuseData: $.noop,
setupForLoad : $.noop,
@@ -191,7 +203,7 @@
open : $.noop,
empty: $.noop };
- viewer.imageInfoProvider.get = function() { return
$.Deferred().resolve(); };
+ viewer.imageInfoProvider.get = function() { return
$.Deferred().resolve( {} ); };
viewer.thumbnailInfoProvider.get = function() { return
$.Deferred().resolve( {} ); };
// load some image
@@ -361,7 +373,17 @@
firstImageDeferred = $.Deferred(),
secondImageDeferred = $.Deferred(),
firstLigthboxInfoDeferred = $.Deferred(),
- secondLigthboxInfoDeferred = $.Deferred();
+ secondLigthboxInfoDeferred = $.Deferred(),
+ firstImage = {
+ filePageTitle: new mw.Title( 'File:Foo.jpg' ),
+ index: 0,
+ extraStatsDeferred: $.Deferred().reject()
+ },
+ secondImage = {
+ filePageTitle: new mw.Title( 'File:Bar.jpg' ),
+ index: 1,
+ extraStatsDeferred: $.Deferred().reject()
+ };
viewer.preloadFullscreenThumbnail = $.noop;
viewer.fetchSizeIndependentLightboxInfo = this.sandbox.stub();
@@ -395,25 +417,25 @@
viewer.imageProvider.get.returns( firstImageDeferred.promise()
);
viewer.fetchSizeIndependentLightboxInfo.returns(
firstLigthboxInfoDeferred.promise() );
- viewer.loadImage( { filePageTitle : new mw.Title(
'File:Foo.jpg' ), index : 0 }, new Image() );
+ viewer.loadImage( firstImage, new Image() );
assert.ok( !viewer.animateMetadataDivOnce.called, 'Metadata of
the first image should not be animated' );
assert.ok( !viewer.ui.panel.setImageInfo.called, 'Metadata of
the first image should not be shown' );
viewer.imageProvider.get.returns( secondImageDeferred.promise()
);
viewer.fetchSizeIndependentLightboxInfo.returns(
secondLigthboxInfoDeferred.promise() );
- viewer.loadImage( { filePageTitle : new mw.Title(
'File:Bar.jpg' ), index : 1 }, new Image() );
+ viewer.loadImage( secondImage, new Image() );
viewer.ui.panel.progressBar.animateTo.reset();
firstImageDeferred.notify( undefined, 45 );
assert.ok( !viewer.ui.panel.progressBar.animateTo.reset.called,
'Progress of the first image should not be shown' );
firstImageDeferred.resolve();
- firstLigthboxInfoDeferred.resolve();
+ firstLigthboxInfoDeferred.resolve( {} );
assert.ok( !viewer.displayRealThumbnail.called, 'The first
image being done loading should have no effect');
viewer.displayRealThumbnail = this.sandbox.spy( function () {
viewer.close(); } );
secondImageDeferred.resolve();
- secondLigthboxInfoDeferred.resolve();
+ secondLigthboxInfoDeferred.resolve( {} );
assert.ok( viewer.displayRealThumbnail.called, 'The second
image being done loading should result in the image being shown');
} );
@@ -438,8 +460,9 @@
viewer.preloadFullscreenThumbnail = $.noop;
viewer.initWithThumbs( [] );
- viewer.loadImage( { filePageTitle : new mw.Title(
'File:Stuff.jpg' ),
- thumbnail : new mw.mmv.model.Thumbnail( 'foo', 10, 10 )
},
+ viewer.loadImage( { filePageTitle: new mw.Title(
'File:Stuff.jpg' ),
+ thumbnail: new mw.mmv.model.Thumbnail( 'foo', 10, 10 ),
+ extraStatsDeferred: $.Deferred().reject() },
new Image() );
viewer.ui.$closeButton.click();
@@ -534,7 +557,7 @@
assert.ok( !guessedThumbnailInfoStub.called, 'When we lack
sample URL and original dimensions, GuessedThumbnailInfoProvider is not called'
);
assert.ok( thumbnailInfoStub.calledOnce, 'When we lack sample
URL and original dimensions, ThumbnailInfoProvider is called once' );
assert.ok( imageStub.calledOnce, 'When we lack sample URL and
original dimensions, ImageProvider is called once' );
- assert.ok( imageStub.calledWithExactly( 'apiURL' ), 'When we
lack sample URL and original dimensions, ImageProvider is called with the API
url' );
+ assert.ok( imageStub.calledWithExactly( 'apiURL', undefined ),
'When we lack sample URL and original dimensions, ImageProvider is called with
the API url' );
assert.strictEqual( promise.state(), 'resolved', 'When we lack
sample URL and original dimensions, fetchThumbnail resolves' );
// When the guesser bails out, the classic provider should be
used
@@ -546,7 +569,7 @@
assert.ok( guessedThumbnailInfoStub.calledOnce, 'When the
guesser bails out, GuessedThumbnailInfoProvider is called once' );
assert.ok( thumbnailInfoStub.calledOnce, 'When the guesser
bails out, ThumbnailInfoProvider is called once' );
assert.ok( imageStub.calledOnce, 'When the guesser bails out,
ImageProvider is called once' );
- assert.ok( imageStub.calledWithExactly( 'apiURL' ), 'When the
guesser bails out, ImageProvider is called with the API url' );
+ assert.ok( imageStub.calledWithExactly( 'apiURL', undefined ),
'When the guesser bails out, ImageProvider is called with the API url' );
assert.strictEqual( promise.state(), 'resolved', 'When the
guesser bails out, fetchThumbnail resolves' );
// When the guesser returns an URL, that should be used
@@ -558,7 +581,7 @@
assert.ok( guessedThumbnailInfoStub.calledOnce, 'When the
guesser returns an URL, GuessedThumbnailInfoProvider is called once' );
assert.ok( !thumbnailInfoStub.called, 'When the guesser returns
an URL, ThumbnailInfoProvider is not called' );
assert.ok( imageStub.calledOnce, 'When the guesser returns an
URL, ImageProvider is called once' );
- assert.ok( imageStub.calledWithExactly( 'guessedURL' ), 'When
the guesser returns an URL, ImageProvider is called with the guessed url' );
+ assert.ok( imageStub.calledWithExactly( 'guessedURL', undefined
), 'When the guesser returns an URL, ImageProvider is called with the guessed
url' );
assert.strictEqual( promise.state(), 'resolved', 'When the
guesser returns an URL, fetchThumbnail resolves' );
// When the guesser returns an URL, but that returns 404, image
loading should be retried with the classic provider
@@ -571,8 +594,8 @@
assert.ok( guessedThumbnailInfoStub.calledOnce, 'When the
guesser returns an URL, but that returns 404, GuessedThumbnailInfoProvider is
called once' );
assert.ok( thumbnailInfoStub.calledOnce, 'When the guesser
returns an URL, but that returns 404, ThumbnailInfoProvider is called once' );
assert.ok( imageStub.calledTwice, 'When the guesser returns an
URL, but that returns 404, ImageProvider is called twice' );
- assert.ok( imageStub.getCall( 0 ).calledWithExactly(
'guessedURL' ), 'When the guesser returns an URL, but that returns 404,
ImageProvider is called first with the guessed url' );
- assert.ok( imageStub.getCall( 1 ).calledWithExactly( 'apiURL'
), 'When the guesser returns an URL, but that returns 404, ImageProvider is
called second with the guessed url' );
+ assert.ok( imageStub.getCall( 0 ).calledWithExactly(
'guessedURL', undefined ), 'When the guesser returns an URL, but that returns
404, ImageProvider is called first with the guessed url' );
+ assert.ok( imageStub.getCall( 1 ).calledWithExactly( 'apiURL',
undefined ), 'When the guesser returns an URL, but that returns 404,
ImageProvider is called second with the guessed url' );
assert.strictEqual( promise.state(), 'resolved', 'When the
guesser returns an URL, but that returns 404, fetchThumbnail resolves' );
// When even the retry fails, fetchThumbnail() should reject
@@ -585,8 +608,8 @@
assert.ok( guessedThumbnailInfoStub.calledOnce, 'When even the
retry fails, GuessedThumbnailInfoProvider is called once' );
assert.ok( thumbnailInfoStub.calledOnce, 'When even the retry
fails, ThumbnailInfoProvider is called once' );
assert.ok( imageStub.calledTwice, 'When even the retry fails,
ImageProvider is called twice' );
- assert.ok( imageStub.getCall( 0 ).calledWithExactly(
'guessedURL' ), 'When even the retry fails, ImageProvider is called first with
the guessed url' );
- assert.ok( imageStub.getCall( 1 ).calledWithExactly( 'apiURL'
), 'When even the retry fails, ImageProvider is called second with the guessed
url' );
+ assert.ok( imageStub.getCall( 0 ).calledWithExactly(
'guessedURL', undefined ), 'When even the retry fails, ImageProvider is called
first with the guessed url' );
+ assert.ok( imageStub.getCall( 1 ).calledWithExactly( 'apiURL',
undefined ), 'When even the retry fails, ImageProvider is called second with
the guessed url' );
assert.strictEqual( promise.state(), 'rejected', 'When even the
retry fails, fetchThumbnail rejects' );
mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing =
false;
@@ -600,7 +623,7 @@
assert.ok( !guessedThumbnailInfoStub.called, 'When guessing is
disabled, GuessedThumbnailInfoProvider is not called' );
assert.ok( thumbnailInfoStub.calledOnce, 'When guessing is
disabled, ThumbnailInfoProvider is called once' );
assert.ok( imageStub.calledOnce, 'When guessing is disabled,
ImageProvider is called once' );
- assert.ok( imageStub.calledWithExactly( 'apiURL' ), 'When
guessing is disabled, ImageProvider is called with the API url' );
+ assert.ok( imageStub.calledWithExactly( 'apiURL', undefined ),
'When guessing is disabled, ImageProvider is called with the API url' );
assert.strictEqual( promise.state(), 'resolved', 'When guessing
is disabled, fetchThumbnail resolves' );
mw.config.get( 'wgMultimediaViewer' ).useThumbnailGuessing =
oldUseThumbnailGuessing;
diff --git a/tests/qunit/mmv/model/mmv.model.Image.test.js
b/tests/qunit/mmv/model/mmv.model.Image.test.js
index 33fda90..7686ffd 100644
--- a/tests/qunit/mmv/model/mmv.model.Image.test.js
+++ b/tests/qunit/mmv/model/mmv.model.Image.test.js
@@ -18,7 +18,7 @@
( function( mw ) {
QUnit.module( 'mmv.model.Image', QUnit.newMwEnvironment() );
- QUnit.test( 'Image model constructor sanity check', 21, function (
assert ) {
+ QUnit.test( 'Image model constructor sanity check', 22, function (
assert ) {
var
title = mw.Title.newFromText( 'File:Foobar.jpg' ),
name = 'Foo bar',
@@ -31,6 +31,7 @@
repo = 'wikimediacommons',
user = 'Kaldari',
datetime = '2011-07-04T23:31:14Z',
+ anondatetime = '20110704000000',
origdatetime = '2010-07-04T23:31:14Z',
description = 'This is a test file.',
source = 'WMF',
@@ -42,7 +43,7 @@
longitude = 100.983829,
imageData = new mw.mmv.model.Image(
title, name, size, width, height, mime, url,
- descurl, repo, user, datetime, origdatetime,
+ descurl, repo, user, datetime, anondatetime,
origdatetime,
description, source, author, authorCount,
license, permission,
latitude, longitude );
@@ -57,6 +58,7 @@
assert.strictEqual( imageData.repo, repo, 'Repository name is
set correctly' );
assert.strictEqual( imageData.lastUploader, user, 'Name of last
uploader is set correctly' );
assert.strictEqual( imageData.uploadDateTime, datetime, 'Date
and time of last upload is set correctly' );
+ assert.strictEqual( imageData.anonymizedUploadDateTime,
anondatetime, 'Anonymized date and time of last upload is set correctly' );
assert.strictEqual( imageData.creationDateTime, origdatetime,
'Date and time of original upload is set correctly' );
assert.strictEqual( imageData.description, description,
'Description is set correctly' );
assert.strictEqual( imageData.source, source, 'Source is set
correctly' );
@@ -74,13 +76,13 @@
firstImageData = new mw.mmv.model.Image(
mw.Title.newFromText( 'File:Foobar.pdf.jpg' ),
'Foo bar',
10, 10, 10, 'image/jpeg', 'http://example.org',
'http://example.com',
- 'example', 'tester', '2013-11-10',
'2013-11-09', 'Blah blah blah',
+ 'example', 'tester', '2013-11-10', '20131110',
'2013-11-09', 'Blah blah blah',
'A person', 'Another person', 1,
'CC-BY-SA-3.0', 'Permitted'
),
secondImageData = new mw.mmv.model.Image(
mw.Title.newFromText( 'File:Foobar.pdf.jpg' ),
'Foo bar',
10, 10, 10, 'image/jpeg', 'http://example.org',
'http://example.com',
- 'example', 'tester', '2013-11-10',
'2013-11-09', 'Blah blah blah',
+ 'example', 'tester', '2013-11-10', '20131110',
'2013-11-09', 'Blah blah blah',
'A person', 'Another person', 1,
'CC-BY-SA-3.0', 'Permitted',
'39.91820938', '78.09812938'
);
diff --git a/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js
b/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js
index e9062b1..6dd3d74 100644
--- a/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js
+++ b/tests/qunit/mmv/provider/mmv.provider.ImageInfo.test.js
@@ -25,7 +25,7 @@
assert.ok( imageInfoProvider );
} );
- QUnit.asyncTest( 'ImageInfo get test', 26, function ( assert ) {
+ QUnit.asyncTest( 'ImageInfo get test', 27, function ( assert ) {
var apiCallCount = 0,
api = { get: function() {
apiCallCount++;
@@ -142,6 +142,7 @@
assert.strictEqual( image.repo, 'shared', 'repo is set
correctly' );
assert.strictEqual( image.lastUploader, 'Dylanbot11',
'lastUploader is set correctly' );
assert.strictEqual( image.uploadDateTime,
'2013-08-25T14:41:02Z', 'uploadDateTime is set correctly' );
+ assert.strictEqual( image.anonymizedUploadDateTime,
'20130825000000', 'anonymizedUploadDateTime is set correctly' );
assert.strictEqual( image.creationDateTime, '18
February 2009\u00a0(according to EXIF data)', 'creationDateTime is set
correctly' );
assert.strictEqual( image.description, 'Wikis stuff',
'description is set correctly' );
assert.strictEqual( image.source, 'Wikipedia', 'source
is set correctly' );
--
To view, visit https://gerrit.wikimedia.org/r/179918
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I673f9487deea15dc148452a3a4d6b91563a2c417
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/MultimediaViewer
Gerrit-Branch: wmf/1.25wmf11
Gerrit-Owner: Gilles <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits