diff --git a/resources/ext.popups/boot.js b/resources/ext.popups/boot.js index 681f5a66a..f51cdbea1 100644 --- a/resources/ext.popups/boot.js +++ b/resources/ext.popups/boot.js @@ -17,9 +17,9 @@ */ function createGateway( config ) { if ( config.get( 'wgPopupsAPIUseRESTBase' ) ) { - return new mw.popups.RESTBaseGateway( $ ); + return mw.popups.createRESTBaseGateway( $.ajax ); } - return new mw.popups.MediaWikiApiGateway( new mw.Api() ); + return mw.popups.createMediaWikiApiGateway( new mw.Api() ); } /** diff --git a/resources/ext.popups/gateway.js b/resources/ext.popups/gateway.js index 2eaea3a3a..3d0c45474 100644 --- a/resources/ext.popups/gateway.js +++ b/resources/ext.popups/gateway.js @@ -10,16 +10,13 @@ /** * Interface for API gateway that fetches page summary * - * @interface Gateway - * @function - * @param {mw.Api|jQuery} + * @interface ext.popups.Gateway */ /** - * Get page summary - * + * Returns a preview model fetched from the api * @function - * @name Gateway#getPageSummary + * @name ext.popups.Gateway#getPageSummary * @param {String} title Page title we're querying * @returns {jQuery.Promise} that resolves with {ext.popups.PreviewModel} * if the request is successful and the response is not empty; otherwise @@ -27,147 +24,163 @@ */ /** - * MediaWiki API gateway + * MediaWiki API gateway factory * - * @class * @param {mw.Api} api - * @implements {ext.popups.Gateway} + * @returns {ext.popups.Gateway} */ - function MediaWikiApiGateway( api ) { - this.api = api; - } + function createMediaWikiApiGateway( api ) { - /** - * Fetch page data from the API - * - * @private - * @param {String} title - * @return {jQuery.Promise} - */ - MediaWikiApiGateway.prototype.fetch = function ( title ) { - return this.api.get( { - action: 'query', - prop: 'info|extracts|pageimages|revisions|info', - formatversion: 2, - redirects: true, - exintro: true, - exchars: EXTRACT_LENGTH, + /** + * Fetch page data from the API + * + * @param {String} title + * @return {jQuery.Promise} + */ + function fetch( title ) { + return api.get( { + action: 'query', + prop: 'info|extracts|pageimages|revisions|info', + formatversion: 2, + redirects: true, + exintro: true, + exchars: EXTRACT_LENGTH, - // There is an added geometric limit on .mwe-popups-extract - // so that text does not overflow from the card. - explaintext: true, + // There is an added geometric limit on .mwe-popups-extract + // so that text does not overflow from the card. + explaintext: true, - piprop: 'thumbnail', - pithumbsize: THUMBNAIL_SIZE, - rvprop: 'timestamp', - inprop: 'url', - titles: title, - smaxage: CACHE_LIFETIME, - maxage: CACHE_LIFETIME, - uselang: 'content' - }, { - headers: { - 'X-Analytics': 'preview=1' - } - } ); - }; - - /** - * Extract page data from the response - * - * @private - * @param {Object} data API response data - * @throws {Error} Throw an error if page data cannot be extracted, - * i.e. if the response is empty, - * @returns {Object} - */ - MediaWikiApiGateway.prototype.extractPageFromResponse = function( data ) { - if ( - data.query && - data.query.pages && - data.query.pages.length - ) { - return data.query.pages[ 0 ]; + piprop: 'thumbnail', + pithumbsize: THUMBNAIL_SIZE, + rvprop: 'timestamp', + inprop: 'url', + titles: title, + smaxage: CACHE_LIFETIME, + maxage: CACHE_LIFETIME, + uselang: 'content' + }, { + headers: { + 'X-Analytics': 'preview=1' + } + } ); } - throw new Error( 'API response `query.pages` is empty.' ); - }; + /** + * Extract page data from the response + * + * @param {Object} data API response data + * @throws {Error} Throw an error if page data cannot be extracted, + * i.e. if the response is empty, + * @returns {Object} + */ + function extractPageFromResponse( data ) { + if ( + data.query && + data.query.pages && + data.query.pages.length + ) { + return data.query.pages[ 0 ]; + } - /** - * Transform the API response to a preview model - * - * @private - * @param {Object} page - * @returns {ext.popups.PreviewModel} - */ - MediaWikiApiGateway.prototype.convertPageToModel = function( page ) { - return mw.popups.preview.createModel( - page.title, - page.canonicalurl, - page.pagelanguagehtmlcode, - page.pagelanguagedir, - page.extract, - page.thumbnail - ); - }; + throw new Error( 'API response `query.pages` is empty.' ); + } - MediaWikiApiGateway.prototype.getPageSummary = function( title ) { - return this.fetch( title ) - .then( this.extractPageFromResponse ) - .then( this.convertPageToModel ); - }; + /** + * Transform the API response to a preview model + * + * @param {Object} page + * @returns {ext.popups.PreviewModel} + */ + function convertPageToModel( page ) { + return mw.popups.preview.createModel( + page.title, + page.canonicalurl, + page.pagelanguagehtmlcode, + page.pagelanguagedir, + page.extract, + page.thumbnail + ); + } - /** - * RESTBase gateway - * - * @class - * @param {jQuery} api - * @implements {ext.popups.Gateway} - */ - function RESTBaseGateway( api ) { - this.api = api; + /** + * Get the page summary from the api and transform the data + * + * @param {String} title + * @returns {jQuery.Promise} + */ + function getPageSummary( title ) { + return fetch( title ) + .then( extractPageFromResponse ) + .then( convertPageToModel ); + } + + return { + fetch: fetch, + extractPageFromResponse: extractPageFromResponse, + convertPageToModel: convertPageToModel, + getPageSummary: getPageSummary + }; } /** - * Fetch page data from the API + * RESTBase gateway factory * - * @private - * @param {String} title - * @return {jQuery.Promise} + * @param {Function} ajax function from jQuery for example + * @returns {ext.popups.Gateway} */ - RESTBaseGateway.prototype.fetch = function ( title ) { - return this.api.ajax( { - url: RESTBASE_ENDPOINT + encodeURIComponent( title ), - headers: { - Accept: 'application/json; charset=utf-8' + - 'profile="' + RESTBASE_PROFILE + '"' - } - } ); - }; + function createRESTBaseGateway( ajax ) { - /** - * Transform the API response to a preview model - * - * @private - * @param {Object} page - * @returns {ext.popups.PreviewModel} - */ - RESTBaseGateway.prototype.convertPageToModel = function( page ) { - return mw.popups.preview.createModel( - page.title, - new mw.Title( page.title ).getUrl(), - page.lang, - page.dir, - page.extract, - page.thumbnail - ); - }; + /** + * Fetch page data from the API + * + * @param {String} title + * @return {jQuery.Promise} + */ + function fetch( title ) { + return ajax( { + url: RESTBASE_ENDPOINT + encodeURIComponent( title ), + headers: { + Accept: 'application/json; charset=utf-8' + + 'profile="' + RESTBASE_PROFILE + '"' + } + } ); + } - RESTBaseGateway.prototype.getPageSummary = function( title ) { - return this.fetch( title ) - .then( this.convertPageToModel ); - }; + /** + * Transform the API response to a preview model + * + * @param {Object} page + * @returns {ext.popups.PreviewModel} + */ + function convertPageToModel( page ) { + return mw.popups.preview.createModel( + page.title, + new mw.Title( page.title ).getUrl(), + page.lang, + page.dir, + page.extract, + page.thumbnail + ); + } - mw.popups.MediaWikiApiGateway = MediaWikiApiGateway; - mw.popups.RESTBaseGateway = RESTBaseGateway; + /** + * Get the page summary from the api and transform the data + * + * @param {String} title + * @returns {jQuery.Promise} + */ + function getPageSummary( title ) { + return fetch( title ) + .then( convertPageToModel ); + } + + return { + fetch: fetch, + convertPageToModel: convertPageToModel, + getPageSummary: getPageSummary + }; + } + + mw.popups.createMediaWikiApiGateway = createMediaWikiApiGateway; + mw.popups.createRESTBaseGateway = createRESTBaseGateway; }( mediaWiki, jQuery ) ); diff --git a/tests/qunit/ext.popups/actions.test.js b/tests/qunit/ext.popups/actions.test.js index 3ab556d26..a9722c504 100644 --- a/tests/qunit/ext.popups/actions.test.js +++ b/tests/qunit/ext.popups/actions.test.js @@ -302,7 +302,6 @@ QUnit.test( 'it shouldn\'t delay dispatching the FETCH_END action if the API request is over the target', function ( assert ) { - var that = this; this.fetch(); @@ -358,8 +357,8 @@ that.waitPromise.then( function () { assert.ok( dispatch.calledWith( { - type: 'ABANDON_END', - token: token + type: 'ABANDON_END', + token: token } ), 'ABANDON_* share the same token.' ); diff --git a/tests/qunit/ext.popups/gateway.test.js b/tests/qunit/ext.popups/gateway.test.js index 104a4892f..2cb8011aa 100644 --- a/tests/qunit/ext.popups/gateway.test.js +++ b/tests/qunit/ext.popups/gateway.test.js @@ -1,8 +1,8 @@ ( function ( mw, $ ) { var createModel = mw.popups.preview.createModel, - MediaWikiApiGateway = mw.popups.MediaWikiApiGateway, - RESTBaseGateway = mw.popups.RESTBaseGateway, + createMediaWikiApiGateway = mw.popups.createMediaWikiApiGateway, + createRESTBaseGateway = mw.popups.createRESTBaseGateway, MEDIAWIKI_API_RESPONSE = { query: { pages: [ @@ -74,11 +74,11 @@ QUnit.module( 'ext.popups/gateway' ); QUnit.test( 'MediaWiki API gateway is called with correct arguments', function ( assert ) { - var getSpy = this.sandbox.spy(), + var spy = this.sandbox.spy(), api = { - get: getSpy + get: spy }, - gateway = new MediaWikiApiGateway( api ), + gateway = createMediaWikiApiGateway( api ), expectedOptions = { action: 'query', prop: 'info|extracts|pageimages|revisions|info', @@ -104,12 +104,15 @@ gateway.fetch( 'Test Title' ); - assert.deepEqual( getSpy.getCall( 0 ).args[ 0 ], expectedOptions, 'options' ); - assert.deepEqual( getSpy.getCall( 0 ).args[ 1 ], expectedHeaders, 'headers' ); + assert.deepEqual( spy.getCall( 0 ).args[ 0 ], expectedOptions, 'options' ); + assert.deepEqual( spy.getCall( 0 ).args[ 1 ], expectedHeaders, 'headers' ); } ); QUnit.test( 'MediaWiki API gateway is correctly extracting the page data from the response ', function ( assert ) { - var gateway = new MediaWikiApiGateway(), + var api = { + get: this.sandbox.stub() + }, + gateway = createMediaWikiApiGateway( api ), errorCases = [ {}, { @@ -151,7 +154,7 @@ } ); QUnit.test( 'MediaWiki API gateway is correctly converting the page data to a model ', function ( assert ) { - var gateway = new MediaWikiApiGateway(), + var gateway = createMediaWikiApiGateway(), page = gateway.extractPageFromResponse( MEDIAWIKI_API_RESPONSE ); assert.deepEqual( @@ -165,7 +168,7 @@ api = { get: this.sandbox.stub().returns( deferred.promise() ) }, - gateway = new MediaWikiApiGateway( api ), + gateway = createMediaWikiApiGateway( api ), done = assert.async( 1 ); gateway.getPageSummary( 'Test Title' ).fail( function () { @@ -182,7 +185,7 @@ $.Deferred().resolve( MEDIAWIKI_API_RESPONSE ).promise() ) }, - gateway = new MediaWikiApiGateway( api ), + gateway = createMediaWikiApiGateway( api ), done = assert.async( 1 ); gateway.getPageSummary( 'Test Title' ).done( function ( result ) { @@ -221,7 +224,7 @@ $.Deferred().resolve( response ).promise() ) }, - gateway = new MediaWikiApiGateway( api ), + gateway = createMediaWikiApiGateway( api ), done = assert.async( 1 ); gateway.getPageSummary( 'Test Title' ).done( function ( result ) { @@ -233,10 +236,7 @@ QUnit.test( 'RESTBase gateway is called with correct arguments', function ( assert ) { var getSpy = this.sandbox.spy(), - api = { - ajax: getSpy - }, - gateway = new RESTBaseGateway( api ), + gateway = createRESTBaseGateway( getSpy ), expectedOptions = { url: '/api/rest_v1/page/summary/' + encodeURIComponent( 'Test Title' ), headers: { @@ -251,7 +251,7 @@ } ); QUnit.test( 'RESTBase gateway is correctly converting the page data to a model ', function ( assert ) { - var gateway = new RESTBaseGateway(); + var gateway = createRESTBaseGateway(); assert.deepEqual( gateway.convertPageToModel( RESTBASE_RESPONSE ), @@ -261,10 +261,8 @@ QUnit.test( 'RESTBase gateway handles the API failure', function ( assert ) { var deferred = $.Deferred(), - api = { - ajax: this.sandbox.stub().returns( deferred.promise() ) - }, - gateway = new RESTBaseGateway( api ), + api = this.sandbox.stub().returns( deferred.promise() ), + gateway = createRESTBaseGateway( api ), done = assert.async( 1 ); gateway.getPageSummary( 'Test Title' ).fail( function () { @@ -276,12 +274,10 @@ } ); QUnit.test( 'RESTBase gateway returns the correct data ', function ( assert ) { - var api = { - ajax: this.sandbox.stub().returns( - $.Deferred().resolve( RESTBASE_RESPONSE ).promise() - ) - }, - gateway = new RESTBaseGateway( api ), + var api = this.sandbox.stub().returns( + $.Deferred().resolve( RESTBASE_RESPONSE ).promise() + ), + gateway = createRESTBaseGateway( api ), done = assert.async( 1 ); gateway.getPageSummary( 'Test Title' ).done( function ( result ) { @@ -298,12 +294,10 @@ detail: 'Page or revision not found.', uri: '/en.wikipedia.org/v1/page/summary/Missing_page' }, - api = { - ajax: this.sandbox.stub().returns( - $.Deferred().rejectWith( response ).promise() - ) - }, - gateway = new RESTBaseGateway( api ), + api = this.sandbox.stub().returns( + $.Deferred().rejectWith( response ).promise() + ), + gateway = createRESTBaseGateway( api ), done = assert.async( 1 ); gateway.getPageSummary( 'Missing Page' ).fail( function () {