jenkins-bot has submitted this change and it was merged. Change subject: Add imageinfo, thumbnail info, repoinfo provider ......................................................................
Add imageinfo, thumbnail info, repoinfo provider Change-Id: I80ffec39ee6c9e0ea0b37be2fc48315063b5ff8a Mingle: https://wikimedia.mingle.thoughtworks.com/projects/multimedia/cards/123 --- M MultimediaViewer.php M MultimediaViewerHooks.php M resources/mmv/model/mmv.model.Image.js M resources/mmv/provider/mmv.provider.Api.js A resources/mmv/provider/mmv.provider.FileRepoInfo.js M resources/mmv/provider/mmv.provider.GlobalUsage.js A resources/mmv/provider/mmv.provider.ImageInfo.js M resources/mmv/provider/mmv.provider.ImageUsage.js A resources/mmv/provider/mmv.provider.ThumbnailInfo.js A tests/qunit/provider/mmv.provider.Api.test.js A tests/qunit/provider/mmv.provider.FileRepoInfo.test.js A tests/qunit/provider/mmv.provider.ImageInfo.test.js A tests/qunit/provider/mmv.provider.ThumbnailInfo.test.js 13 files changed, 963 insertions(+), 42 deletions(-) Approvals: Aarcos: Looks good to me, approved jenkins-bot: Verified diff --git a/MultimediaViewer.php b/MultimediaViewer.php index ba55b3d..131b5a1 100644 --- a/MultimediaViewer.php +++ b/MultimediaViewer.php @@ -147,6 +147,9 @@ 'mmv.provider.Api.js', 'mmv.provider.ImageUsage.js', 'mmv.provider.GlobalUsage.js', + 'mmv.provider.ImageInfo.js', + 'mmv.provider.FileRepoInfo.js', + 'mmv.provider.ThumbnailInfo.js', ), 'dependencies' => array( diff --git a/MultimediaViewerHooks.php b/MultimediaViewerHooks.php index b66e33e..5c4cd9e 100644 --- a/MultimediaViewerHooks.php +++ b/MultimediaViewerHooks.php @@ -122,8 +122,12 @@ 'tests/qunit/mmv.testhelpers.js', 'tests/qunit/mmv.test.js', 'tests/qunit/mmv.model.test.js', + 'tests/qunit/provider/mmv.provider.Api.test.js', 'tests/qunit/provider/mmv.provider.ImageUsage.test.js', 'tests/qunit/provider/mmv.provider.GlobalUsage.test.js', + 'tests/qunit/provider/mmv.provider.ImageInfo.test.js', + 'tests/qunit/provider/mmv.provider.FileRepoInfo.test.js', + 'tests/qunit/provider/mmv.provider.ThumbnailInfo.test.js', 'tests/qunit/mmv.lightboxinterface.test.js', 'tests/qunit/mmv.ui.description.test.js', 'tests/qunit/mmv.ui.fileUsage.test.js', diff --git a/resources/mmv/model/mmv.model.Image.js b/resources/mmv/model/mmv.model.Image.js index 88a6d6b..65e6e09 100644 --- a/resources/mmv/model/mmv.model.Image.js +++ b/resources/mmv/model/mmv.model.Image.js @@ -29,8 +29,8 @@ * @param {string} descriptionUrl URL to the image description page * @param {string} repo The repository this image belongs to * @param {string} lastUploader The last person to upload a version of this image. - * @param {string} lastUploadDateTime The time and date the last upload occurred - * @param {string} originalUploadDateTime The time and date the original upload occurred + * @param {string} uploadDateTime The time and date the last upload occurred + * @param {string} creationDateTime The time and date the original upload occurred * @param {string} description * @param {string} source * @param {string} author diff --git a/resources/mmv/provider/mmv.provider.Api.js b/resources/mmv/provider/mmv.provider.Api.js index 9f0247e..cd091e7 100644 --- a/resources/mmv/provider/mmv.provider.Api.js +++ b/resources/mmv/provider/mmv.provider.Api.js @@ -15,8 +15,7 @@ * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. */ -( function ( mw ) { - +( function ( mw, $ ) { /** * @class mw.mmv.provider.Api * Base class for API-based data providers. @@ -62,6 +61,82 @@ return errorMessage; }; + /** + * @method + * Returns a fixed a title based on the API response. + * The title of the returned file might be different from the requested title, e.g. + * if we used a namespace alias. If that happens the old and new title will be set in + * data.query.normalized; this method creates an updated title based on that. + * @param {mw.Title} title + * @param {Object} data + * @return {mw.Title} + */ + Api.prototype.getNormalizedTitle = function( title, data ) { + if ( data && data.query && data.query.normalized ) { + for ( var normalized = data.query.normalized, length = normalized.length, i = 0; i < length; i++ ) { + if ( normalized[i].from === title.getPrefixedText() ) { + title = new mw.Title( normalized[i].to ); + break; + } + } + } + return title; + }; + + /** + * @method + * Returns a promise with the specified field from the API result. + * This is intended to be used as a .then() callback for action=query APIs. + * @param {string} field + * @param {Object} data + * @return {jQuery.Promise} when successful, the first argument will be the field data, + * when unsuccessful, it will be an error message. The second argument is always + * the full API response. + */ + Api.prototype.getQueryField = function( field, data ) { + if ( data && data.query && data.query[field] ) { + return $.Deferred().resolve( data.query[field], data ); + } else { + return $.Deferred().reject( this.getErrorMessage( data ), data ); + } + }; + + /** + * @method + * Returns a promise with the specified page from the API result. + * This is intended to be used as a .then() callback for action=query&prop=(...) APIs. + * @param {mw.Title} title + * @param {Object} data + * @return {jQuery.Promise} when successful, the first argument will be the page data, + * when unsuccessful, it will be an error message. The second argument is always + * the full API response. + */ + Api.prototype.getQueryPage = function( title, data ) { + var pageName, pageData = null; + if ( data && data.query && data.query.pages ) { + title = this.getNormalizedTitle( title, data ); + pageName = title.getPrefixedText(); + + // pages is an associative array indexed by pageid, + // we need to iterate through to find the right page + $.each( data.query.pages, function ( id, page ) { + if ( page.title === pageName ) { + pageData = page; + return false; + } + } ); + + if ( pageData ) { + return $.Deferred().resolve( pageData, data ); + } + } + + // If we got to this point either the pages array is missing completely, or we iterated + // through it and the requested page was not found. Neither is supposed to happen + // (if the page simply did not exist, there would still be a record for it). + return $.Deferred().reject( this.getErrorMessage( data ), data ); + }; + mw.mmv.provider = {}; mw.mmv.provider.Api = Api; -}( mediaWiki ) ); +}( mediaWiki, jQuery ) ); diff --git a/resources/mmv/provider/mmv.provider.FileRepoInfo.js b/resources/mmv/provider/mmv.provider.FileRepoInfo.js new file mode 100644 index 0000000..799750f --- /dev/null +++ b/resources/mmv/provider/mmv.provider.FileRepoInfo.js @@ -0,0 +1,60 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, oo, $ ) { + + /** + * @class mw.mmv.provider.FileRepoInfo + * Gets file repo information. + * @extends mw.mmv.provider.Api + * @inheritdoc + * @param {mw.Api} api + */ + function FileRepoInfo( api ) { + mw.mmv.provider.Api.call( this, api ); + } + oo.inheritClass( FileRepoInfo, mw.mmv.provider.Api ); + + /** + * @method + * Runs an API GET request to get the repo info. + * @return {jQuery.Promise} a promise which resolves to an array of mw.mmv.model.Repo objects. + */ + FileRepoInfo.prototype.get = function() { + var provider = this; + + if ( !this.cache['*'] ) { + this.cache['*'] = this.api.get( { + action: 'query', + meta: 'filerepoinfo', + format: 'json' + } ).then( function( data ) { + return provider.getQueryField( 'repos', data ); + } ).then( function( reposArray ) { + var reposHash = {}; + $.each( reposArray, function ( i, repo ) { + reposHash[repo.name] = mw.mmv.model.Repo.newFromRepoInfo( repo ); + } ); + return reposHash; + } ); + } + + return this.cache['*']; + }; + + mw.mmv.provider.FileRepoInfo = FileRepoInfo; +}( mediaWiki, OO, jQuery ) ); diff --git a/resources/mmv/provider/mmv.provider.GlobalUsage.js b/resources/mmv/provider/mmv.provider.GlobalUsage.js index 9a1f4b3..b7b852c 100644 --- a/resources/mmv/provider/mmv.provider.GlobalUsage.js +++ b/resources/mmv/provider/mmv.provider.GlobalUsage.js @@ -74,27 +74,22 @@ gulimit: this.options.apiLimit, format: 'json' } ).then( function( data ) { + return provider.getQueryPage( file, data ); + } ).then( function( pageData, data ) { var pages; - if ( data && data.query && data.query.pages ) { - // pages is an associative array indexed by pageid, turn it into proper array - pages = $.map( data.query.pages, function ( v ) { return v; } ); - // the API returns a result for non-existent files as well so pages[0] will always exist - pages = $.map( pages[0].globalusage || {}, function( item ) { - return { - wiki: item.wiki, - page: new mw.Title( item.title, item.ns ) - }; - } ); - return new mw.mmv.model.FileUsage( - file, - mw.mmv.model.FileUsage.Scope.GLOBAL, - pages.slice( 0, provider.options.dataLimit ), - pages.length, - !!( data['query-continue'] && data['query-continue'].globalusage ) - ); - } else { - return $.Deferred().reject( provider.getErrorMessage( data ) ); - } + pages = $.map( pageData.globalusage || {}, function( item ) { + return { + wiki: item.wiki, + page: new mw.Title( item.title, item.ns ) + }; + } ); + return new mw.mmv.model.FileUsage( + file, + mw.mmv.model.FileUsage.Scope.GLOBAL, + pages.slice( 0, provider.options.dataLimit ), + pages.length, + !!( data['query-continue'] && data['query-continue'].globalusage ) + ); } ); } diff --git a/resources/mmv/provider/mmv.provider.ImageInfo.js b/resources/mmv/provider/mmv.provider.ImageInfo.js new file mode 100644 index 0000000..b2c8c49 --- /dev/null +++ b/resources/mmv/provider/mmv.provider.ImageInfo.js @@ -0,0 +1,97 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, oo, $ ) { + + /** + * @class mw.mmv.provider.ImageInfo + * Gets file information. + * See https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii + * @extends mw.mmv.provider.Api + * @inheritdoc + * @param {mw.Api} api + */ + function ImageInfo( api ) { + mw.mmv.provider.Api.call( this, api ); + } + oo.inheritClass( ImageInfo, mw.mmv.provider.Api ); + + /** + * List of imageinfo API properties which are needed to construct an Image model. + * @type {string[]} + */ + ImageInfo.prototype.iiprop = [ + 'timestamp', + 'user', + 'url', + 'size', + 'mime', + 'mediatype', + 'extmetadata' + ]; + + /** + * List of imageinfo extmetadata fields which are needed to construct an Image model. + * @type {string[]} + */ + ImageInfo.prototype.iiextmetadatafilter = [ + 'DateTime', + 'DateTimeOriginal', + 'ImageDescription', + 'License', + 'Credit', + 'Artist', + 'GPSLatitude', + 'GPSLongitude' + ]; + + /** + * @method + * Runs an API GET request to get the image info. + * @param {mw.Title} file + * @return {jQuery.Promise} a promise which resolves to an mw.mmv.model.Image object. + */ + ImageInfo.prototype.get = function( file ) { + var provider = this, + cacheKey = file.getPrefixedDb(); + + if ( !this.cache[cacheKey] ) { + this.cache[cacheKey] = this.api.get( { + action: 'query', + prop: 'imageinfo', + titles: file.getPrefixedDb(), + iiprop: this.iiprop, + iiextmetadatafilter: this.iiextmetadatafilter, + format: 'json' + } ).then( function( data ) { + return provider.getQueryPage( file, data ); + } ).then( function( page ) { + if ( page.imageinfo && page.imageinfo.length ) { + return mw.mmv.model.Image.newFromImageInfo( file, page ); + } else if ( page.missing === '' && page.imagerepository === '' ) { + return $.Deferred().reject( 'file does not exist: ' + file.getPrefixedDb() ); + } else { + return $.Deferred().reject( 'unknown error' ); + } + } ); + } + + return this.cache[cacheKey]; + }; + + mw.mmv.provider.ImageInfo = ImageInfo; +}( mediaWiki, OO, jQuery ) ); diff --git a/resources/mmv/provider/mmv.provider.ImageUsage.js b/resources/mmv/provider/mmv.provider.ImageUsage.js index 69a4711..114245a 100644 --- a/resources/mmv/provider/mmv.provider.ImageUsage.js +++ b/resources/mmv/provider/mmv.provider.ImageUsage.js @@ -60,24 +60,22 @@ iulimit: this.options.apiLimit, format: 'json' } ).then( function( data ) { + return provider.getQueryField( 'imageusage', data ); + } ).then( function( imageusage, data ) { var pages; - if ( data && data.query && data.query.imageusage ) { - pages = $.map( data.query.imageusage, function( item ) { - return { - wiki: null, - page: new mw.Title( item.title, item.ns ) - }; - } ); - return new mw.mmv.model.FileUsage( - file, - mw.mmv.model.FileUsage.Scope.LOCAL, - pages.slice( 0, provider.options.dataLimit ), - pages.length, - !!( data['query-continue'] && data['query-continue'].imageusage ) - ); - } else { - return $.Deferred().reject( provider.getErrorMessage( data ) ); - } + pages = $.map( imageusage, function( item ) { + return { + wiki: null, + page: new mw.Title( item.title, item.ns ) + }; + } ); + return new mw.mmv.model.FileUsage( + file, + mw.mmv.model.FileUsage.Scope.LOCAL, + pages.slice( 0, provider.options.dataLimit ), + pages.length, + !!( data['query-continue'] && data['query-continue'].imageusage ) + ); } ); } diff --git a/resources/mmv/provider/mmv.provider.ThumbnailInfo.js b/resources/mmv/provider/mmv.provider.ThumbnailInfo.js new file mode 100644 index 0000000..f5c044d --- /dev/null +++ b/resources/mmv/provider/mmv.provider.ThumbnailInfo.js @@ -0,0 +1,69 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, oo, $ ) { + + /** + * @class mw.mmv.provider.ThumbnailInfo + * Gets thumbnail information. + * See https://www.mediawiki.org/wiki/API:Properties#imageinfo_.2F_ii + * @extends mw.mmv.provider.Api + * @inheritdoc + * @param {mw.Api} api + */ + function ThumbnailInfo( api ) { + mw.mmv.provider.Api.call( this, api ); + } + oo.inheritClass( ThumbnailInfo, mw.mmv.provider.Api ); + + /** + * @method + * Runs an API GET request to get the thumbnail info. + * @param {mw.Title} file + * @param {number} width thumbnail width + * @return {jQuery.Promise} a promise which resolves to the thumbnail URL + */ + ThumbnailInfo.prototype.get = function( file, width ) { + var provider = this, + cacheKey = file.getPrefixedDb() + '|' + width; + + if ( !this.cache[cacheKey] ) { + this.cache[cacheKey] = this.api.get( { + action: 'query', + prop: 'imageinfo', + titles: file.getPrefixedDb(), + iiprop: ['url'], + iiurlwidth: width, + format: 'json' + } ).then( function( data ) { + return provider.getQueryPage( file, data ); + } ).then( function( page ) { + if ( page.imageinfo && page.imageinfo[0] ) { + return page.imageinfo[0].thumburl; + } else if ( page.missing === '' && page.imagerepository === '' ) { + return $.Deferred().reject( 'file does not exist: ' + file.getPrefixedDb() ); + } else { + return $.Deferred().reject( 'unknown error' ); + } + } ); + } + + return this.cache[cacheKey]; + }; + + mw.mmv.provider.ThumbnailInfo = ThumbnailInfo; +}( mediaWiki, OO, jQuery ) ); diff --git a/tests/qunit/provider/mmv.provider.Api.test.js b/tests/qunit/provider/mmv.provider.Api.test.js new file mode 100644 index 0000000..1602e8a --- /dev/null +++ b/tests/qunit/provider/mmv.provider.Api.test.js @@ -0,0 +1,177 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw ) { + QUnit.module( 'mmv.provider.Api', QUnit.newMwEnvironment() ); + + QUnit.test( 'Api constructor sanity check', 2, function ( assert ) { + var api = { get: function() {} }, + options = {}, + apiProvider = new mw.mmv.provider.Api( api, options ), + ApiProviderWithNoOptions = new mw.mmv.provider.Api( api ); + + assert.ok( apiProvider ); + assert.ok( ApiProviderWithNoOptions ); + } ); + + QUnit.test( 'getErrorMessage test', 2, function ( assert ) { + var api = { get: function() {} }, + apiProvider = new mw.mmv.provider.Api( api ), + errorMessage; + + errorMessage = apiProvider.getErrorMessage( { + servedby: 'mw1194', + error: { + code: 'unknown_action', + info: 'Unrecognized value for parameter \'action\': FOO' + } + } ); + assert.strictEqual( errorMessage, + 'unknown_action: Unrecognized value for parameter \'action\': FOO', + 'error message is parsed correctly'); + + assert.strictEqual( apiProvider.getErrorMessage( {} ), 'unknown error', 'missing error message is handled'); + } ); + + QUnit.test( 'getNormalizedTitle test', 3, function ( assert ) { + var api = { get: function() {} }, + apiProvider = new mw.mmv.provider.Api( api ), + title = new mw.Title( 'Image:Stuff.jpg' ), + normalizedTitle; + + normalizedTitle = apiProvider.getNormalizedTitle( title, {} ); + assert.strictEqual( normalizedTitle, title, 'missing normalization block is handled' ); + + normalizedTitle = apiProvider.getNormalizedTitle( title, { + query: { + normalized: [ + { + from: 'Image:Foo.jpg', + to: 'File:Foo.jpg' + } + ] + } + } ); + assert.strictEqual( normalizedTitle, title, 'irrelevant normalization info is skipped' ); + + normalizedTitle = apiProvider.getNormalizedTitle( title, { + query: { + normalized: [ + { + from: 'Image:Stuff.jpg', + to: 'File:Stuff.jpg' + } + ] + } + } ); + assert.strictEqual( normalizedTitle.getPrefixedDb(), 'File:Stuff.jpg', 'normalization happens' ); + } ); + + QUnit.test( 'getQueryField test', 3, function ( assert ) { + var api = { get: function() {} }, + apiProvider = new mw.mmv.provider.Api( api ), + data; + + data = { + query: { + imageusage: [ + { + pageid: 736, + ns: 0, + title: 'Albert Einstein' + } + ] + } + }; + QUnit.stop(); + apiProvider.getQueryField( 'imageusage', data ).then( function ( field ) { + assert.strictEqual( field, data.query.imageusage, 'specified field is found'); + QUnit.start(); + } ); + + QUnit.stop(); + apiProvider.getQueryField( 'imageusage', {} ).fail( function () { + assert.ok( true, 'promise rejected when data is missing'); + QUnit.start(); + } ); + + QUnit.stop(); + apiProvider.getQueryField( 'imageusage', { data: { query: {} } } ).fail( function () { + assert.ok( true, 'promise rejected when field is missing'); + QUnit.start(); + } ); + } ); + + QUnit.test( 'getQueryPage test', 6, function ( assert ) { + var api = { get: function() {} }, + apiProvider = new mw.mmv.provider.Api( api ), + title = new mw.Title( 'File:Stuff.jpg' ), + titleWithNamespaceAlias = new mw.Title( 'Image:Stuff.jpg' ), + otherTitle = new mw.Title( 'File:Foo.jpg' ), + data; + + data = { + normalized: [ + { + from: 'Image:Stuff.jpg', + to: 'File:Stuff.jpg' + } + ], + query: { + pages: { + '-1': { + title: 'File:Stuff.jpg' + } + } + } + }; + QUnit.stop(); + apiProvider.getQueryPage( title, data ).then( function ( field ) { + assert.strictEqual( field, data.query.pages['-1'], 'specified page is found'); + QUnit.start(); + } ); + QUnit.stop(); + apiProvider.getQueryPage( titleWithNamespaceAlias, data ).then( function ( field ) { + assert.strictEqual( field, data.query.pages['-1'], + 'specified page is found even if its title was normalized'); + QUnit.start(); + } ); + QUnit.stop(); + apiProvider.getQueryPage( otherTitle, {} ).fail( function () { + assert.ok( true, 'promise rejected when page has different title'); + QUnit.start(); + } ); + + QUnit.stop(); + apiProvider.getQueryPage( title, {} ).fail( function () { + assert.ok( true, 'promise rejected when data is missing'); + QUnit.start(); + } ); + + QUnit.stop(); + apiProvider.getQueryPage( title, { data: { query: {} } } ).fail( function () { + assert.ok( true, 'promise rejected when pages are missing'); + QUnit.start(); + } ); + + QUnit.stop(); + apiProvider.getQueryPage( title, { data: { query: { pages: {} } } } ).fail( function () { + assert.ok( true, 'promise rejected when pages are empty'); + QUnit.start(); + } ); + } ); +}( mediaWiki ) ); diff --git a/tests/qunit/provider/mmv.provider.FileRepoInfo.test.js b/tests/qunit/provider/mmv.provider.FileRepoInfo.test.js new file mode 100644 index 0000000..c42308c --- /dev/null +++ b/tests/qunit/provider/mmv.provider.FileRepoInfo.test.js @@ -0,0 +1,126 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, $ ) { + QUnit.module( 'mmv.provider.FileRepoInfo', QUnit.newMwEnvironment() ); + + QUnit.test( 'FileRepoInfo constructor sanity check', 1, function ( assert ) { + var api = { get: function() {} }, + fileRepoInfoProvider = new mw.mmv.provider.FileRepoInfo( api ); + + assert.ok( fileRepoInfoProvider ); + } ); + + QUnit.asyncTest( 'FileRepoInfo get test', 14, function ( assert ) { + var apiCallCount = 0, + api = { get: function() { + apiCallCount++; + return $.Deferred().resolve( { + 'query': { + 'repos': [ + { + 'name': 'shared', + 'displayname': 'Wikimedia Commons', + 'rootUrl': '//upload.beta.wmflabs.org/wikipedia/commons', + 'local': false, + 'url': '//upload.beta.wmflabs.org/wikipedia/commons', + 'thumbUrl': '//upload.beta.wmflabs.org/wikipedia/commons/thumb', + 'initialCapital': true, + 'descBaseUrl': '//commons.wikimedia.beta.wmflabs.org/wiki/File:', + 'scriptDirUrl': '//commons.wikimedia.beta.wmflabs.org/w', + 'fetchDescription': true, + 'favicon': 'http://bits.wikimedia.org/favicon/wikipedia.ico' + }, + { + 'name': 'wikimediacommons', + 'displayname': 'Wikimedia Commons', + 'rootUrl': '//upload.beta.wmflabs.org/wikipedia/en', + 'local': false, + 'url': '//upload.beta.wmflabs.org/wikipedia/en', + 'thumbUrl': '//upload.beta.wmflabs.org/wikipedia/en/thumb', + 'initialCapital': true, + 'scriptDirUrl': 'http://commons.wikimedia.org/w', + 'fetchDescription': true, + 'descriptionCacheExpiry': 43200, + 'apiurl': 'http://commons.wikimedia.org/w/api.php', + 'articlepath': '/wiki/$1', + 'server': '//commons.wikimedia.org', + 'favicon': '//bits.wikimedia.org/favicon/commons.ico' + }, + { + 'name': 'local', + 'displayname': null, + 'rootUrl': '//upload.beta.wmflabs.org/wikipedia/en', + 'local': true, + 'url': '//upload.beta.wmflabs.org/wikipedia/en', + 'thumbUrl': '//upload.beta.wmflabs.org/wikipedia/en/thumb', + 'initialCapital': true, + 'scriptDirUrl': '/w', + 'favicon': 'http://bits.wikimedia.org/favicon/wikipedia.ico' + } + ] + } + } ); + } }, + fileRepoInfoProvider = new mw.mmv.provider.FileRepoInfo( api ); + + fileRepoInfoProvider.get().then( function( repos ) { + assert.strictEqual( repos.shared.displayName, + 'Wikimedia Commons', 'displayName is set correctly' ); + assert.strictEqual( repos.shared.favIcon, + 'http://bits.wikimedia.org/favicon/wikipedia.ico', 'favIcon is set correctly' ); + assert.strictEqual( repos.shared.isLocal, false, 'isLocal is set correctly' ); + assert.strictEqual( repos.shared.descBaseUrl, + '//commons.wikimedia.beta.wmflabs.org/wiki/File:', 'descBaseUrl is set correctly' ); + + assert.strictEqual( repos.wikimediacommons.displayName, + 'Wikimedia Commons', 'displayName is set correctly' ); + assert.strictEqual( repos.wikimediacommons.favIcon, + '//bits.wikimedia.org/favicon/commons.ico', 'favIcon is set correctly' ); + assert.strictEqual( repos.wikimediacommons.isLocal, false, 'isLocal is set correctly' ); + assert.strictEqual( repos.wikimediacommons.apiUrl, + 'http://commons.wikimedia.org/w/api.php', 'apiUrl is set correctly' ); + assert.strictEqual( repos.wikimediacommons.server, + '//commons.wikimedia.org', 'server is set correctly' ); + assert.strictEqual( repos.wikimediacommons.articlePath, + '/wiki/$1', 'articlePath is set correctly' ); + + assert.strictEqual( repos.local.displayName, null, 'displayName is set correctly' ); + assert.strictEqual( repos.local.favIcon, + 'http://bits.wikimedia.org/favicon/wikipedia.ico', 'favIcon is set correctly' ); + assert.strictEqual( repos.local.isLocal, true, 'isLocal is set correctly' ); + } ).then( function() { + // call the data provider a second time to check caching + return fileRepoInfoProvider.get(); + } ).then( function() { + assert.strictEqual( apiCallCount, 1 ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'FileRepoInfo fail test', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( {} ); + } }, + fileRepoInfoProvider = new mw.mmv.provider.FileRepoInfo( api ); + + fileRepoInfoProvider.get().fail( function() { + assert.ok( true, 'promise rejected when no data is returned' ); + QUnit.start(); + } ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/provider/mmv.provider.ImageInfo.test.js b/tests/qunit/provider/mmv.provider.ImageInfo.test.js new file mode 100644 index 0000000..71d53ea --- /dev/null +++ b/tests/qunit/provider/mmv.provider.ImageInfo.test.js @@ -0,0 +1,185 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, $ ) { + QUnit.module( 'mmv.provider.ImageInfo', QUnit.newMwEnvironment() ); + + QUnit.test( 'ImageInfo constructor sanity check', 1, function ( assert ) { + var api = { get: function() {} }, + imageInfoProvider = new mw.mmv.provider.ImageInfo( api ); + + assert.ok( imageInfoProvider ); + } ); + + QUnit.asyncTest( 'ImageInfo get test', 18, function ( assert ) { + var apiCallCount = 0, + api = { get: function() { + apiCallCount++; + return $.Deferred().resolve( { + query: { + pages: { + '-1': { + ns: 6, + title: 'File:Stuff.jpg', + missing: '', + imagerepository: 'shared', + imageinfo: [ + { + timestamp: '2013-08-25T14:41:02Z', + user: 'Dylanbot11', + userid: '3053121', + size: 346684, + width: 720, + height: 1412, + comment: 'User created page with UploadWizard', + url: 'https://upload.wikimedia.org/wikipedia/commons/1/19/Stuff.jpg', + descriptionurl: 'https://commons.wikimedia.org/wiki/File:Stuff.jpg', + sha1: 'a1ba23d471f4dad208b71c143e2e105a0e3032db', + metadata: [], + extmetadata: { + License: { + value: 'cc0', + source: 'commons-templates', + hidden: '' + }, + GPSLatitude: { + value: '90.000000', + source: 'commons-desc-page' + }, + GPSLongitude: { + value: ' 180.000000', + source: 'commons-desc-page' + }, + ImageDescription: { + value: 'Wikis stuff', + source: 'commons-desc-page' + }, + DateTimeOriginal: { + value: '2013-08-25, 15:38:38', + source: 'commons-desc-page' + }, + DateTime: { + value: '2013-08-25T14:41:02Z', + source: 'commons-desc-page' + }, + Credit: { + value: 'Wikipedia', + source: 'commons-desc-page', + hidden: '' + }, + Artist: { + value: 'Wikimeda', + source: 'commons-desc-page' + } + }, + mime: 'image/jpeg', + mediatype: 'BITMAP' + } + ] + } + } + } + } ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + imageInfoProvider = new mw.mmv.provider.ImageInfo( api ); + + imageInfoProvider.get( file ).then( function( image ) { + assert.strictEqual( image.title.getPrefixedDb(), 'File:Stuff.jpg', 'title is set correctly' ); + assert.strictEqual( image.size, 346684, 'size is set correctly' ); + assert.strictEqual( image.width, 720, 'width is set correctly' ); + assert.strictEqual( image.height, 1412, 'height is set correctly' ); + assert.strictEqual( image.mimeType, 'image/jpeg', 'mimeType is set correctly' ); + assert.strictEqual( image.url, 'https://upload.wikimedia.org/wikipedia/commons/1/19/Stuff.jpg', 'url is set correctly' ); + assert.strictEqual( image.descriptionUrl, 'https://commons.wikimedia.org/wiki/File:Stuff.jpg', 'descriptionUrl is set correctly' ); + 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.creationDateTime, '2013-08-25, 15:38:38', 'creationDateTime is set correctly' ); + assert.strictEqual( image.description, 'Wikis stuff', 'description is set correctly' ); + assert.strictEqual( image.source, 'Wikipedia', 'source is set correctly' ); + assert.strictEqual( image.author, 'Wikimeda', 'author is set correctly' ); + assert.strictEqual( image.license, 'cc0', 'license is set correctly' ); + assert.strictEqual( image.latitude, 90, 'latitude is set correctly' ); + assert.strictEqual( image.longitude, 180, 'longitude is set correctly' ); + } ).then( function() { + // call the data provider a second time to check caching + return imageInfoProvider.get( file ); + } ).then( function() { + assert.strictEqual( apiCallCount, 1 ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'ImageInfo fail test', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( {} ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + imageInfoProvider = new mw.mmv.provider.ImageInfo( api ); + + imageInfoProvider.get( file ).fail( function() { + assert.ok( true, 'promise rejected when no data is returned' ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'ImageInfo fail test 2', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( { + query: { + pages: { + '-1': { + title: 'File:Stuff.jpg' + } + } + } + } ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + imageInfoProvider = new mw.mmv.provider.ImageInfo( api ); + + imageInfoProvider.get( file ).fail( function() { + assert.ok( true, 'promise rejected when imageinfo is missing' ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'ImageInfo missing page test', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( { + query: { + pages: { + '-1': { + title: 'File:Stuff.jpg', + missing: '', + imagerepository: '' + } + } + } + } ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + imageInfoProvider = new mw.mmv.provider.ImageInfo( api ); + + imageInfoProvider.get( file ).fail( function( errorMessage ) { + assert.strictEqual(errorMessage, 'file does not exist: File:Stuff.jpg', + 'error message is set correctly for missing file'); + QUnit.start(); + } ); + } ); +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/provider/mmv.provider.ThumbnailInfo.test.js b/tests/qunit/provider/mmv.provider.ThumbnailInfo.test.js new file mode 100644 index 0000000..f3d9168 --- /dev/null +++ b/tests/qunit/provider/mmv.provider.ThumbnailInfo.test.js @@ -0,0 +1,132 @@ +/* + * This file is part of the MediaWiki extension MultimediaViewer. + * + * MultimediaViewer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * MultimediaViewer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with MultimediaViewer. If not, see <http://www.gnu.org/licenses/>. + */ + +( function ( mw, $ ) { + QUnit.module( 'mmv.provider.ThumbnailInfo', QUnit.newMwEnvironment() ); + + QUnit.test( 'ThumbnailInfo constructor sanity check', 1, function ( assert ) { + var api = { get: function() {} }, + thumbnailInfoProvider = new mw.mmv.provider.ThumbnailInfo( api ); + + assert.ok( thumbnailInfoProvider ); + } ); + + QUnit.asyncTest( 'ThumbnailInfo get test', 4, function ( assert ) { + var apiCallCount = 0, + api = { get: function() { + apiCallCount++; + return $.Deferred().resolve( { + query: { + pages: { + '-1': { + ns: 6, + title: 'File:Stuff.jpg', + missing: '', + imagerepository: 'shared', + imageinfo: [ + { + thumburl: 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Stuff.jpg/51px-Stuff.jpg', + thumbwidth: 100, + thumbheight: 200, + url: 'https://upload.wikimedia.org/wikipedia/commons/1/19/Stuff.jpg', + descriptionurl: 'https://commons.wikimedia.org/wiki/File:Stuff.jpg' + } + ] + } + } + } + } ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + thumbnailInfoProvider = new mw.mmv.provider.ThumbnailInfo( api ); + + thumbnailInfoProvider.get( file, 100 ).then( function( thumnailUrl ) { + assert.strictEqual( thumnailUrl, + 'https://upload.wikimedia.org/wikipedia/commons/thumb/1/19/Stuff.jpg/51px-Stuff.jpg', + 'URL is set correctly' ); + } ).then( function() { + assert.strictEqual( apiCallCount, 1 ); + // call the data provider a second time to check caching + return thumbnailInfoProvider.get( file, 100 ); + } ).then( function() { + assert.strictEqual( apiCallCount, 1 ); + // call a third time with different size to check caching + return thumbnailInfoProvider.get( file, 110 ); + } ).then( function() { + assert.strictEqual( apiCallCount, 2 ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'ThumbnailInfo fail test', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( {} ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + thumbnailInfoProvider = new mw.mmv.provider.ThumbnailInfo( api ); + + thumbnailInfoProvider.get( file, 100 ).fail( function() { + assert.ok( true, 'promise rejected when no data is returned' ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'ThumbnailInfo fail test 2', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( { + query: { + pages: { + '-1': { + title: 'File:Stuff.jpg' + } + } + } + } ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + thumbnailInfoProvider = new mw.mmv.provider.ThumbnailInfo( api ); + + thumbnailInfoProvider.get( file, 100 ).fail( function() { + assert.ok( true, 'promise rejected when imageinfo is missing' ); + QUnit.start(); + } ); + } ); + + QUnit.asyncTest( 'ThumbnailInfo missing page test', 1, function ( assert ) { + var api = { get: function() { + return $.Deferred().resolve( { + query: { + pages: { + '-1': { + title: 'File:Stuff.jpg', + missing: '', + imagerepository: '' + } + } + } + } ); + } }, + file = new mw.Title( 'File:Stuff.jpg' ), + thumbnailInfoProvider = new mw.mmv.provider.ThumbnailInfo( api ); + + thumbnailInfoProvider.get( file ).fail( function( errorMessage ) { + assert.strictEqual(errorMessage, 'file does not exist: File:Stuff.jpg', + 'error message is set correctly for missing file'); + QUnit.start(); + } ); + } ); +}( mediaWiki, jQuery ) ); -- To view, visit https://gerrit.wikimedia.org/r/110496 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: I80ffec39ee6c9e0ea0b37be2fc48315063b5ff8a Gerrit-PatchSet: 9 Gerrit-Project: mediawiki/extensions/MultimediaViewer Gerrit-Branch: master Gerrit-Owner: Gergő Tisza <[email protected]> Gerrit-Reviewer: Aarcos <[email protected]> Gerrit-Reviewer: Gergő Tisza <[email protected]> Gerrit-Reviewer: Gilles <[email protected]> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list [email protected] https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits
