mirror of
https://gerrit.wikimedia.org/r/mediawiki/skins/MinervaNeue
synced 2024-11-11 16:38:20 +00:00
A/B test bucketing wrapper for page issues AB.
Provides a class that initiates AB-test bucketing and registers as a MF module. Activates the reading depth test for users who are bucketed in either buckets "A" or "B". Does not add event-logging or visual style changes for page issues AB test. Bug: T193584 Change-Id: If8504a35059c6d1b056cef063a595b1c2ffd351a
This commit is contained in:
parent
cacf03525f
commit
7617174d40
|
@ -73,7 +73,9 @@ class MinervaHooks {
|
|||
$testModule = [
|
||||
'dependencies' => [
|
||||
'mobile.startup',
|
||||
'skins.minerva.notifications.badge'
|
||||
'skins.minerva.notifications.badge',
|
||||
'mediawiki.user',
|
||||
'mediawiki.experiments'
|
||||
],
|
||||
'localBasePath' => dirname( __DIR__ ),
|
||||
'remoteSkinPath' => 'MinervaNeue',
|
||||
|
@ -82,9 +84,11 @@ class MinervaHooks {
|
|||
// additional scaffolding (minus initialisation scripts)
|
||||
'resources/skins.minerva.scripts/utils.js',
|
||||
'resources/skins.minerva.scripts/DownloadIcon.js',
|
||||
'resources/skins.minerva.scripts/AB.js',
|
||||
// test files
|
||||
'tests/qunit/skins.minerva.scripts/test_DownloadIcon.js',
|
||||
'tests/qunit/skins.minerva.scripts/test_utils.js',
|
||||
'tests/qunit/skins.minerva.scripts/test_AB.js',
|
||||
'tests/qunit/skins.minerva.notifications.badge/test_NotificationBadge.js'
|
||||
],
|
||||
];
|
||||
|
|
78
resources/skins.minerva.scripts/AB.js
Normal file
78
resources/skins.minerva.scripts/AB.js
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Bucketing wrapper for creating AB-tests.
|
||||
*
|
||||
* Given a test name, sampling rate, and session ID, provides a class
|
||||
* that buckets users into predefined bucket ("control", "A", "B") and
|
||||
* starts an AB-test.
|
||||
*/
|
||||
|
||||
( function ( mw, M, mwExperiments ) {
|
||||
|
||||
/**
|
||||
* Buckets users based on params and exposes an `isEnabled` and `getBucket` method.
|
||||
*
|
||||
* @param {string} testName name of the AB-test.
|
||||
* @param {number} samplingRate sampling rate for the AB-test.
|
||||
* @param {number} sessionId session ID for user bucketing.
|
||||
* @constructor
|
||||
*/
|
||||
function AB( testName, samplingRate, sessionId ) {
|
||||
|
||||
var CONTROL_BUCKET = 'control',
|
||||
test = {
|
||||
name: testName,
|
||||
enabled: !!samplingRate,
|
||||
buckets: {
|
||||
control: 1 - samplingRate,
|
||||
A: samplingRate / 2,
|
||||
B: samplingRate / 2
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Starts the AB-test and enters the user into the Reading Depth test.
|
||||
*/
|
||||
function startABTest() {
|
||||
// See: https://gerrit.wikimedia.org/r/#/c/mediawiki/extensions/WikimediaEvents/+/437686/
|
||||
mw.track( 'wikimedia.event.ReadingDepthSchema.enable' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the users AB-test bucket
|
||||
*
|
||||
* @return {string} AB-test bucket, CONTROL_BUCKET by default, "A" or "B" buckets otherwise.
|
||||
*/
|
||||
function getBucket() {
|
||||
return mwExperiments.getBucket( test, sessionId );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not a user is in the AB-test,
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isEnabled() {
|
||||
return getBucket() !== CONTROL_BUCKET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the AB-test.
|
||||
*
|
||||
* return {void}
|
||||
*/
|
||||
function init() {
|
||||
if ( isEnabled() ) {
|
||||
startABTest();
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
return {
|
||||
getBucket: getBucket,
|
||||
isEnabled: isEnabled
|
||||
};
|
||||
}
|
||||
|
||||
M.define( 'skins.minerva.scripts/AB', AB );
|
||||
|
||||
}( mw, mw.mobileFrontend, mw.experiments ) );
|
|
@ -328,7 +328,10 @@
|
|||
"mobile.issues",
|
||||
"mobile.search.api",
|
||||
"mobile.search",
|
||||
"mobile.references"
|
||||
"mobile.references",
|
||||
"mediawiki.user",
|
||||
"mediawiki.storage",
|
||||
"mediawiki.experiments"
|
||||
],
|
||||
"messages": [
|
||||
"edithelp",
|
||||
|
@ -355,6 +358,7 @@
|
|||
"resources/skins.minerva.scripts/search.js",
|
||||
"resources/skins.minerva.scripts/references.js",
|
||||
"resources/skins.minerva.scripts/utils.js",
|
||||
"resources/skins.minerva.scripts/AB.js",
|
||||
"resources/skins.minerva.scripts/cleanuptemplates.js"
|
||||
]
|
||||
},
|
||||
|
|
40
tests/qunit/skins.minerva.scripts/test_AB.js
Normal file
40
tests/qunit/skins.minerva.scripts/test_AB.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
( function ( M ) {
|
||||
|
||||
var AB = M.require( 'skins.minerva.scripts/AB' ),
|
||||
aBName = 'WME.MinervaABTest',
|
||||
samplingRate = 0.5;
|
||||
|
||||
QUnit.module( 'Minerva AB-test' );
|
||||
|
||||
QUnit.test( 'Bucketing test', function ( assert ) {
|
||||
var userBuckets = {
|
||||
control: 0,
|
||||
A: 0,
|
||||
B: 0
|
||||
},
|
||||
maxUsers = 1000,
|
||||
bucketingTest,
|
||||
i;
|
||||
|
||||
for ( i = 0; i < maxUsers; i++ ) {
|
||||
bucketingTest = new AB( aBName, samplingRate, mw.user.generateRandomSessionId() );
|
||||
userBuckets[ bucketingTest.getBucket() ] += 1;
|
||||
}
|
||||
|
||||
assert.strictEqual(
|
||||
( userBuckets.control / maxUsers > 0.4 ) &&
|
||||
( userBuckets.control / maxUsers < 0.6 ),
|
||||
true, 'test control group is about 50% (' + userBuckets.control / 10 + '%)' );
|
||||
|
||||
assert.strictEqual(
|
||||
( userBuckets.A / maxUsers > 0.2 ) &&
|
||||
( userBuckets.A / maxUsers < 0.3 ),
|
||||
true, 'test group A is about 25% (' + userBuckets.A / 10 + '%)' );
|
||||
|
||||
assert.strictEqual(
|
||||
( userBuckets.B / maxUsers > 0.2 ) &&
|
||||
( userBuckets.B / maxUsers < 0.3 ),
|
||||
true, 'test group B is about 25% (' + userBuckets.B / 10 + '%)' );
|
||||
} );
|
||||
|
||||
}( mw.mobileFrontend ) );
|
Loading…
Reference in a new issue