Phuedx has uploaded a new change for review. https://gerrit.wikimedia.org/r/325337
Change subject: WIP: Log events ...................................................................... WIP: Log events Bug: T152225 Change-Id: I02eeb6a981107fa2c643b158f87529612d5240fd --- M extension.json M resources/ext.popups/actions.js M resources/ext.popups/boot.js A resources/ext.popups/changeListeners/eventLogging.js M resources/ext.popups/reducers.js A resources/ext.popups/schema.js A tests/qunit/ext.popups/changeListeners/eventLogging.test.js M tests/qunit/ext.popups/reducers.eventLogging.test.js A tests/qunit/ext.popups/schema.test.js 9 files changed, 252 insertions(+), 4 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Popups refs/changes/37/325337/1 diff --git a/extension.json b/extension.json index 055ac17..828993b 100644 --- a/extension.json +++ b/extension.json @@ -68,10 +68,12 @@ "resources/ext.popups/reducers.js", "resources/ext.popups/changeListener.js", "resources/ext.popups/renderer.js", + "resources/ext.popups/schema.js", "resources/ext.popups/changeListeners/footerLink.js", "resources/ext.popups/changeListeners/linkTitle.js", "resources/ext.popups/changeListeners/render.js", "resources/ext.popups/changeListeners/previewCount.js", + "resources/ext.popups/changeListeners/eventLogging.js", "resources/ext.popups/boot.js" ], "templates": { diff --git a/resources/ext.popups/actions.js b/resources/ext.popups/actions.js index 966379b..c90e9a3 100644 --- a/resources/ext.popups/actions.js +++ b/resources/ext.popups/actions.js @@ -19,7 +19,8 @@ PREVIEW_CLICK: 'PREVIEW_CLICK', COG_CLICK: 'COG_CLICK', SETTINGS_DIALOG_RENDERED: 'SETTINGS_DIALOG_RENDERED', - SETTINGS_DIALOG_CLOSED: 'SETTINGS_DIALOG_CLOSED' + SETTINGS_DIALOG_CLOSED: 'SETTINGS_DIALOG_CLOSED', + EVENT_LOGGING_QUEUE_DRAINED: 'EVENT_LOGGING_QUEUE_DRAINED' }, FETCH_START_DELAY = 500, // ms. ABANDON_END_DELAY = 300; // ms. @@ -232,6 +233,18 @@ }; }; + /** + * Represents the Event Logging queue being drained by the + * `mw.popups.changeListeners.eventLogging` change listener. + * + * @return {Object} + */ + actions.eventLoggingQueueDrained = function () { + return { + type: types.EVENT_LOGGING_QUEUE_DRAINED + }; + }; + mw.popups.actions = actions; mw.popups.actionTypes = types; diff --git a/resources/ext.popups/boot.js b/resources/ext.popups/boot.js index 6729599..a09c67e 100644 --- a/resources/ext.popups/boot.js +++ b/resources/ext.popups/boot.js @@ -30,8 +30,9 @@ * @param {Redux.Store} store * @param {Object} actions * @param {ext.popups.UserSettings} userSettings + * @param {mw.eventLog.Schema} schema */ - function registerChangeListeners( store, actions, userSettings ) { + function registerChangeListeners( store, actions, userSettings, schema ) { // Sugar. var changeListeners = mw.popups.changeListeners, @@ -41,6 +42,10 @@ registerChangeListener( store, changeListeners.linkTitle() ); registerChangeListener( store, changeListeners.render( actions ) ); registerChangeListener( store, changeListeners.previewCount( userSettings ) ); + + if ( window.QUnit === undefined ) { + registerChangeListener( store, changeListeners.eventLogging( actions, schema ) ); + } } /** @@ -74,9 +79,12 @@ generateToken = mw.user.generateRandomSessionId, gateway = createGateway(), userSettings = mw.popups.createUserSettings( mw.storage, mw.user ), - isUserInCondition; + isUserInCondition, + schema; isUserInCondition = mw.popups.createExperiment( mw.config, mw.user, userSettings ); + + schema = mw.popups.createSchema( mw.config, window.navigator ); // If debug mode is enabled, then enable Redux DevTools. if ( mw.config.get( 'debug' ) === true ) { @@ -90,7 +98,7 @@ ) ) ); actions = createBoundActions( store ); - registerChangeListeners( store, actions, userSettings ); + registerChangeListeners( store, actions, userSettings, schema ); actions.boot( isUserInCondition, diff --git a/resources/ext.popups/changeListeners/eventLogging.js b/resources/ext.popups/changeListeners/eventLogging.js new file mode 100644 index 0000000..19adc2d --- /dev/null +++ b/resources/ext.popups/changeListeners/eventLogging.js @@ -0,0 +1,24 @@ +( function ( mw, $ ) { + + /** + * + * @param {Object} boundActions + * @param {mw.eventLog.Schema} schema + * @return {ext.popups.ChangeListener} + */ + mw.popups.changeListeners.eventLogging = function ( boundActions, schema ) { + return function ( _, state ) { + var queue = state.eventLogging.queue, + baseData = state.eventLogging.baseData; + + if ( queue.length ) { + $.each( queue, function ( _, data ) { + schema.log( $.extend( true, {}, baseData, data ) ); + } ); + + boundActions.eventLoggingQueueDrained(); + } + }; + }; + +}( mediaWiki, jQuery ) ); diff --git a/resources/ext.popups/reducers.js b/resources/ext.popups/reducers.js index c8f28d5..d91e0ed 100644 --- a/resources/ext.popups/reducers.js +++ b/resources/ext.popups/reducers.js @@ -172,6 +172,11 @@ } ) } ); + case mw.popups.actionTypes.EVENT_LOGGING_QUEUE_DRAINED: + return nextState( state, { + queue: [] + } ); + default: return state; } diff --git a/resources/ext.popups/schema.js b/resources/ext.popups/schema.js new file mode 100644 index 0000000..88c41b3 --- /dev/null +++ b/resources/ext.popups/schema.js @@ -0,0 +1,18 @@ +( function ( mw, $ ) { + + /** + * @param {mw.Map} config + * @param {Navigator} navigator + * @return {mw.eventLog.Schema} + */ + mw.popups.createSchema = function ( config, navigator ) { + var samplingRate = config.get( 'wgPopupsSchemaPopupsSamplingRate', 0 ); + + if ( !navigator || !$.isFunction( navigator.sendBeacon ) ) { + samplingRate = 0; + } + + return new mw.eventLog.Schema( 'Popups', samplingRate ); + }; + +}( mediaWiki, jQuery ) ); diff --git a/tests/qunit/ext.popups/changeListeners/eventLogging.test.js b/tests/qunit/ext.popups/changeListeners/eventLogging.test.js new file mode 100644 index 0000000..d4f5767 --- /dev/null +++ b/tests/qunit/ext.popups/changeListeners/eventLogging.test.js @@ -0,0 +1,92 @@ +( function ( mw ) { + + QUnit.module( 'ext.popups/changeListeners/eventLogging', { + setup: function () { + this.boundActions = { + eventLoggingQueueDrained: this.sandbox.spy() + }; + + this.schema = { + log: this.sandbox.spy() + }; + + this.changeListener = mw.popups.changeListeners.eventLogging( + this.boundActions, + this.schema + ); + } + } ); + + QUnit.test( 'it should log all the queued events individually', function ( assert ) { + var baseData, + state; + + assert.expect( 2 ); + + baseData = { + foo: 'bar', + baz: 'qux' + }; + + state = { + eventLogging: { + baseData: baseData, + queue: [ + { + action: 'pageLoaded' + }, + { + action: 'opened' + } + ] + } + }; + + this.changeListener( undefined, state ); + + assert.ok( + this.schema.log.calledWith( { + foo: 'bar', + baz: 'qux', + action: 'pageLoaded' + } ), + 'It should merge the event data and the accumulated base data.' + ); + + assert.ok( this.schema.log.calledWith( { + foo: 'bar', + baz: 'qux', + action: 'opened' + } ) ); + } ); + + QUnit.test( + 'it should call the eventLoggingQueueDrained bound action creator', + function ( assert ) { + var state = { + eventLogging: { + baseData: {}, + queue: [] + } + }; + + this.changeListener( undefined, state ); + + assert.notOk( + this.boundActions.eventLoggingQueueDrained.called, + 'It shouldn\'t call the eventLoggingQueueDrained bound action creator if the queue is empty.' + ); + + // --- + + state.eventLogging.queue.push( { + action: 'pageLoaded' + } ); + + this.changeListener( undefined, state ); + + assert.ok( this.boundActions.eventLoggingQueueDrained.called ); + } + ); + +}( mediaWiki ) ); diff --git a/tests/qunit/ext.popups/reducers.eventLogging.test.js b/tests/qunit/ext.popups/reducers.eventLogging.test.js index 2a3a7d0..8190a4a 100644 --- a/tests/qunit/ext.popups/reducers.eventLogging.test.js +++ b/tests/qunit/ext.popups/reducers.eventLogging.test.js @@ -88,4 +88,32 @@ ); } ); + QUnit.test( 'EVENT_LOGGING_QUEUE_DRAINED', function ( assert ) { + var state, + action; + + state = { + queue: [ + { + action: 'pageLoaded' + }, + { + action: 'opened' + } + ] + }; + + action = { + type: 'EVENT_LOGGING_QUEUE_DRAINED' + }; + + assert.deepEqual( + mw.popups.reducers.eventLogging( state, action ), + { + queue: [] + }, + 'It resets the queue.' + ); + } ); + }( mediaWiki ) ); diff --git a/tests/qunit/ext.popups/schema.test.js b/tests/qunit/ext.popups/schema.test.js new file mode 100644 index 0000000..2989700 --- /dev/null +++ b/tests/qunit/ext.popups/schema.test.js @@ -0,0 +1,58 @@ +( function ( mw ) { + + QUnit.module( 'ext.popups/schema', { + setup: function () { + this.config = new mw.Map(); + this.config.set( 'wgPopupsSchemaPopupsSamplingRate', 1 ); + + this.navigator = { + sendBeacon: function () {} + }; + + // Stub out the mw.eventLog.Schema constructor function. + this.sandbox.stub( mw.eventLog, 'Schema', function () {} ); + } + } ); + + QUnit.test( 'it should use $wgPopupsSchemaPopupsSamplingRate as the sampling rate', function ( assert ) { + assert.expect( 2 ); + + mw.popups.createSchema( this.config, this.navigator ); + + assert.ok( mw.eventLog.Schema.calledWith( 'Popups', 1 ) ); + + // --- + + mw.popups.createSchema( new mw.Map(), this.navigator ); + + assert.ok( + mw.eventLog.Schema.calledWith( 'Popups', 0 ), + 'If $wgPopupsSchemaPopupsSamplingRate isn\'t set, then the sampling rate should be 0.' + ); + } ); + + QUnit.test( 'it should use a 0 sampling rate when sendBeacon isn\'t supported', function ( assert ) { + var expectedArgs = [ 'Popups', 0 ]; + + assert.expect( 3 ); + + mw.popups.createSchema( this.config, undefined ); + + assert.deepEqual( mw.eventLog.Schema.getCall( 0 ).args, expectedArgs ); + + // --- + + mw.popups.createSchema( this.config, { } ); + + assert.deepEqual( mw.eventLog.Schema.getCall( 1 ).args, expectedArgs ); + + // --- + + mw.popups.createSchema( this.config, { + sendBeacon: 'NOT A FUNCTION' + } ); + + assert.deepEqual( mw.eventLog.Schema.getCall( 1 ).args, expectedArgs ); + } ); + +}( mediaWiki ) ); -- To view, visit https://gerrit.wikimedia.org/r/325337 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I02eeb6a981107fa2c643b158f87529612d5240fd Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Popups Gerrit-Branch: mpga Gerrit-Owner: Phuedx <samsm...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits