Przeglądaj źródła

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

Setup null split tests

GitOrigin-RevId: 4cba55e123d0a4add19cdace7434506e9d20c7a9
Alexandre Bourdin 4 lat temu
rodzic
commit
f9873619ad

+ 33 - 6
services/web/app/src/Features/Project/ProjectController.js

@@ -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)
+              }
             }
             }
           )
           )
         },
         },

+ 18 - 0
services/web/app/src/Features/SplitTests/SplitTestV2Handler.js

@@ -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,

+ 5 - 0
services/web/frontend/js/features/share-project-modal/components/share-modal-body.js

@@ -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']) {

+ 12 - 5
services/web/test/unit/src/Project/ProjectControllerTests.js

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