Merge pull request #17530 from overleaf/dp-teardown-onboarding-flow-split-test

Teardown onboarding flow split test

GitOrigin-RevId: 48e95e4e736772074cb68d195fc950a9da3aebcf
This commit is contained in:
David 2024-04-05 09:37:57 +01:00 committed by Copybot
parent 2f74b79d3a
commit b1928cecef
12 changed files with 168 additions and 26 deletions

View file

@ -7,7 +7,6 @@ const fs = require('fs')
const ErrorController = require('../Errors/ErrorController')
const SessionManager = require('../Authentication/SessionManager')
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
const { expressify } = require('@overleaf/promise-utils')
const logger = require('@overleaf/logger')
@ -32,17 +31,11 @@ async function index(req, res) {
async function home(req, res) {
if (Features.hasFeature('homepage') && homepageExists) {
const onboardingFlowAssignment =
await SplitTestHandler.promises.getAssignment(req, res, 'onboarding-flow')
AnalyticsManager.recordEventForSession(req.session, 'home-page-view', {
page: req.path,
})
res.render('external/home/website-redesign/index', {
onboardingFlowVariant: onboardingFlowAssignment.variant,
hideNewsletterCheckbox:
onboardingFlowAssignment.variant === 'token-confirmation-odc',
})
res.render('external/home/website-redesign/index')
} else {
res.redirect('/login')
}

View file

@ -7,7 +7,6 @@ const UserDeleter = require('./UserDeleter')
const UserGetter = require('./UserGetter')
const UserUpdater = require('./UserUpdater')
const Analytics = require('../Analytics/AnalyticsManager')
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
const UserOnboardingEmailManager = require('./UserOnboardingEmailManager')
const UserPostRegistrationAnalyticsManager = require('./UserPostRegistrationAnalyticsManager')
const OError = require('@overleaf/o-error')
@ -37,16 +36,9 @@ async function _addAffiliation(user, affiliationOptions) {
}
async function recordRegistrationEvent(user) {
const onboardingFlowAssignment =
await SplitTestHandler.promises.getAssignmentForUser(
user._id,
'onboarding-flow'
)
try {
const segmentation = {
'home-registration': 'default',
'onboarding-flow': onboardingFlowAssignment.variant,
}
if (user.thirdPartyIdentifiers && user.thirdPartyIdentifiers.length > 0) {
segmentation.provider = user.thirdPartyIdentifiers[0].providerId

View file

@ -945,7 +945,6 @@
"new_tag": "Nyt tag",
"new_tag_name": "Navn til nyt tag",
"newsletter": "Nyhedsbrev",
"newsletter-accept": "Jeg vil gerne modtage e-mails om produkttilbud, virksomhedsnyheder og begivenheder.",
"newsletter_info_note": "Du vil stadig modtage vigtige e-mails såsom projektinvitationer og sikkerhedsbeskeder (nulstilling af kode, kontoforbindelser, osv.).",
"newsletter_info_subscribed": "Du er <0>tilmeldt</0> til __appName__s nyhedsbrev. Hvis du foretrækker ikke at modtage disse e-mails, kan du altid framelde dig.",
"newsletter_info_summary": "Hvert par måneder sender vi et nyhedsbrev som opsummerer de nyeste tilgængelige funktioner.",

View file

@ -937,7 +937,6 @@
"new_snippet_project": "Ohne Titel",
"new_subscription_will_be_billed_immediately": "Dein neues Abonnement wird umgehend mit deiner aktuellen Zahlungsmethode abgerechnet.",
"newsletter": "Newsletter",
"newsletter-accept": "Ich möchte E-Mails zu Produktangeboten, Neuigkeiten und Veranstaltungen des Unternehmens erhalten.",
"newsletter_info_note": "Bitte beachte: Du erhältst weiterhin wichtige E-Mails wie Projekteinladungen und Sicherheitsbenachrichtigungen (Passwortzurücksetzung, Kontoverknüpfung usw.).",
"newsletter_info_subscribed": "Du hast den __appName__-Newsletter <0>abonniert</0>. Wenn du diese E-Mails lieber nicht erhalten möchtest, kannst du dich jederzeit abmelden.",
"newsletter_info_summary": "Alle paar Monate versenden wir einen Newsletter mit einer Zusammenfassung der neu verfügbaren Funktionen.",

View file

@ -1177,7 +1177,6 @@
"new_tag": "New Tag",
"new_tag_name": "New tag name",
"newsletter": "Newsletter",
"newsletter-accept": "Id like emails about product offers and company news and events.",
"newsletter_info_note": "Please note: you will still receive important emails, such as project invites and security notifications (password resets, account linking, etc).",
"newsletter_info_subscribed": "You are currently <0>subscribed</0> to the __appName__ newsletter. If you would prefer not to receive this email then you can unsubscribe at any time.",
"newsletter_info_summary": "Every few months we send a newsletter out summarizing the new features available.",

View file

@ -606,7 +606,6 @@
"new_password": "Nouveau mot de passe",
"new_project": "Nouveau projet",
"new_snippet_project": "Sans titre",
"newsletter-accept": "Je souhaite recevoir des courriels portant sur des offres de produits et sur les actualités et événements de notre entreprise.",
"next_payment_of_x_collectected_on_y": "Le prochain paiement de <0>__paymentAmmount__</0> sera débité le <1>__collectionDate__</1>.",
"nl": "Hollandais",
"no": "Norvégien",

View file

@ -413,7 +413,6 @@
"new_name": "Novo Nome",
"new_password": "Nova Senha",
"new_project": "Novo Projeto",
"newsletter-accept": "Gostaria de receber e-mails sobre ofertas de produtos, notícias e eventos da empresa.",
"next_payment_of_x_collectected_on_y": "O próximo pagamento de <0>__paymentAmmount__</0> será coletado em <1>__collectionDate__</1>",
"nl": "Holandês",
"no": "Noroeguês",

View file

@ -579,7 +579,6 @@
"new_password": "Nytt lösenord",
"new_project": "Nytt projekt",
"new_snippet_project": "Namnlös",
"newsletter-accept": "Jag vill få e-post om produkterbjudanden, nyheter och evenemang från företaget.",
"next_payment_of_x_collectected_on_y": "Nästa betalning på <0>__paymentAmmount__</0> kommer att genomföras den <1>__collectionDate__</1>",
"nl": "Holländska",
"no": "Norska",

View file

@ -1153,7 +1153,6 @@
"new_tag": "新建标签",
"new_tag_name": "新标签名",
"newsletter": "电子邮件",
"newsletter-accept": "我想要关于产品报价和公司新闻和事件的电子邮件。",
"newsletter_info_note": "请注意:您仍然会收到重要的电子邮件,例如项目邀请和安全通知(密码重置、帐户链接等)。",
"newsletter_info_subscribed": "您当前<0>订阅了</0>__appName__ 新闻资讯。 如果您不想收到此电子邮件,则可以随时取消订阅。",
"newsletter_info_summary": "每隔几个月,我们就会发送一份简讯,总结可用的新功能。",

View file

@ -8,6 +8,50 @@ const MockReCAPTCHAApi = require('../mocks/MockReCaptchaApi')
const {
gracefulShutdown,
} = require('../../../../app/src/infrastructure/GracefulShutdown')
const { app } = require('../../../../app/src/infrastructure/Server')
/**
* Inject an endpoint to get the current users session into our app. This
* endpoint should only be available when running in the test environment.
* It is used to retrieve an email confirmation code when registering a
* new user account in acceptance tests.
*/
const addSessionEndpoint = app => {
const stack = app._router.stack
stack.forEach(layer => {
if (layer.name !== 'router' || !layer.handle || !layer.handle.stack) {
return
}
// We want to position our /dev/session endpoint next to the /dev/csrf
// endpoint so we check each router for the presence of this path.
const newRouteIndex = layer.handle.stack.findIndex(
route =>
route &&
route.route &&
route.route.path &&
route.route.path === '/dev/csrf'
)
if (newRouteIndex !== -1) {
// We add our new endpoint to the end of the router stack.
layer.handle.get('/dev/session', (req, res) => {
return res.json(req.session)
})
const routeStack = layer.handle.stack
const sessionRoute = routeStack[routeStack.length - 1]
// Then we reposition it next to the /dev/csrf endpoint.
layer.handle.stack = [
...routeStack.slice(0, newRouteIndex),
sessionRoute,
...routeStack.slice(newRouteIndex, routeStack.length - 1),
]
}
})
}
logger.logger.level('error')
@ -18,6 +62,7 @@ MockReCAPTCHAApi.initialize(2222)
let server
before('start main app', function (done) {
addSessionEndpoint(app)
server = App.listen(23000, 'localhost', done)
})

View file

@ -32,6 +32,46 @@ class User {
})
}
getSession(callback) {
this.request.get(
{
url: '/dev/session',
},
(err, response, body) => {
if (err != null) {
return callback(err)
}
if (response.statusCode !== 200) {
return callback(
new Error(
`get session failed: status=${
response.statusCode
} body=${JSON.stringify(body)}`
)
)
}
const session = JSON.parse(response.body)
callback(null, session)
}
)
}
getEmailConfirmationCode(callback) {
this.getSession((err, session) => {
if (err != null) {
return callback(err)
}
const code = session.pendingUserRegistration?.confirmCode
if (!code) {
return callback(new Error('No confirmation code found in session'))
}
callback(null, code)
})
}
resetCookies() {
this.jar = request.jar()
this.request = request.defaults({
@ -114,12 +154,40 @@ class User {
)
)
}
db.users.findOne({ email: this.email }, (error, user) => {
this.getEmailConfirmationCode((error, code) => {
if (error != null) {
return callback(error)
}
this.setExtraAttributes(user)
callback(null, user)
this.request.post(
{
url: '/registration/confirm-email',
json: { code },
},
(error, response, body) => {
if (error != null) {
return callback(error)
}
if (response.statusCode !== 200) {
return callback(
new Error(
`email confirmation failed: status=${
response.statusCode
} body=${JSON.stringify(body)}`
)
)
}
db.users.findOne({ email: this.email }, (error, user) => {
if (error != null) {
return callback(error)
}
this.setExtraAttributes(user)
callback(null, user)
})
}
)
})
}
)

View file

@ -140,6 +140,33 @@ class UserHelper {
this._csrfToken = body
}
/**
* Requests user session
*/
async getSession() {
const response = await this.fetch('/dev/session')
const body = await response.text()
if (response.status !== 200) {
throw new Error(
`get session failed: status=${response.status} body=${JSON.stringify(
body
)}`
)
}
return JSON.parse(body)
}
async getEmailConfirmationCode() {
const session = await this.getSession()
const code = session.pendingUserRegistration?.confirmCode
if (!code) {
throw new Error('No confirmation code found in session')
}
return code
}
/**
* Make request to POST /logout
* @param {object} [options] options to pass to request
@ -353,6 +380,30 @@ class UserHelper {
`cannot register intitutional email: ${options.json.email}`
)
}
const code = await userHelper.getEmailConfirmationCode()
const confirmationResponse = await userHelper.fetch(
'/registration/confirm-email',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({ code }),
...options,
}
)
if (confirmationResponse.status !== 200) {
throw new Error(
`email confirmation failed: status=${
response.status
} body=${JSON.stringify(body)}`
)
}
userHelper.user = await UserGetter.promises.getUser({
email: userData.email,
})