Merge pull request #5769 from overleaf/ab-null-split-tests

Setup null split tests

GitOrigin-RevId: 4cba55e123d0a4add19cdace7434506e9d20c7a9
This commit is contained in:
Alexandre Bourdin 2021-11-22 11:35:10 +01:00 committed by Copybot
parent fa6bc3fc7b
commit f9873619ad
4 changed files with 68 additions and 11 deletions

View file

@ -611,6 +611,12 @@ const ProjectController = {
: undefined : undefined
} }
// null test targeting logged in users
SplitTestV2Handler.promises.getAssignmentForSession(
req.session,
'null-test-dashboard'
)
res.render('project/list', viewModel) res.render('project/list', viewModel)
timer.done() timer.done()
}) })
@ -724,12 +730,27 @@ const ProjectController = {
TpdsProjectFlusher.flushProjectToTpdsIfNeeded(projectId, cb) TpdsProjectFlusher.flushProjectToTpdsIfNeeded(projectId, cb)
}, },
sharingModalSplitTest(cb) { sharingModalSplitTest(cb) {
SplitTestV2Handler.assignInLocalsContext( SplitTestV2Handler.assignInLocalsContextForSession(
res, res,
userId, req.session,
'project-share-modal-paywall', 'project-share-modal-paywall',
err => { {},
cb(err, null) () => {
// do not fail editor load if assignment fails
cb()
}
)
},
sharingModalNullTest(cb) {
// null test targeting logged in users, for front-end side
SplitTestV2Handler.assignInLocalsContextForSession(
res,
req.session,
'null-test-share-modal',
{},
() => {
// do not fail editor load if assignment fails
cb()
} }
) )
}, },
@ -737,8 +758,14 @@ const ProjectController = {
SplitTestV2Handler.getAssignmentForSession( SplitTestV2Handler.getAssignmentForSession(
req.session, req.session,
'react-pdf-preview-rollout', 'react-pdf-preview-rollout',
(err, assignment) => { {},
cb(err, assignment) (error, assignment) => {
if (error) {
// do not fail editor load if assignment fails
cb(null, { variant: 'default' })
} else {
cb(null, assignment)
}
} }
) )
}, },

View file

@ -78,6 +78,15 @@ async function getAssignmentForSession(session, splitTestName, options) {
return _getAssignment(analyticsId, userId, session, splitTestName, options) return _getAssignment(analyticsId, userId, session, splitTestName, options)
} }
/**
* Get the assignment of a user to a split test by their ID and stores it in the locals context.
*
* @param res the Express response object
* @param userId the user ID
* @param splitTestName the unique name of the split test
* @param options {Object<sync: boolean>} - for test purposes only, to force the synchronous update of the user's profile
* @returns {Promise<void>}
*/
async function assignInLocalsContext(res, userId, splitTestName, options) { async function assignInLocalsContext(res, userId, splitTestName, options) {
const assignment = await getAssignment(userId, splitTestName, options) const assignment = await getAssignment(userId, splitTestName, options)
if (!res.locals.splitTestVariants) { if (!res.locals.splitTestVariants) {
@ -86,6 +95,15 @@ async function assignInLocalsContext(res, userId, splitTestName, options) {
res.locals.splitTestVariants[splitTestName] = assignment.variant res.locals.splitTestVariants[splitTestName] = assignment.variant
} }
/**
* Get the assignment of a user to a split test by their session and stores it in the locals context.
*
* @param res the Express response object
* @param session the request session
* @param splitTestName the unique name of the split test
* @param options {Object<sync: boolean>} - for test purposes only, to force the synchronous update of the user's profile
* @returns {Promise<void>}
*/
async function assignInLocalsContextForSession( async function assignInLocalsContextForSession(
res, res,
session, session,

View file

@ -11,6 +11,7 @@ import { useSplitTestContext } from '../../../shared/context/split-test-context'
import { Row } from 'react-bootstrap' import { Row } from 'react-bootstrap'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import RecaptchaConditions from '../../../shared/components/recaptcha-conditions' import RecaptchaConditions from '../../../shared/components/recaptcha-conditions'
import * as eventTracking from '../../../infrastructure/event-tracking'
export default function ShareModalBody() { export default function ShareModalBody() {
const { isAdmin } = useShareProjectContext() const { isAdmin } = useShareProjectContext()
@ -18,6 +19,10 @@ export default function ShareModalBody() {
splitTestVariants: PropTypes.object, splitTestVariants: PropTypes.object,
}) })
eventTracking.sendMB('share-modal-opened', {
splitTestVariant: splitTestVariants['null-test-share-modal'],
})
const project = useProjectContext() const project = useProjectContext()
switch (splitTestVariants['project-share-modal-paywall']) { switch (splitTestVariants['project-share-modal-paywall']) {

View file

@ -136,14 +136,21 @@ describe('ProjectController', function () {
} }
this.SplitTestV2Handler = { this.SplitTestV2Handler = {
promises: { promises: {
getAssignment: sinon.stub().resolves({ active: false }), getAssignment: sinon.stub().resolves({ variant: 'default' }),
assignInLocalsContext: sinon.stub().resolves(), getAssignmentForSession: sinon.stub().resolves({ variant: 'default' }),
assignInLocalsContext: sinon.stub().resolves({ variant: 'default' }),
assignInLocalsContextForSession: sinon
.stub()
.resolves({ variant: 'default' }),
}, },
getAssignment: sinon.stub().yields(null, { variant: 'default' }),
getAssignmentForSession: sinon getAssignmentForSession: sinon
.stub() .stub()
.yields(null, { variant: 'variant' }), .yields(null, { variant: 'default' }),
getAssignment: sinon.stub().yields(null, { active: false }), assignInLocalsContext: sinon.stub().yields(null, { variant: 'default' }),
assignInLocalsContext: sinon.stub().yields(null), assignInLocalsContextForSession: sinon
.stub()
.yields(null, { variant: 'default' }),
} }
this.ProjectController = SandboxedModule.require(MODULE_PATH, { this.ProjectController = SandboxedModule.require(MODULE_PATH, {