From 73ad2ee3dba71e63e5cf91ac432fb7b9691b84f8 Mon Sep 17 00:00:00 2001 From: Alexandre Bourdin Date: Wed, 2 Jul 2025 11:33:12 +0200 Subject: [PATCH] Merge pull request #26699 from overleaf/ab-survey-exclude-labs-users-option Add an option to hide a survey from Labs users GitOrigin-RevId: d6f87bb31221d8db38f8506afa7cc3313ad235ce --- .../app/src/Features/Survey/SurveyHandler.mjs | 13 ++++++-- services/web/app/src/models/Survey.js | 4 +++ .../web/test/acceptance/src/helpers/User.mjs | 31 ++++++++++++++----- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/services/web/app/src/Features/Survey/SurveyHandler.mjs b/services/web/app/src/Features/Survey/SurveyHandler.mjs index db857d9fd3..bcb9c4bee0 100644 --- a/services/web/app/src/Features/Survey/SurveyHandler.mjs +++ b/services/web/app/src/Features/Survey/SurveyHandler.mjs @@ -34,9 +34,13 @@ async function getSurvey(userId) { return } - const { earliestSignupDate, latestSignupDate } = survey.options || {} - if (earliestSignupDate || latestSignupDate) { - const user = await UserGetter.promises.getUser(userId, { signUpDate: 1 }) + const { earliestSignupDate, latestSignupDate, excludeLabsUsers } = + survey.options || {} + if (earliestSignupDate || latestSignupDate || excludeLabsUsers) { + const user = await UserGetter.promises.getUser(userId, { + signUpDate: 1, + labsProgram: 1, + }) if (!user) { return } @@ -51,6 +55,9 @@ async function getSurvey(userId) { if (earliestSignupDate && signUpDate < earliestSignupDate) { return } + if (excludeLabsUsers && user.labsProgram) { + return + } } return { name, title, text, cta, url } diff --git a/services/web/app/src/models/Survey.js b/services/web/app/src/models/Survey.js index bd1582a08b..01068ef49a 100644 --- a/services/web/app/src/models/Survey.js +++ b/services/web/app/src/models/Survey.js @@ -50,6 +50,10 @@ const SurveySchema = new Schema( type: Number, default: 100, }, + excludeLabsUsers: { + type: Boolean, + default: false, + }, }, }, { diff --git a/services/web/test/acceptance/src/helpers/User.mjs b/services/web/test/acceptance/src/helpers/User.mjs index 361466e259..4371ecbe49 100644 --- a/services/web/test/acceptance/src/helpers/User.mjs +++ b/services/web/test/acceptance/src/helpers/User.mjs @@ -10,6 +10,7 @@ import fs from 'node:fs' import Path from 'node:path' import { fileURLToPath } from 'node:url' import { Cookie } from 'tough-cookie' + const __dirname = fileURLToPath(new URL('.', import.meta.url)) const COOKIE_DOMAIN = settings.cookieDomain // The cookie domain has a leading '.' but the cookie jar stores it without. @@ -37,6 +38,7 @@ class User { jar: this.jar, }) this.signUpDate = options.signUpDate ?? new Date() + this.labsProgram = options.labsProgram || false } getSession(options, callback) { @@ -257,13 +259,19 @@ class User { getAuditLog(callback) { this.get((error, user) => { - if (error) return callback(error) - if (!user) return callback(new Error('User not found')) + if (error) { + return callback(error) + } + if (!user) { + return callback(new Error('User not found')) + } db.userAuditLogEntries .find({ userId: new ObjectId(this._id) }) .toArray((error, auditLog) => { - if (error) return callback(error) + if (error) { + return callback(error) + } callback(null, auditLog || []) }) }) @@ -271,7 +279,9 @@ class User { getAuditLogWithoutNoise(callback) { this.getAuditLog((error, auditLog) => { - if (error) return callback(error) + if (error) { + return callback(error) + } callback( null, auditLog.filter(entry => { @@ -413,7 +423,9 @@ class User { } ensureUserExists(callback) { - if (this._id) return callback() // already exists + if (this._id) { + return callback() + } // already exists const filter = { email: this.email } const options = { upsert: true, new: true, setDefaultsOnInsert: true } @@ -431,6 +443,7 @@ class User { hashedPassword, emails: this.emails, signUpDate: this.signUpDate, + labsProgram: this.labsProgram, }, }, options @@ -447,7 +460,9 @@ class User { // Update and persist feature upgrade. Downgrades will be flaky! upgradeFeatures(features, callback) { this.setFeatures(features, err => { - if (err) return callback(err) + if (err) { + return callback(err) + } // Persist the feature update, otherwise the next feature refresh will reset them. this.setFeaturesOverride( { @@ -1195,7 +1210,9 @@ class User { json: newSettings, }, (err, response, body) => { - if (err) return callback(err) + if (err) { + return callback(err) + } if (response.statusCode !== 200) { return callback( new Error(