Phuedx has uploaded a new change for review. https://gerrit.wikimedia.org/r/207762
Change subject: Hygiene: Generalise the futureLog mechanism ...................................................................... Hygiene: Generalise the futureLog mechanism Add the Schema#logBeacon method, which logs the event on the next page load. Events logged with the #logBeacon event are referred to as "beacons" throughout. Also, fix documentation of optional parameters in settings#get and Bug: T96326 Change-Id: I312adb5ca9665c388d5c1766252f326789cd0d3f --- M includes/Resources.php M javascripts/settings.js M resources/mobile.startup/Schema.js A resources/mobile.startup/beacons.js M resources/mobile.startup/init.js M tests/qunit/mobile.startup/test_Schema.js A tests/qunit/mobile.startup/test_beacons.js 7 files changed, 188 insertions(+), 13 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/MobileFrontend refs/changes/62/207762/1 diff --git a/includes/Resources.php b/includes/Resources.php index 61a301f..99782af 100644 --- a/includes/Resources.php +++ b/includes/Resources.php @@ -359,6 +359,7 @@ 'resources/mobile.startup/Thumbnail.js', 'resources/mobile.startup/Page.js', 'resources/mobile.startup/Skin.js', + 'resources/mobile.startup/beacons.js', 'resources/mobile.startup/Schema.js', 'resources/mobile.startup/util.js', 'resources/mobile.startup/init.js', diff --git a/javascripts/settings.js b/javascripts/settings.js index fb7f548..f5f5438 100644 --- a/javascripts/settings.js +++ b/javascripts/settings.js @@ -32,7 +32,7 @@ * @method * @param {String} name The key to refer to this value * @param {String} value The value to store alongside the key - * @param {Boolean} useCookieFallback Optional: When set this will use + * @param {Boolean} [useCookieFallback] When set this will use * cookies when local storage not available. * @returns {Boolean} Whether the save was successful or not */ @@ -49,7 +49,7 @@ * Retrieves a user setting from a previous browser setting * @method * @param {String} name The key to refer to this value - * @param {Boolean} useCookieFallback Optional: When set this will use cookies + * @param {Boolean} [useCookieFallback] When set this will use cookies * when local storage not available. * @returns {String|Boolean} Returns the associated value or False if nothing * is found diff --git a/resources/mobile.startup/Schema.js b/resources/mobile.startup/Schema.js index f471e70..b2976cb 100644 --- a/resources/mobile.startup/Schema.js +++ b/resources/mobile.startup/Schema.js @@ -1,7 +1,8 @@ ( function ( M, $ ) { var Schema, Class = M.require( 'Class' ), - user = M.require( 'user' ); + user = M.require( 'user' ), + beacons = M.require( 'beacons' ); /** * @class Schema @@ -59,6 +60,17 @@ } else { return $.Deferred().reject( 'EventLogging not installed.' ); } + }, + + /** + * Try to log an event after the next page load. + * + * @method + * + * @param {Object} data to log + */ + logBeacon: function ( data ) { + beacons.add( this.name, data ); } } ); diff --git a/resources/mobile.startup/beacons.js b/resources/mobile.startup/beacons.js new file mode 100644 index 0000000..160f68b --- /dev/null +++ b/resources/mobile.startup/beacons.js @@ -0,0 +1,92 @@ +( function ( M ) { + + var settings = M.require( 'settings' ); + + /** + * Loads the beacons from local storage. + * + * @returns {Array} + */ + function load() { + return JSON.parse( settings.get( 'mobileFrontend/beacons' ) ) || []; + } + + /** + * Saves the beacons to local storage. + * + * @param {Object[]} beacons + */ + function save( beacons ) { + settings.save( 'mobileFrontend/beacons', JSON.stringify( beacons ) ); + } + + // FIXME: [EL] This could be made more general if we decide to move the + // schema class to their respective modules. + /** + * Creates an instance of a schema in the `loggingSchemas` group, e.g. + * `factorySchema( 'MobileWebSearch' )` would return an instance of the + * `SchemaMobileWebSearch` class. + * + * @param {String} name + * @returns {Schema} + * @throws Error If the schema isn't defined + */ + function factorySchema( name ) { + var klass = M.require( 'loggingSchemas/Schema' + name ); + + return new klass; + } + + /** + * @singleton + */ + M.define( 'beacons', { + + /** + * Adds a beacon to be logged. + * + * @method + * + * @param {Object} event + */ + add: function ( schema, data ) { + var beacons = load(); + + beacons.push( { + schema: schema, + data: data + } ); + + save( beacons ); + }, + + /** + * Deletes all beacons without logging them. + * + * @method + */ + clear: function () { + save( [] ); + }, + + /** + * Logs all beacons in parallel. + * + * Once all of the logging requests have been started, then the beacons are deleted, and if + * a beacon fails, then it isn't retried. + * + * @method + */ + flush: function () { + var beacons = load(), + beacon; + + while ( beacon = beacons.shift() ) { + factorySchema( beacon.schema ).log( beacon.data ) + } + + this.clear(); + } + } ); + +} ( mw.mobileFrontend ) ); \ No newline at end of file diff --git a/resources/mobile.startup/init.js b/resources/mobile.startup/init.js index 2f65dbb..3daee2b 100644 --- a/resources/mobile.startup/init.js +++ b/resources/mobile.startup/init.js @@ -5,7 +5,7 @@ * @class mw.mobileFrontend * @singleton */ -( function ( M, $ ) { +( function ( M, $, mw ) { var currentPage, skin, $cachedIcons = $( '#page-actions' ).find( '.icon' ), PageApi = M.require( 'PageApi' ), @@ -86,4 +86,9 @@ $cachedIcons.addClass( 'mw-ui-icon mw-ui-icon-element' ).removeClass( 'icon' ); $cachedIcons.filter( '.icon-text' ).addClass( 'mw-ui-icon-before' ).removeClass( 'icon-text mw-ui-icon-element' ); } -}( mw.mobileFrontend, jQuery ) ); + + mw.loader.using( 'mobile.loggingSchemas' ).done( function () { + M.require( 'beacons' ).flush(); + } ); + +}( mw.mobileFrontend, jQuery, mw ) ); diff --git a/tests/qunit/mobile.startup/test_Schema.js b/tests/qunit/mobile.startup/test_Schema.js index 65dbf7b..dfaf0fb 100644 --- a/tests/qunit/mobile.startup/test_Schema.js +++ b/tests/qunit/mobile.startup/test_Schema.js @@ -1,10 +1,18 @@ ( function ( $, M ) { - var Schema = M.require( 'Schema' ); + var Schema = M.require( 'Schema' ), + beacons = M.require( 'beacons' ); - QUnit.module( 'Schema' ); + QUnit.module( 'Schema', { + setup: function () { + this.TestSchema = Schema.extend( { + name: 'test' + } ); + this.stub( beacons, 'add' ).returns( null ); + } + } ); QUnit.test( '#initialize', 3, function ( assert ) { - var s1, s2, s3, SubSchema; + var s1, s2, s3; // Creating a schema without name throws try { s1 = new Schema(); @@ -15,11 +23,19 @@ s2 = new Schema( {}, 'aname' ); assert.strictEqual( s2.name, 'aname', 'explicit name gets set' ); - SubSchema = Schema.extend( { - name: 'subname' - } ); - s3 = new SubSchema( {} ); - assert.strictEqual( s3.name, 'subname', 'subclassed name works' ); + s3 = new this.TestSchema( {} ); + assert.strictEqual( s3.name, 'test', 'subclassed name works' ); + } ); + + QUnit.test( '#logBeacon', 1, function ( assert ) { + var test = new this.TestSchema(), + data = { + foo: 'bar' + }; + + test.logBeacon( data ); + + assert.deepEqual( [ 'test', data ], beacons.add.firstCall.args, '#logBeacon calls beacons.add' ); } ); }( jQuery, mw.mobileFrontend ) ); diff --git a/tests/qunit/mobile.startup/test_beacons.js b/tests/qunit/mobile.startup/test_beacons.js new file mode 100644 index 0000000..e7f81d0 --- /dev/null +++ b/tests/qunit/mobile.startup/test_beacons.js @@ -0,0 +1,49 @@ +( function ( M ) { + + var Schema = M.require( 'Schema' ), + TestSchema = Schema.extend( { + name: 'test' + } ), + beacons = M.require( 'beacons' ); + + // Because these can't be undefined, we have to do this in the module preamble (not setup and + // teardown). + M.define( 'loggingSchemas/SchemaTest', TestSchema ); + M.define( 'loggingSchemas/SchemaTest2', TestSchema ); + + QUnit.module( 'MobileFrontend: beacons', { + setup: function () { + this.stub( TestSchema.prototype, 'log' ); + this.logStub = TestSchema.prototype.log; + } + } ); + + QUnit.test( '#flush should log the beacon', 1, function ( assert ) { + var data = { + foo: 'bar' + }; + + beacons.add( 'Test', data ); + beacons.flush(); + + assert.deepEqual( [ data ], this.logStub.firstCall.args ); + } ); + + QUnit.test( '#flush should log all the beacons that have been added', 1, function ( assert ) { + beacons.add( 'Test', {} ); + beacons.add( 'Test2', {} ); + beacons.flush(); + + assert.strictEqual( 2, this.logStub.callCount ); + } ); + + QUnit.test( '#clear shouldn\'t log the beacons that have been added', 1, function ( assert ) { + beacons.add( 'Test', {} ); + beacons.add( 'Test2', {} ); + beacons.clear(); + beacons.flush(); + + assert.strictEqual( 0, this.logStub.callCount ); + } ); + +} ( mw.mobileFrontend ) ); -- To view, visit https://gerrit.wikimedia.org/r/207762 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I312adb5ca9665c388d5c1766252f326789cd0d3f Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/MobileFrontend Gerrit-Branch: master Gerrit-Owner: Phuedx <g...@samsmith.io> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits