node-qunit tests for GrowthExperiments
This also moves serializeActionData into a separate module. Change-Id: I46e4d0746c90d0e97f73de8b272d67df8d27936e
这个提交包含在:
父节点
9657448e6c
当前提交
815808e086
|
@ -1,3 +1,5 @@
|
|||
/node_modules
|
||||
/vendor
|
||||
/composer.lock
|
||||
/.nyc_output
|
||||
/coverage
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"all": true,
|
||||
"include": [ "modules/**/*.js" ],
|
||||
"statements": 52,
|
||||
"branches": 45,
|
||||
"functions": 52,
|
||||
"lines": 52,
|
||||
"check-coverage": false
|
||||
}
|
|
@ -272,7 +272,8 @@
|
|||
"ext.growthExperiments.Homepage": {
|
||||
"packageFiles": [
|
||||
"homepage/ext.growthExperiments.Homepage.js",
|
||||
"homepage/ext.growthExperiments.Homepage.Logger.js"
|
||||
"homepage/ext.growthExperiments.Homepage.Logger.js",
|
||||
"utils/ext.growthExperiments.Utils.js"
|
||||
],
|
||||
"dependencies": [
|
||||
"mediawiki.user"
|
||||
|
@ -325,6 +326,7 @@
|
|||
"packageFiles": [
|
||||
"help/ext.growthExperiments.Help.js",
|
||||
"help/ext.growthExperiments.HelpPanelLogger.js",
|
||||
"utils/ext.growthExperiments.Utils.js",
|
||||
"help/ext.growthExperiments.HelpPanelSearchWidget.js",
|
||||
"help/ext.growthExperiments.HelpPanelProcessDialog.js",
|
||||
{
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
.hide();
|
||||
$emailInput.after( $warningBox );
|
||||
}
|
||||
// eslint-disable-next-line no-jquery/no-slide
|
||||
$warningBox.slideDown();
|
||||
} )
|
||||
.on( 'blur', function () {
|
||||
// Hide the warning again if the user leaves the email input without typing anything
|
||||
var $warningBox = $emailInput.next( '.warning' );
|
||||
if ( $warningBox.length && $emailInput.val().trim() === '' ) {
|
||||
// eslint-disable-next-line no-jquery/no-slide
|
||||
$warningBox.slideUp();
|
||||
}
|
||||
} );
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
( function () {
|
||||
|
||||
var Utils = require( '../utils/ext.growthExperiments.Utils.js' );
|
||||
|
||||
/**
|
||||
* @class mw.libs.ge.HelpPanelLogger
|
||||
* @constructor
|
||||
|
@ -28,7 +30,7 @@
|
|||
{
|
||||
action: action,
|
||||
/* eslint-disable-next-line camelcase */
|
||||
action_data: this.serializeActionData( data )
|
||||
action_data: Utils.serializeActionData( data )
|
||||
},
|
||||
this.getMetaData(),
|
||||
metadataOverride
|
||||
|
@ -43,27 +45,6 @@
|
|||
this.previousEditorInterface = eventData.editor_interface;
|
||||
};
|
||||
|
||||
HelpPanelLogger.prototype.serializeActionData = function ( data ) {
|
||||
if ( !data ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( typeof data === 'object' ) {
|
||||
return Object.keys( data )
|
||||
.map( function ( key ) {
|
||||
return key + '=' + data[ key ];
|
||||
} )
|
||||
.join( ';' );
|
||||
}
|
||||
|
||||
if ( Array.isArray( data ) ) {
|
||||
return data.join( ';' );
|
||||
}
|
||||
|
||||
// assume it is string or number or bool
|
||||
return data;
|
||||
};
|
||||
|
||||
HelpPanelLogger.prototype.getMetaData = function () {
|
||||
var editor = this.getEditor(),
|
||||
readingMode = editor === 'reading';
|
||||
|
@ -112,11 +93,13 @@
|
|||
}
|
||||
|
||||
// Mobile: wikitext
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
if ( $( 'textarea#wikitext-editor:visible' ).length ) {
|
||||
return 'wikitext';
|
||||
}
|
||||
|
||||
// Mobile: VE
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
if ( $( '.ve-init-mw-mobileArticleTarget:visible' ).length ) {
|
||||
return 'visualeditor';
|
||||
}
|
||||
|
@ -142,6 +125,7 @@
|
|||
}
|
||||
|
||||
// Desktop: old wikitext editor
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
if ( $( '#wpTextbox1:visible' ).length ) {
|
||||
return 'wikitext';
|
||||
}
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
}
|
||||
|
||||
$( function () {
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
var $buttonToInfuse = $( '#mw-ge-help-panel-cta-button' ),
|
||||
$buttonWrapper = $buttonToInfuse.parent(),
|
||||
$mfOverlay,
|
||||
$veUiOverlay,
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$body = $( 'body' ),
|
||||
windowManager = new OO.ui.WindowManager( { modal: OO.ui.isMobile() } ),
|
||||
$overlay = $( '<div>' ).addClass( 'mw-ge-help-panel-widget-overlay' ),
|
||||
|
@ -110,9 +112,11 @@
|
|||
// HACK: Detach the MobileFrontend overlay for both VE and source edit modes.
|
||||
// Per T212967, leaving them enabled results in a phantom text input that the
|
||||
// user can only see the cursor input for.
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$mfOverlay = $( '.overlay' ).detach();
|
||||
// Detach the VE UI overlay, needed to prevent interference with scrolling in
|
||||
// our dialog.
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$veUiOverlay = $( '.ve-ui-overlay' ).detach();
|
||||
// More hacks. WindowManager#toggleGlobalEvents adds the modal-active class,
|
||||
// which is styled with position:relative. This seems to interfere with search
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
( function () {
|
||||
|
||||
var Utils = require( '../utils/ext.growthExperiments.Utils.js' );
|
||||
|
||||
/**
|
||||
* @param {boolean} enabled
|
||||
* @param {string} homepagePageviewToken
|
||||
|
@ -28,7 +30,7 @@
|
|||
mw.track( 'event.HomepageModule', {
|
||||
/* eslint-disable camelcase */
|
||||
action: action,
|
||||
action_data: this.serializeActionData( data ),
|
||||
action_data: Utils.serializeActionData( data ),
|
||||
user_id: this.userId,
|
||||
user_editcount: this.userEditCount,
|
||||
module: module,
|
||||
|
@ -38,26 +40,5 @@
|
|||
} );
|
||||
};
|
||||
|
||||
HomepageModuleLogger.prototype.serializeActionData = function ( data ) {
|
||||
if ( !data ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( typeof data === 'object' ) {
|
||||
return Object.keys( data )
|
||||
.map( function ( key ) {
|
||||
return key + '=' + data[ key ];
|
||||
} )
|
||||
.join( ';' );
|
||||
}
|
||||
|
||||
if ( Array.isArray( data ) ) {
|
||||
return data.join( ';' );
|
||||
}
|
||||
|
||||
// assume it is string or number or bool
|
||||
return data;
|
||||
};
|
||||
|
||||
module.exports = HomepageModuleLogger;
|
||||
}() );
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
logger: logger
|
||||
}, config.dialog ) );
|
||||
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$( 'body' ).append( windowManager.$element );
|
||||
windowManager.addWindows( [ dialog ] );
|
||||
|
||||
|
|
|
@ -34,9 +34,11 @@
|
|||
logger.log( moduleName, 'impression', getModuleExtraData( moduleName ) );
|
||||
};
|
||||
|
||||
/* eslint-disable no-jquery/no-event-shorthand */
|
||||
$( moduleSelector )
|
||||
.hover( handleHover( 'in' ), handleHover( 'out' ) )
|
||||
.on( 'click', '[data-link-id]', handleClick )
|
||||
.each( logImpression );
|
||||
/* eslint-enable no-jquery/no-event-shorthand */
|
||||
|
||||
}() );
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
( function () {
|
||||
|
||||
/**
|
||||
* Serialize data for use with action_data event logging property.
|
||||
*
|
||||
* @param {Object|string|boolean|integer|Array} data
|
||||
* @return {string|*}
|
||||
*/
|
||||
function serializeActionData( data ) {
|
||||
if ( !data ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( Array.isArray( data ) ) {
|
||||
return data.join( ';' );
|
||||
}
|
||||
|
||||
if ( typeof data === 'object' ) {
|
||||
return Object.keys( data )
|
||||
.map( function ( key ) {
|
||||
return key + '=' + data[ key ];
|
||||
} )
|
||||
.join( ';' );
|
||||
}
|
||||
|
||||
// assume it is string or number or bool
|
||||
return data;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
serializeActionData: serializeActionData
|
||||
};
|
||||
|
||||
}() );
|
|
@ -12,6 +12,7 @@
|
|||
privacyStatementUrl: mw.config.get( 'wgWelcomeSurveyPrivacyPolicyUrl' )
|
||||
} );
|
||||
|
||||
// eslint-disable-next-line no-jquery/no-global-selector
|
||||
$( 'body' ).append( windowManager.$element );
|
||||
windowManager.addWindows( [ survey, confirmation ] );
|
||||
windowManager.openWindow( survey );
|
||||
|
|
12
package.json
12
package.json
|
@ -1,10 +1,18 @@
|
|||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
"test": "grunt test && npm run test:unit",
|
||||
"test:unit": "NODE_PATH=modules nyc --reporter=lcovonly --reporter=lcov --reporter=text --reporter=text-summary qunit 'tests/node-qunit/**/*.test.js'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-config-wikimedia": "0.9.0",
|
||||
"jquery": "3.3.1",
|
||||
"jsdom": "14.0.0",
|
||||
"oojs": "2.2.2",
|
||||
"oojs-ui": "0.31.1",
|
||||
"qunit": "2.9.2",
|
||||
"sinon": "7.2.7",
|
||||
"nyc": "13.1.0",
|
||||
"eslint-config-wikimedia": "0.11.0",
|
||||
"grunt": "1.0.3",
|
||||
"grunt-eslint": "21.0.0",
|
||||
"grunt-banana-checker": "0.6.0",
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": [
|
||||
"wikimedia/qunit",
|
||||
"../../.eslintrc.json"
|
||||
],
|
||||
"globals": {
|
||||
"global": true,
|
||||
"require": true
|
||||
},
|
||||
"rules": {
|
||||
"no-implicit-globals": "off"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
var jsdom = require( 'jsdom' ),
|
||||
sinon = require( 'sinon' ),
|
||||
HelpPanelLogger = require( '../../../modules/help/ext.growthExperiments.HelpPanelLogger.js' ),
|
||||
sandbox,
|
||||
dom;
|
||||
|
||||
QUnit.module( 'HelpPanelLogger', {
|
||||
beforeEach: function () {
|
||||
sandbox = sinon.createSandbox();
|
||||
dom = new jsdom.JSDOM( '<!doctype html><html><body></body></html>' );
|
||||
global.window = dom.window;
|
||||
global.document = global.window.document;
|
||||
global.jQuery = global.$ = window.jQuery = window.$ = require( 'jquery' );
|
||||
global.OO = require( 'oojs' );
|
||||
|
||||
// Both OOUI and the WMF theme need to be loaded into scope via require();
|
||||
// properties are automatically added to OO namespace.
|
||||
require( 'oojs-ui' );
|
||||
require( 'oojs-ui/dist/oojs-ui-wikimediaui.js' );
|
||||
|
||||
global.mw = {};
|
||||
global.mw.config = {};
|
||||
global.mw.config.get = sinon.stub().returns( 42 );
|
||||
global.mw.user = {};
|
||||
global.mw.user.generateRandomSessionId = sinon.stub().returns( 'foo' );
|
||||
global.mw.user.getId = sinon.stub().returns( 24 );
|
||||
global.mw.user.sessionId = sinon.stub().returns( 'bar' );
|
||||
global.mw.track = sinon.stub();
|
||||
global.mw.Uri = sinon.stub().returns( {
|
||||
query: sinon.stub()
|
||||
} );
|
||||
},
|
||||
|
||||
afterEach: function () {
|
||||
delete require.cache[ require.resolve( 'jquery' ) ];
|
||||
sandbox.reset();
|
||||
}
|
||||
}, function () {
|
||||
QUnit.test( 'disabled/enabled', function ( assert ) {
|
||||
var helpPanelLogger = new HelpPanelLogger( false );
|
||||
helpPanelLogger.log();
|
||||
assert.strictEqual( global.mw.track.called, false );
|
||||
helpPanelLogger = new HelpPanelLogger( true );
|
||||
helpPanelLogger.log();
|
||||
assert.strictEqual( global.mw.track.called, true );
|
||||
} );
|
||||
QUnit.test( 'log', function ( assert ) {
|
||||
var helpPanelLogger = new HelpPanelLogger( true, {
|
||||
editorInterface: 'reading',
|
||||
sessionId: 'foo'
|
||||
} );
|
||||
// eslint-disable-next-line camelcase
|
||||
helpPanelLogger.log( 'impression', 'blah', { editor_interface: 'wikitext' } );
|
||||
assert.strictEqual( global.mw.track.getCall( 0 ).args[ 0 ], 'event.HelpPanel' );
|
||||
assert.deepEqual( global.mw.track.getCall( 0 ).args[ 1 ], {
|
||||
/* eslint-disable camelcase */
|
||||
action: 'impression',
|
||||
action_data: 'blah',
|
||||
user_id: 24,
|
||||
user_editcount: 42,
|
||||
editor_interface: 'wikitext',
|
||||
is_mobile: false,
|
||||
page_id: 0,
|
||||
page_title: '',
|
||||
page_ns: 42,
|
||||
user_can_edit: 42,
|
||||
page_protection: '',
|
||||
session_token: 'bar',
|
||||
help_panel_session_id: 'foo'
|
||||
/* eslint-enable camelcase */
|
||||
} );
|
||||
} );
|
||||
|
||||
} );
|
|
@ -0,0 +1,64 @@
|
|||
var jsdom = require( 'jsdom' ),
|
||||
sinon = require( 'sinon' ),
|
||||
HomepageModuleLogger = require( '../../../modules/homepage/ext.growthExperiments.Homepage.Logger.js' ),
|
||||
sandbox,
|
||||
dom;
|
||||
|
||||
QUnit.module( 'HomepageLogger', {
|
||||
beforeEach: function () {
|
||||
sandbox = sinon.createSandbox();
|
||||
dom = new jsdom.JSDOM( '<!doctype html><html><body></body></html>' );
|
||||
global.window = dom.window;
|
||||
global.document = global.window.document;
|
||||
global.jQuery = global.$ = window.jQuery = window.$ = require( 'jquery' );
|
||||
global.OO = require( 'oojs' );
|
||||
|
||||
// Both OOUI and the WMF theme need to be loaded into scope via require();
|
||||
// properties are automatically added to OO namespace.
|
||||
require( 'oojs-ui' );
|
||||
require( 'oojs-ui/dist/oojs-ui-wikimediaui.js' );
|
||||
|
||||
global.mw = {};
|
||||
global.mw.config = {};
|
||||
global.mw.config.get = sinon.stub().returns( 42 );
|
||||
global.mw.user = {};
|
||||
global.mw.user.generateRandomSessionId = sinon.stub().returns( 'foo' );
|
||||
global.mw.user.getId = sinon.stub().returns( 24 );
|
||||
global.mw.user.sessionId = sinon.stub().returns( 'bar' );
|
||||
global.mw.track = sinon.stub();
|
||||
global.mw.Uri = sinon.stub().returns( {
|
||||
query: sinon.stub()
|
||||
} );
|
||||
},
|
||||
|
||||
afterEach: function () {
|
||||
delete require.cache[ require.resolve( 'jquery' ) ];
|
||||
sandbox.reset();
|
||||
}
|
||||
}, function () {
|
||||
QUnit.test( 'disabled/enabled', function ( assert ) {
|
||||
var homepageModuleLogger = new HomepageModuleLogger( false, 'blah' );
|
||||
homepageModuleLogger.log();
|
||||
assert.strictEqual( global.mw.track.called, false );
|
||||
homepageModuleLogger = new HomepageModuleLogger( true, 'blah' );
|
||||
homepageModuleLogger.log();
|
||||
assert.strictEqual( global.mw.track.called, true );
|
||||
} );
|
||||
QUnit.test( 'log', function ( assert ) {
|
||||
var homepageModuleLogger = new HomepageModuleLogger( true, 'blah' );
|
||||
homepageModuleLogger.log( 'tutorial', 'hover-in', { foo: 'bar' } );
|
||||
assert.strictEqual( global.mw.track.getCall( 0 ).args[ 0 ], 'event.HomepageModule' );
|
||||
assert.deepEqual( global.mw.track.getCall( 0 ).args[ 1 ], {
|
||||
/* eslint-disable camelcase */
|
||||
action: 'hover-in',
|
||||
action_data: 'foo=bar',
|
||||
user_id: 24,
|
||||
user_editcount: 42,
|
||||
module: 'tutorial',
|
||||
is_mobile: false,
|
||||
homepage_pageview_token: 'blah'
|
||||
/* eslint-enable camelcase */
|
||||
} );
|
||||
} );
|
||||
|
||||
} );
|
|
@ -0,0 +1,12 @@
|
|||
QUnit.module( 'ext.growthExperiments.Utils.js', {}, function () {
|
||||
QUnit.test( 'serializeActionData', function ( assert ) {
|
||||
var Utils = require( '../../../modules/utils/ext.growthExperiments.Utils.js' );
|
||||
assert.strictEqual( Utils.serializeActionData( null ), '' );
|
||||
assert.strictEqual( Utils.serializeActionData( { foo: 'bar', blah: 1 } ), 'foo=bar;blah=1' );
|
||||
assert.strictEqual( Utils.serializeActionData( [ 'abc', 'def', 'ghi' ] ), 'abc;def;ghi' );
|
||||
assert.strictEqual( Utils.serializeActionData( '' ), '' );
|
||||
assert.strictEqual( Utils.serializeActionData( 'foo' ), 'foo' );
|
||||
assert.strictEqual( Utils.serializeActionData( 42 ), 42 );
|
||||
assert.strictEqual( Utils.serializeActionData( true ), true );
|
||||
} );
|
||||
} );
|
正在加载...
在新工单中引用