Просмотр исходного кода

[web] Convert some Features files to ES modules (part 2) (#28275)

* Rename files

* Rename test files

* Convert to ESM

GitOrigin-RevId: b0ee442ac8edd4ef3695f93a91ffd9521e6bf259
Antoine Clausse 11 месяцев назад
Родитель
Сommit
723954ccc2
29 измененных файлов с 908 добавлено и 833 удалено
  1. 1 1
      services/web/app/src/Features/Exports/ExportsHandler.mjs
  2. 5 5
      services/web/app/src/Features/Project/ProjectEntityRestoreHandler.mjs
  3. 6 6
      services/web/app/src/Features/Project/ProjectHistoryHandler.mjs
  4. 5 5
      services/web/app/src/Features/Referal/ReferalAllocator.mjs
  5. 9 9
      services/web/app/src/Features/ServerAdmin/AdminController.mjs
  6. 6 6
      services/web/app/src/Features/SplitTests/SlackNotificationManager.mjs
  7. 5 5
      services/web/app/src/Features/SplitTests/SplitTestMiddleware.mjs
  8. 1 1
      services/web/app/src/Features/Subscription/GroupUtils.mjs
  9. 1 1
      services/web/app/src/Features/Subscription/SubscriptionGroupController.mjs
  10. 22 19
      services/web/app/src/Features/Subscription/SubscriptionGroupHandler.mjs
  11. 1 1
      services/web/app/src/Features/Survey/SurveyCache.mjs
  12. 3 3
      services/web/app/src/Features/Survey/SurveyManager.mjs
  13. 4 4
      services/web/app/src/Features/SystemMessages/SystemMessageController.mjs
  14. 2 2
      services/web/app/src/router.mjs
  15. 1 1
      services/web/scripts/add_subscription_members_csv.mjs
  16. 1 1
      services/web/scripts/disconnect_all_users.mjs
  17. 1 1
      services/web/scripts/recover_docs_from_redis.mjs
  18. 1 1
      services/web/scripts/restore_orphaned_docs.mjs
  19. 1 1
      services/web/scripts/restore_soft_deleted_docs.mjs
  20. 4 4
      services/web/test/acceptance/src/helpers/groupSSO.mjs
  21. 105 0
      services/web/test/unit/src/Project/ProjectEntityRestoreHandler.test.mjs
  22. 0 97
      services/web/test/unit/src/Project/ProjectEntityRestoreHandlerTests.js
  23. 166 0
      services/web/test/unit/src/Project/ProjectHistoryHandler.test.mjs
  24. 0 163
      services/web/test/unit/src/Project/ProjectHistoryHandlerTests.js
  25. 138 0
      services/web/test/unit/src/Referal/ReferalAllocator.test.mjs
  26. 0 133
      services/web/test/unit/src/Referal/ReferalAllocatorTests.js
  27. 89 0
      services/web/test/unit/src/SplitTests/SplitTestMiddleware.test.mjs
  28. 0 89
      services/web/test/unit/src/SplitTests/SplitTestMiddlewareTests.js
  29. 330 274
      services/web/test/unit/src/Subscription/SubscriptionGroupHandler.test.mjs

+ 1 - 1
services/web/app/src/Features/Exports/ExportsHandler.mjs

@@ -13,7 +13,7 @@
  */
 import OError from '@overleaf/o-error'
 import ProjectGetter from '../Project/ProjectGetter.js'
-import ProjectHistoryHandler from '../Project/ProjectHistoryHandler.js'
+import ProjectHistoryHandler from '../Project/ProjectHistoryHandler.mjs'
 import ProjectLocator from '../Project/ProjectLocator.js'
 import ProjectRootDocManager from '../Project/ProjectRootDocManager.js'
 import UserGetter from '../User/UserGetter.js'

+ 5 - 5
services/web/app/src/Features/Project/ProjectEntityRestoreHandler.js → services/web/app/src/Features/Project/ProjectEntityRestoreHandler.mjs

@@ -1,7 +1,7 @@
-const { callbackify } = require('util')
-const Path = require('path')
-const ProjectEntityHandler = require('./ProjectEntityHandler')
-const EditorController = require('../Editor/EditorController')
+import { callbackify } from 'node:util'
+import Path from 'node:path'
+import ProjectEntityHandler from './ProjectEntityHandler.js'
+import EditorController from '../Editor/EditorController.js'
 
 // generate a new name based on the original, with an optional label.
 // e.g. origname-20210101-122345.tex          (default)
@@ -36,7 +36,7 @@ async function restoreDeletedDoc(projectId, docId, docName, userId) {
   )
 }
 
-module.exports = {
+export default {
   restoreDeletedDoc: callbackify(restoreDeletedDoc),
   generateRestoredName,
   promises: {

+ 6 - 6
services/web/app/src/Features/Project/ProjectHistoryHandler.js → services/web/app/src/Features/Project/ProjectHistoryHandler.mjs

@@ -1,8 +1,8 @@
-const { Project } = require('../../models/Project')
-const ProjectDetailsHandler = require('./ProjectDetailsHandler')
-const HistoryManager = require('../History/HistoryManager')
-const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
-const { callbackify } = require('util')
+import { Project } from '../../models/Project.js'
+import ProjectDetailsHandler from './ProjectDetailsHandler.js'
+import HistoryManager from '../History/HistoryManager.js'
+import ProjectEntityUpdateHandler from './ProjectEntityUpdateHandler.js'
+import { callbackify } from 'node:util'
 
 const ProjectHistoryHandler = {
   async setHistoryId(projectId, historyId) {
@@ -55,7 +55,7 @@ const ProjectHistoryHandler = {
   },
 }
 
-module.exports = {
+export default {
   setHistoryId: callbackify(ProjectHistoryHandler.setHistoryId),
   getHistoryId: callbackify(ProjectHistoryHandler.getHistoryId),
   ensureHistoryExistsForProject: callbackify(

+ 5 - 5
services/web/app/src/Features/Referal/ReferalAllocator.js → services/web/app/src/Features/Referal/ReferalAllocator.mjs

@@ -1,7 +1,7 @@
-const OError = require('@overleaf/o-error')
-const { User } = require('../../models/User')
-const FeaturesUpdater = require('../Subscription/FeaturesUpdater')
-const { callbackify } = require('@overleaf/promise-utils')
+import OError from '@overleaf/o-error'
+import { User } from '../../models/User.js'
+import FeaturesUpdater from '../Subscription/FeaturesUpdater.js'
+import { callbackify } from '@overleaf/promise-utils'
 
 async function allocate(referalId, newUserId, referalSource, referalMedium) {
   if (referalId == null) {
@@ -40,7 +40,7 @@ async function allocate(referalId, newUserId, referalSource, referalMedium) {
   }
 }
 
-module.exports = {
+export default {
   allocate: callbackify(allocate),
   promises: {
     allocate,

+ 9 - 9
services/web/app/src/Features/ServerAdmin/AdminController.js → services/web/app/src/Features/ServerAdmin/AdminController.mjs

@@ -1,11 +1,11 @@
-const logger = require('@overleaf/logger')
-const http = require('http')
-const https = require('https')
-const Settings = require('@overleaf/settings')
-const TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
-const TpdsProjectFlusher = require('../ThirdPartyDataStore/TpdsProjectFlusher')
-const EditorRealTimeController = require('../Editor/EditorRealTimeController')
-const SystemMessageManager = require('../SystemMessages/SystemMessageManager')
+import logger from '@overleaf/logger'
+import http from 'node:http'
+import https from 'node:https'
+import Settings from '@overleaf/settings'
+import TpdsUpdateSender from '../ThirdPartyDataStore/TpdsUpdateSender.js'
+import TpdsProjectFlusher from '../ThirdPartyDataStore/TpdsProjectFlusher.js'
+import EditorRealTimeController from '../Editor/EditorRealTimeController.js'
+import SystemMessageManager from '../SystemMessages/SystemMessageManager.js'
 
 const AdminController = {
   _sendDisconnectAllUsersMessage: delay => {
@@ -94,4 +94,4 @@ const AdminController = {
   },
 }
 
-module.exports = AdminController
+export default AdminController

+ 6 - 6
services/web/app/src/Features/SplitTests/SlackNotificationManager.js → services/web/app/src/Features/SplitTests/SlackNotificationManager.mjs

@@ -1,8 +1,8 @@
-const logger = require('@overleaf/logger')
-const Settings = require('@overleaf/settings')
-const { IncomingWebhook } = require('@slack/webhook')
-const moment = require('moment')
-const SplitTestUtils = require('./SplitTestUtils')
+import logger from '@overleaf/logger'
+import Settings from '@overleaf/settings'
+import { IncomingWebhook } from '@slack/webhook'
+import moment from 'moment'
+import SplitTestUtils from './SplitTestUtils.js'
 
 async function sendNotification(splitTest, action, user) {
   const lastVersion = SplitTestUtils.getCurrentVersion(splitTest)
@@ -60,6 +60,6 @@ async function sendNotification(splitTest, action, user) {
   }
 }
 
-module.exports = {
+export default {
   sendNotification,
 }

+ 5 - 5
services/web/app/src/Features/SplitTests/SplitTestMiddleware.js → services/web/app/src/Features/SplitTests/SplitTestMiddleware.mjs

@@ -1,7 +1,7 @@
-const SplitTestHandler = require('./SplitTestHandler')
-const logger = require('@overleaf/logger')
-const { expressify } = require('@overleaf/promise-utils')
-const Errors = require('../Errors/Errors')
+import SplitTestHandler from './SplitTestHandler.js'
+import logger from '@overleaf/logger'
+import { expressify } from '@overleaf/promise-utils'
+import Errors from '../Errors/Errors.js'
 
 function loadAssignmentsInLocals(splitTestNames) {
   return async function (req, res, next) {
@@ -43,7 +43,7 @@ function ensureSplitTestEnabledForUser(
   })
 }
 
-module.exports = {
+export default {
   loadAssignmentsInLocals,
   ensureSplitTestEnabledForUser,
 }

+ 1 - 1
services/web/app/src/Features/Subscription/GroupUtils.js → services/web/app/src/Features/Subscription/GroupUtils.mjs

@@ -9,6 +9,6 @@ function getProviderId(subscriptionId) {
   return `ol-group-subscription-id:${subscriptionId.toString()}`
 }
 
-module.exports = {
+export default {
   getProviderId,
 }

+ 1 - 1
services/web/app/src/Features/Subscription/SubscriptionGroupController.mjs

@@ -1,4 +1,4 @@
-import SubscriptionGroupHandler from './SubscriptionGroupHandler.js'
+import SubscriptionGroupHandler from './SubscriptionGroupHandler.mjs'
 
 import OError from '@overleaf/o-error'
 import logger from '@overleaf/logger'

+ 22 - 19
services/web/app/src/Features/Subscription/SubscriptionGroupHandler.js → services/web/app/src/Features/Subscription/SubscriptionGroupHandler.mjs

@@ -1,26 +1,29 @@
-const { callbackify } = require('util')
-const _ = require('lodash')
-const OError = require('@overleaf/o-error')
-const SubscriptionUpdater = require('./SubscriptionUpdater')
-const SubscriptionLocator = require('./SubscriptionLocator')
-const SubscriptionController = require('./SubscriptionController')
-const SubscriptionHelper = require('./SubscriptionHelper')
-const { Subscription } = require('../../models/Subscription')
-const { User } = require('../../models/User')
-const PlansLocator = require('./PlansLocator')
-const TeamInvitesHandler = require('./TeamInvitesHandler')
-const GroupPlansData = require('./GroupPlansData')
-const Modules = require('../../infrastructure/Modules')
-const { MEMBERS_LIMIT_ADD_ON_CODE } = require('./PaymentProviderEntities')
-const {
+import { callbackify } from 'node:util'
+import _ from 'lodash'
+import OError from '@overleaf/o-error'
+import SubscriptionUpdater from './SubscriptionUpdater.js'
+import SubscriptionLocator from './SubscriptionLocator.js'
+import SubscriptionController from './SubscriptionController.js'
+import SubscriptionHelper from './SubscriptionHelper.js'
+import { Subscription } from '../../models/Subscription.js'
+import { User } from '../../models/User.js'
+import PlansLocator from './PlansLocator.js'
+import TeamInvitesHandler from './TeamInvitesHandler.js'
+import GroupPlansData from './GroupPlansData.js'
+import Modules from '../../infrastructure/Modules.js'
+import PaymentProviderEntities from './PaymentProviderEntities.js'
+import {
   ManuallyCollectedError,
   PendingChangeError,
   InactiveError,
   HasPastDueInvoiceError,
   HasNoAdditionalLicenseWhenManuallyCollectedError,
-} = require('./Errors')
-const EmailHelper = require('../Helpers/EmailHelper')
-const { InvalidEmailError } = require('../Errors/Errors')
+} from './Errors.js'
+import EmailHelper from '../Helpers/EmailHelper.js'
+import { InvalidEmailError } from '../Errors/Errors.js'
+
+const MEMBERS_LIMIT_ADD_ON_CODE =
+  PaymentProviderEntities.MEMBERS_LIMIT_ADD_ON_CODE
 
 async function removeUserFromGroup(subscriptionId, userIdToRemove, auditLog) {
   await SubscriptionUpdater.promises.removeUserFromGroup(
@@ -470,7 +473,7 @@ async function updateGroupMembersBulk(
   return result
 }
 
-module.exports = {
+export default {
   removeUserFromGroup: callbackify(removeUserFromGroup),
   replaceUserReferencesInGroups: callbackify(replaceUserReferencesInGroups),
   ensureFlexibleLicensingEnabled: callbackify(ensureFlexibleLicensingEnabled),

+ 1 - 1
services/web/app/src/Features/Survey/SurveyCache.mjs

@@ -1,4 +1,4 @@
-import SurveyManager from './SurveyManager.js'
+import SurveyManager from './SurveyManager.mjs'
 import { Survey } from '../../models/Survey.js'
 import { CacheLoader } from 'cache-flow'
 

+ 3 - 3
services/web/app/src/Features/Survey/SurveyManager.js → services/web/app/src/Features/Survey/SurveyManager.mjs

@@ -1,5 +1,5 @@
-const { Survey } = require('../../models/Survey')
-const OError = require('@overleaf/o-error')
+import { Survey } from '../../models/Survey.js'
+import OError from '@overleaf/o-error'
 
 async function getSurvey() {
   try {
@@ -67,7 +67,7 @@ async function deleteSurvey() {
   }
 }
 
-module.exports = {
+export default {
   getSurvey,
   updateSurvey,
   deleteSurvey,

+ 4 - 4
services/web/app/src/Features/SystemMessages/SystemMessageController.js → services/web/app/src/Features/SystemMessages/SystemMessageController.mjs

@@ -1,6 +1,6 @@
-const Settings = require('@overleaf/settings')
-const SessionManager = require('../Authentication/SessionManager')
-const SystemMessageManager = require('./SystemMessageManager')
+import Settings from '@overleaf/settings'
+import SessionManager from '../Authentication/SessionManager.js'
+import SystemMessageManager from './SystemMessageManager.js'
 
 const ProjectController = {
   getMessages(req, res, next) {
@@ -24,4 +24,4 @@ const ProjectController = {
   },
 }
 
-module.exports = ProjectController
+export default ProjectController

+ 2 - 2
services/web/app/src/router.mjs

@@ -1,4 +1,4 @@
-import AdminController from './Features/ServerAdmin/AdminController.js'
+import AdminController from './Features/ServerAdmin/AdminController.mjs'
 import ErrorController from './Features/Errors/ErrorController.mjs'
 import Features from './infrastructure/Features.js'
 import ProjectController from './Features/Project/ProjectController.mjs'
@@ -54,7 +54,7 @@ import TokenAccessRouter from './Features/TokenAccess/TokenAccessRouter.mjs'
 import LinkedFilesRouter from './Features/LinkedFiles/LinkedFilesRouter.mjs'
 import TemplatesRouter from './Features/Templates/TemplatesRouter.js'
 import UserMembershipRouter from './Features/UserMembership/UserMembershipRouter.mjs'
-import SystemMessageController from './Features/SystemMessages/SystemMessageController.js'
+import SystemMessageController from './Features/SystemMessages/SystemMessageController.mjs'
 import AnalyticsRegistrationSourceMiddleware from './Features/Analytics/AnalyticsRegistrationSourceMiddleware.js'
 import AnalyticsUTMTrackingMiddleware from './Features/Analytics/AnalyticsUTMTrackingMiddleware.mjs'
 import CaptchaMiddleware from './Features/Captcha/CaptchaMiddleware.mjs'

+ 1 - 1
services/web/scripts/add_subscription_members_csv.mjs

@@ -2,7 +2,7 @@ import fs from 'node:fs'
 import minimist from 'minimist'
 import { parse } from 'csv'
 import Stream from 'node:stream/promises'
-import SubscriptionGroupHandler from '../app/src/Features/Subscription/SubscriptionGroupHandler.js'
+import SubscriptionGroupHandler from '../app/src/Features/Subscription/SubscriptionGroupHandler.mjs'
 import { Subscription } from '../app/src/models/Subscription.js'
 import { InvalidEmailError } from '../app/src/Features/Errors/Errors.js'
 

+ 1 - 1
services/web/scripts/disconnect_all_users.mjs

@@ -1,6 +1,6 @@
 import { promisify } from 'node:util'
 import Settings from '@overleaf/settings'
-import AdminController from '../app/src/Features/ServerAdmin/AdminController.js'
+import AdminController from '../app/src/Features/ServerAdmin/AdminController.mjs'
 import minimist from 'minimist'
 import { fileURLToPath } from 'node:url'
 import { scriptRunner } from './lib/ScriptRunner.mjs'

+ 1 - 1
services/web/scripts/recover_docs_from_redis.mjs

@@ -4,7 +4,7 @@ import minimist from 'minimist'
 
 import { db, ObjectId } from '../app/src/infrastructure/mongodb.js'
 import ProjectEntityUpdateHandler from '../app/src/Features/Project/ProjectEntityUpdateHandler.js'
-import ProjectEntityRestoreHandler from '../app/src/Features/Project/ProjectEntityRestoreHandler.js'
+import ProjectEntityRestoreHandler from '../app/src/Features/Project/ProjectEntityRestoreHandler.mjs'
 import RedisWrapper from '@overleaf/redis-wrapper'
 import Settings from '@overleaf/settings'
 import logger from '@overleaf/logger'

+ 1 - 1
services/web/scripts/restore_orphaned_docs.mjs

@@ -1,4 +1,4 @@
-import ProjectEntityRestoreHandler from '../app/src/Features/Project/ProjectEntityRestoreHandler.js'
+import ProjectEntityRestoreHandler from '../app/src/Features/Project/ProjectEntityRestoreHandler.mjs'
 import ProjectEntityHandler from '../app/src/Features/Project/ProjectEntityHandler.js'
 import DocstoreManager from '../app/src/Features/Docstore/DocstoreManager.js'
 import { scriptRunner } from './lib/ScriptRunner.mjs'

+ 1 - 1
services/web/scripts/restore_soft_deleted_docs.mjs

@@ -1,4 +1,4 @@
-import ProjectEntityRestoreHandler from '../app/src/Features/Project/ProjectEntityRestoreHandler.js'
+import ProjectEntityRestoreHandler from '../app/src/Features/Project/ProjectEntityRestoreHandler.mjs'
 import DocstoreManager from '../app/src/Features/Docstore/DocstoreManager.js'
 import { scriptRunner } from './lib/ScriptRunner.mjs'
 

+ 4 - 4
services/web/test/acceptance/src/helpers/groupSSO.mjs

@@ -6,7 +6,7 @@ import { SSOConfig } from '../../../../app/src/models/SSOConfig.js'
 import UserHelper from './UserHelper.mjs'
 import SAMLHelper from './SAMLHelper.mjs'
 import Settings from '@overleaf/settings'
-import { getProviderId } from '../../../../app/src/Features/Subscription/GroupUtils.js'
+import GroupUtils from '../../../../app/src/Features/Subscription/GroupUtils.mjs'
 import UserGetter from '../../../../app/src/Features/User/UserGetter.js'
 import { fileURLToPath } from 'node:url'
 import { Subscription as SubscriptionModel } from '../../../../app/src/models/Subscription.js'
@@ -75,7 +75,7 @@ export async function createGroupSSO(
   await subscription.ensureExists()
   const subscriptionId = subscription._id.toString()
   const enrollmentUrl = getEnrollmentUrl(subscriptionId)
-  const internalProviderId = getProviderId(subscriptionId)
+  const internalProviderId = GroupUtils.getProviderId(subscriptionId)
 
   if (SSOConfigValidated) {
     await linkGroupMember(
@@ -122,7 +122,7 @@ export async function linkGroupMember(
     .exec()
   const userIdAttribute = subscription?.ssoConfig?.userIdAttribute
 
-  const internalProviderId = getProviderId(groupId)
+  const internalProviderId = GroupUtils.getProviderId(groupId)
   const enrollmentUrl = getEnrollmentUrl(groupId)
   const userHelper = await UserHelper.loginUser(
     {
@@ -189,7 +189,7 @@ export async function linkGroupMember(
 }
 
 export async function checkUserHasSSOLinked(userId, groupId) {
-  const internalProviderId = getProviderId(groupId)
+  const internalProviderId = GroupUtils.getProviderId(groupId)
   const user = await UserGetter.promises.getUser(
     { _id: userId },
     { samlIdentifiers: 1, enrollment: 1 }

+ 105 - 0
services/web/test/unit/src/Project/ProjectEntityRestoreHandler.test.mjs

@@ -0,0 +1,105 @@
+import { vi, expect } from 'vitest'
+import sinon from 'sinon'
+
+const MODULE_PATH =
+  '../../../../app/src/Features/Project/ProjectEntityRestoreHandler.mjs'
+
+describe('ProjectEntityRestoreHandler', function () {
+  beforeEach(async function (ctx) {
+    ctx.project = {
+      _id: '123213jlkj9kdlsaj',
+    }
+
+    ctx.user = {
+      _id: '588f3ddae8ebc1bac07c9fa4',
+      first_name: 'bjkdsjfk',
+      features: {},
+    }
+
+    ctx.docId = '4eecb1c1bffa66588e0000a2'
+
+    ctx.DocModel = class Doc {
+      constructor(options) {
+        this.name = options.name
+        this.lines = options.lines
+        this._id = this.docId
+        this.rev = 0
+      }
+    }
+
+    ctx.ProjectEntityHandler = {
+      promises: {
+        getDoc: sinon.stub(),
+      },
+    }
+
+    ctx.EditorController = {
+      promises: {
+        addDocWithRanges: sinon.stub(),
+      },
+    }
+
+    vi.doMock(
+      '../../../../app/src/Features/Project/ProjectEntityHandler.js',
+      () => ({
+        default: ctx.ProjectEntityHandler,
+      })
+    )
+
+    vi.doMock(
+      '../../../../app/src/Features/Editor/EditorController.js',
+      () => ({
+        default: ctx.EditorController,
+      })
+    )
+
+    ctx.ProjectEntityRestoreHandler = (await import(MODULE_PATH)).default
+  })
+
+  it('should add a new doc with timestamp name and old content', async function (ctx) {
+    const docName = 'deletedDoc'
+
+    ctx.docLines = ['line one', 'line two']
+    ctx.rev = 3
+    ctx.ranges = { comments: [{ id: 123 }] }
+
+    ctx.newDoc = new ctx.DocModel({
+      name: ctx.docName,
+      lines: undefined,
+      _id: ctx.docId,
+      rev: 0,
+    })
+
+    ctx.ProjectEntityHandler.promises.getDoc.resolves({
+      lines: ctx.docLines,
+      rev: ctx.rev,
+      version: 'version',
+      ranges: ctx.ranges,
+    })
+
+    ctx.EditorController.promises.addDocWithRanges = sinon
+      .stub()
+      .resolves(ctx.newDoc)
+
+    await ctx.ProjectEntityRestoreHandler.promises.restoreDeletedDoc(
+      ctx.project._id,
+      ctx.docId,
+      docName,
+      ctx.user._id
+    )
+
+    const docNameMatcher = new RegExp(docName + '-\\d{4}-\\d{2}-\\d{2}-\\d+')
+
+    expect(
+      ctx.EditorController.promises.addDocWithRanges
+    ).to.have.been.calledWith(
+      ctx.project._id,
+      null,
+      sinon.match(docNameMatcher),
+      ctx.docLines,
+      ctx.ranges,
+      null,
+      ctx.user._id
+    )
+  })
+})

+ 0 - 97
services/web/test/unit/src/Project/ProjectEntityRestoreHandlerTests.js

@@ -1,97 +0,0 @@
-const { expect } = require('chai')
-const sinon = require('sinon')
-const SandboxedModule = require('sandboxed-module')
-
-const MODULE_PATH =
-  '../../../../app/src/Features/Project/ProjectEntityRestoreHandler.js'
-
-describe('ProjectEntityRestoreHandler', function () {
-  beforeEach(function () {
-    this.project = {
-      _id: '123213jlkj9kdlsaj',
-    }
-
-    this.user = {
-      _id: '588f3ddae8ebc1bac07c9fa4',
-      first_name: 'bjkdsjfk',
-      features: {},
-    }
-
-    this.docId = '4eecb1c1bffa66588e0000a2'
-
-    this.DocModel = class Doc {
-      constructor(options) {
-        this.name = options.name
-        this.lines = options.lines
-        this._id = this.docId
-        this.rev = 0
-      }
-    }
-
-    this.ProjectEntityHandler = {
-      promises: {
-        getDoc: sinon.stub(),
-      },
-    }
-
-    this.EditorController = {
-      promises: {
-        addDocWithRanges: sinon.stub(),
-      },
-    }
-
-    this.ProjectEntityRestoreHandler = SandboxedModule.require(MODULE_PATH, {
-      requires: {
-        './ProjectEntityHandler': this.ProjectEntityHandler,
-        '../Editor/EditorController': this.EditorController,
-      },
-    })
-  })
-
-  it('should add a new doc with timestamp name and old content', async function () {
-    const docName = 'deletedDoc'
-
-    this.docLines = ['line one', 'line two']
-    this.rev = 3
-    this.ranges = { comments: [{ id: 123 }] }
-
-    this.newDoc = new this.DocModel({
-      name: this.docName,
-      lines: undefined,
-      _id: this.docId,
-      rev: 0,
-    })
-
-    this.ProjectEntityHandler.promises.getDoc.resolves({
-      lines: this.docLines,
-      rev: this.rev,
-      version: 'version',
-      ranges: this.ranges,
-    })
-
-    this.EditorController.promises.addDocWithRanges = sinon
-      .stub()
-      .resolves(this.newDoc)
-
-    await this.ProjectEntityRestoreHandler.promises.restoreDeletedDoc(
-      this.project._id,
-      this.docId,
-      docName,
-      this.user._id
-    )
-
-    const docNameMatcher = new RegExp(docName + '-\\d{4}-\\d{2}-\\d{2}-\\d+')
-
-    expect(
-      this.EditorController.promises.addDocWithRanges
-    ).to.have.been.calledWith(
-      this.project._id,
-      null,
-      sinon.match(docNameMatcher),
-      this.docLines,
-      this.ranges,
-      null,
-      this.user._id
-    )
-  })
-})

+ 166 - 0
services/web/test/unit/src/Project/ProjectHistoryHandler.test.mjs

@@ -0,0 +1,166 @@
+import { vi } from 'vitest'
+import sinon from 'sinon'
+
+const modulePath =
+  '../../../../app/src/Features/Project/ProjectHistoryHandler.mjs'
+
+describe('ProjectHistoryHandler', function () {
+  const projectId = '4eecb1c1bffa66588e0000a1'
+
+  beforeEach(async function (ctx) {
+    let Project
+    ctx.ProjectModel = Project = (function () {
+      Project = class Project {
+        static initClass() {
+          this.prototype.rootFolder = [this.rootFolder]
+        }
+
+        constructor(options) {
+          this._id = projectId
+          this.name = 'project_name_here'
+          this.rev = 0
+        }
+      }
+      Project.initClass()
+      return Project
+    })()
+    ctx.project = new ctx.ProjectModel()
+    ctx.historyId = ctx.project._id.toString()
+
+    ctx.callback = sinon.stub()
+
+    vi.doMock('@overleaf/settings', () => ({
+      default: (ctx.Settings = {}),
+    }))
+
+    vi.doMock('../../../../app/src/models/Project.js', () => ({
+      Project: ctx.ProjectModel,
+    }))
+
+    vi.doMock(
+      '../../../../app/src/Features/Project/ProjectDetailsHandler.js',
+      () => ({
+        default: (ctx.ProjectDetailsHandler = {
+          promises: {},
+        }),
+      })
+    )
+
+    vi.doMock('../../../../app/src/Features/History/HistoryManager.js', () => ({
+      default: (ctx.HistoryManager = {
+        promises: {},
+      }),
+    }))
+
+    vi.doMock(
+      '../../../../app/src/Features/Project/ProjectEntityUpdateHandler',
+      () => ({
+        default: (ctx.ProjectEntityUpdateHandler = {
+          promises: {},
+        }),
+      })
+    )
+
+    return (ctx.ProjectHistoryHandler = (await import(modulePath)).default)
+  })
+
+  describe('starting history for an existing project', function () {
+    beforeEach(async function (ctx) {
+      ctx.HistoryManager.promises.initializeProject = sinon
+        .stub()
+        .resolves(ctx.historyId)
+      ctx.HistoryManager.promises.flushProject = sinon.stub()
+
+      return (ctx.ProjectEntityUpdateHandler.promises.resyncProjectHistory =
+        sinon.stub())
+    })
+
+    describe('when the history does not already exist', function () {
+      beforeEach(async function (ctx) {
+        ctx.ProjectDetailsHandler.promises.getDetails = sinon
+          .stub()
+          .withArgs(projectId)
+          .resolves(ctx.project)
+        ctx.ProjectModel.updateOne = sinon.stub().resolves({ matchedCount: 1 })
+        return ctx.ProjectHistoryHandler.promises.ensureHistoryExistsForProject(
+          projectId
+        )
+      })
+
+      it('should get any existing history id for the project', async function (ctx) {
+        return ctx.ProjectDetailsHandler.promises.getDetails
+          .calledWith(projectId)
+          .should.equal(true)
+      })
+
+      it('should initialize a new history in the v1 history service', async function (ctx) {
+        return ctx.HistoryManager.promises.initializeProject.called.should.equal(
+          true
+        )
+      })
+
+      it('should set the new history id on the project', async function (ctx) {
+        return ctx.ProjectModel.updateOne
+          .calledWith(
+            { _id: projectId, 'overleaf.history.id': { $exists: false } },
+            { 'overleaf.history.id': ctx.historyId }
+          )
+          .should.equal(true)
+      })
+
+      it('should resync the project history', async function (ctx) {
+        return ctx.ProjectEntityUpdateHandler.promises.resyncProjectHistory
+          .calledWith(projectId)
+          .should.equal(true)
+      })
+
+      it('should flush the project history', async function (ctx) {
+        return ctx.HistoryManager.promises.flushProject
+          .calledWith(projectId)
+          .should.equal(true)
+      })
+    })
+
+    describe('when the history already exists', function () {
+      beforeEach(function (ctx) {
+        ctx.project.overleaf = { history: { id: 1234 } }
+        ctx.ProjectDetailsHandler.promises.getDetails = sinon
+          .stub()
+          .withArgs(projectId)
+          .resolves(ctx.project)
+        ctx.ProjectModel.updateOne = sinon.stub().resolves({ matchedCount: 1 })
+        return ctx.ProjectHistoryHandler.promises.ensureHistoryExistsForProject(
+          projectId
+        )
+      })
+
+      it('should get any existing history id for the project', async function (ctx) {
+        return ctx.ProjectDetailsHandler.promises.getDetails
+          .calledWith(projectId)
+          .should.equal(true)
+      })
+
+      it('should not initialize a new history in the v1 history service', async function (ctx) {
+        return ctx.HistoryManager.promises.initializeProject.called.should.equal(
+          false
+        )
+      })
+
+      it('should not set the new history id on the project', async function (ctx) {
+        return ctx.ProjectModel.updateOne.called.should.equal(false)
+      })
+
+      it('should not resync the project history', async function (ctx) {
+        return ctx.ProjectEntityUpdateHandler.promises.resyncProjectHistory.called.should.equal(
+          false
+        )
+      })
+
+      it('should not flush the project history', async function (ctx) {
+        return ctx.HistoryManager.promises.flushProject.called.should.equal(
+          false
+        )
+      })
+    })
+  })
+})

+ 0 - 163
services/web/test/unit/src/Project/ProjectHistoryHandlerTests.js

@@ -1,163 +0,0 @@
-/* eslint-disable
-    max-len,
-    no-return-assign,
-    no-unused-vars,
-*/
-// TODO: This file was created by bulk-decaffeinate.
-// Fix any style issues and re-enable lint.
-/*
- * decaffeinate suggestions:
- * DS102: Remove unnecessary code created because of implicit returns
- * DS206: Consider reworking classes to avoid initClass
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
-const { assert, expect } = require('chai')
-const sinon = require('sinon')
-const modulePath = '../../../../app/src/Features/Project/ProjectHistoryHandler'
-const SandboxedModule = require('sandboxed-module')
-
-describe('ProjectHistoryHandler', function () {
-  const projectId = '4eecb1c1bffa66588e0000a1'
-  const userId = 1234
-
-  beforeEach(function () {
-    let Project
-    this.ProjectModel = Project = (function () {
-      Project = class Project {
-        static initClass() {
-          this.prototype.rootFolder = [this.rootFolder]
-        }
-
-        constructor(options) {
-          this._id = projectId
-          this.name = 'project_name_here'
-          this.rev = 0
-        }
-      }
-      Project.initClass()
-      return Project
-    })()
-    this.project = new this.ProjectModel()
-    this.historyId = this.project._id.toString()
-
-    this.callback = sinon.stub()
-
-    return (this.ProjectHistoryHandler = SandboxedModule.require(modulePath, {
-      requires: {
-        '@overleaf/settings': (this.Settings = {}),
-        '../../models/Project': {
-          Project: this.ProjectModel,
-        },
-        './ProjectDetailsHandler': (this.ProjectDetailsHandler = {
-          promises: {},
-        }),
-        '../History/HistoryManager': (this.HistoryManager = {
-          promises: {},
-        }),
-        './ProjectEntityUpdateHandler': (this.ProjectEntityUpdateHandler = {
-          promises: {},
-        }),
-      },
-    }))
-  })
-
-  describe('starting history for an existing project', function () {
-    beforeEach(async function () {
-      this.HistoryManager.promises.initializeProject = sinon
-        .stub()
-        .resolves(this.historyId)
-      this.HistoryManager.promises.flushProject = sinon.stub()
-
-      return (this.ProjectEntityUpdateHandler.promises.resyncProjectHistory =
-        sinon.stub())
-    })
-
-    describe('when the history does not already exist', function () {
-      beforeEach(async function () {
-        this.ProjectDetailsHandler.promises.getDetails = sinon
-          .stub()
-          .withArgs(projectId)
-          .resolves(this.project)
-        this.ProjectModel.updateOne = sinon.stub().resolves({ matchedCount: 1 })
-        return this.ProjectHistoryHandler.promises.ensureHistoryExistsForProject(
-          projectId
-        )
-      })
-
-      it('should get any existing history id for the project', async function () {
-        return this.ProjectDetailsHandler.promises.getDetails
-          .calledWith(projectId)
-          .should.equal(true)
-      })
-
-      it('should initialize a new history in the v1 history service', async function () {
-        return this.HistoryManager.promises.initializeProject.called.should.equal(
-          true
-        )
-      })
-
-      it('should set the new history id on the project', async function () {
-        return this.ProjectModel.updateOne
-          .calledWith(
-            { _id: projectId, 'overleaf.history.id': { $exists: false } },
-            { 'overleaf.history.id': this.historyId }
-          )
-          .should.equal(true)
-      })
-
-      it('should resync the project history', async function () {
-        return this.ProjectEntityUpdateHandler.promises.resyncProjectHistory
-          .calledWith(projectId)
-          .should.equal(true)
-      })
-
-      it('should flush the project history', async function () {
-        return this.HistoryManager.promises.flushProject
-          .calledWith(projectId)
-          .should.equal(true)
-      })
-    })
-
-    describe('when the history already exists', function () {
-      beforeEach(function () {
-        this.project.overleaf = { history: { id: 1234 } }
-        this.ProjectDetailsHandler.promises.getDetails = sinon
-          .stub()
-          .withArgs(projectId)
-          .resolves(this.project)
-        this.ProjectModel.updateOne = sinon.stub().resolves({ matchedCount: 1 })
-        return this.ProjectHistoryHandler.promises.ensureHistoryExistsForProject(
-          projectId
-        )
-      })
-
-      it('should get any existing history id for the project', async function () {
-        return this.ProjectDetailsHandler.promises.getDetails
-          .calledWith(projectId)
-          .should.equal(true)
-      })
-
-      it('should not initialize a new history in the v1 history service', async function () {
-        return this.HistoryManager.promises.initializeProject.called.should.equal(
-          false
-        )
-      })
-
-      it('should not set the new history id on the project', async function () {
-        return this.ProjectModel.updateOne.called.should.equal(false)
-      })
-
-      it('should not resync the project history', async function () {
-        return this.ProjectEntityUpdateHandler.promises.resyncProjectHistory.called.should.equal(
-          false
-        )
-      })
-
-      it('should not flush the project history', async function () {
-        return this.HistoryManager.promises.flushProject.called.should.equal(
-          false
-        )
-      })
-    })
-  })
-})

+ 138 - 0
services/web/test/unit/src/Referal/ReferalAllocator.test.mjs

@@ -0,0 +1,138 @@
+import { vi } from 'vitest'
+import sinon from 'sinon'
+
+const modulePath = '../../../../app/src/Features/Referal/ReferalAllocator.mjs'
+
+describe('ReferalAllocator', function () {
+  beforeEach(async function (ctx) {
+    vi.doMock('../../../../app/src/models/User.js', () => ({
+      User: (ctx.User = {}),
+    }))
+
+    vi.doMock(
+      '../../../../app/src/Features/Subscription/FeaturesUpdater.js',
+      () => ({
+        default: (ctx.FeaturesUpdater = {}),
+      })
+    )
+
+    vi.doMock('@overleaf/settings', () => ({
+      default: (ctx.Settings = {}),
+    }))
+
+    ctx.ReferalAllocator = (await import(modulePath)).default
+    ctx.referal_id = 'referal-id-123'
+    ctx.referal_medium = 'twitter'
+    ctx.user_id = 'user-id-123'
+    ctx.new_user_id = 'new-user-id-123'
+    ctx.FeaturesUpdater.promises = {
+      refreshFeatures: sinon.stub().resolves(),
+    }
+    ctx.User.updateOne = sinon.stub().returns({
+      exec: sinon.stub().resolves(),
+    })
+    ctx.User.findOne = sinon.stub().returns({
+      exec: sinon.stub().resolves({ _id: ctx.user_id }),
+    })
+  })
+
+  describe('allocate', function () {
+    describe('when the referal was a bonus referal', function () {
+      beforeEach(async function (ctx) {
+        ctx.referal_source = 'bonus'
+        await ctx.ReferalAllocator.promises.allocate(
+          ctx.referal_id,
+          ctx.new_user_id,
+          ctx.referal_source,
+          ctx.referal_medium
+        )
+      })
+
+      it('should update the referring user with the refered users id', function (ctx) {
+        ctx.User.updateOne
+          .calledWith(
+            {
+              referal_id: ctx.referal_id,
+            },
+            {
+              $push: {
+                refered_users: ctx.new_user_id,
+              },
+              $inc: {
+                refered_user_count: 1,
+              },
+            }
+          )
+          .should.equal(true)
+      })
+
+      it('find the referring users id', function (ctx) {
+        ctx.User.findOne
+          .calledWith({ referal_id: ctx.referal_id })
+          .should.equal(true)
+      })
+
+      it("should refresh the user's subscription", function (ctx) {
+        ctx.FeaturesUpdater.promises.refreshFeatures
+          .calledWith(ctx.user_id)
+          .should.equal(true)
+      })
+    })
+
+    describe('when there is no user for the referal id', function () {
+      beforeEach(async function (ctx) {
+        ctx.referal_source = 'bonus'
+        ctx.referal_id = 'wombat'
+        ctx.User.findOne = sinon.stub().returns({
+          exec: sinon.stub().resolves(null),
+        })
+        await ctx.ReferalAllocator.promises.allocate(
+          ctx.referal_id,
+          ctx.new_user_id,
+          ctx.referal_source,
+          ctx.referal_medium
+        )
+      })
+
+      it('should find the referring users id', function (ctx) {
+        ctx.User.findOne
+          .calledWith({ referal_id: ctx.referal_id })
+          .should.equal(true)
+      })
+
+      it('should not update the referring user with the refered users id', function (ctx) {
+        ctx.User.updateOne.called.should.equal(false)
+      })
+
+      it('should not assign the user a bonus', function (ctx) {
+        ctx.FeaturesUpdater.promises.refreshFeatures.called.should.equal(false)
+      })
+    })
+
+    describe('when the referal is not a bonus referal', function () {
+      beforeEach(async function (ctx) {
+        ctx.referal_source = 'public_share'
+        await ctx.ReferalAllocator.promises.allocate(
+          ctx.referal_id,
+          ctx.new_user_id,
+          ctx.referal_source,
+          ctx.referal_medium
+        )
+      })
+
+      it('should not update the referring user with the refered users id', function (ctx) {
+        ctx.User.updateOne.called.should.equal(false)
+      })
+
+      it('find the referring users id', function (ctx) {
+        ctx.User.findOne
+          .calledWith({ referal_id: ctx.referal_id })
+          .should.equal(true)
+      })
+
+      it('should not assign the user a bonus', function (ctx) {
+        ctx.FeaturesUpdater.promises.refreshFeatures.called.should.equal(false)
+      })
+    })
+  })
+})

+ 0 - 133
services/web/test/unit/src/Referal/ReferalAllocatorTests.js

@@ -1,133 +0,0 @@
-const SandboxedModule = require('sandboxed-module')
-const sinon = require('sinon')
-const modulePath = require('path').join(
-  __dirname,
-  '../../../../app/src/Features/Referal/ReferalAllocator.js'
-)
-
-describe('ReferalAllocator', function () {
-  beforeEach(function () {
-    this.ReferalAllocator = SandboxedModule.require(modulePath, {
-      requires: {
-        '../../models/User': {
-          User: (this.User = {}),
-        },
-        '../Subscription/FeaturesUpdater': (this.FeaturesUpdater = {}),
-        '@overleaf/settings': (this.Settings = {}),
-      },
-    })
-    this.referal_id = 'referal-id-123'
-    this.referal_medium = 'twitter'
-    this.user_id = 'user-id-123'
-    this.new_user_id = 'new-user-id-123'
-    this.FeaturesUpdater.promises = {
-      refreshFeatures: sinon.stub().resolves(),
-    }
-    this.User.updateOne = sinon.stub().returns({
-      exec: sinon.stub().resolves(),
-    })
-    this.User.findOne = sinon.stub().returns({
-      exec: sinon.stub().resolves({ _id: this.user_id }),
-    })
-  })
-
-  describe('allocate', function () {
-    describe('when the referal was a bonus referal', function () {
-      beforeEach(async function () {
-        this.referal_source = 'bonus'
-        await this.ReferalAllocator.promises.allocate(
-          this.referal_id,
-          this.new_user_id,
-          this.referal_source,
-          this.referal_medium
-        )
-      })
-
-      it('should update the referring user with the refered users id', function () {
-        this.User.updateOne
-          .calledWith(
-            {
-              referal_id: this.referal_id,
-            },
-            {
-              $push: {
-                refered_users: this.new_user_id,
-              },
-              $inc: {
-                refered_user_count: 1,
-              },
-            }
-          )
-          .should.equal(true)
-      })
-
-      it('find the referring users id', function () {
-        this.User.findOne
-          .calledWith({ referal_id: this.referal_id })
-          .should.equal(true)
-      })
-
-      it("should refresh the user's subscription", function () {
-        this.FeaturesUpdater.promises.refreshFeatures
-          .calledWith(this.user_id)
-          .should.equal(true)
-      })
-    })
-
-    describe('when there is no user for the referal id', function () {
-      beforeEach(async function () {
-        this.referal_source = 'bonus'
-        this.referal_id = 'wombat'
-        this.User.findOne = sinon.stub().returns({
-          exec: sinon.stub().resolves(null),
-        })
-        await this.ReferalAllocator.promises.allocate(
-          this.referal_id,
-          this.new_user_id,
-          this.referal_source,
-          this.referal_medium
-        )
-      })
-
-      it('should find the referring users id', function () {
-        this.User.findOne
-          .calledWith({ referal_id: this.referal_id })
-          .should.equal(true)
-      })
-
-      it('should not update the referring user with the refered users id', function () {
-        this.User.updateOne.called.should.equal(false)
-      })
-
-      it('should not assign the user a bonus', function () {
-        this.FeaturesUpdater.promises.refreshFeatures.called.should.equal(false)
-      })
-    })
-
-    describe('when the referal is not a bonus referal', function () {
-      beforeEach(async function () {
-        this.referal_source = 'public_share'
-        await this.ReferalAllocator.promises.allocate(
-          this.referal_id,
-          this.new_user_id,
-          this.referal_source,
-          this.referal_medium
-        )
-      })
-
-      it('should not update the referring user with the refered users id', function () {
-        this.User.updateOne.called.should.equal(false)
-      })
-
-      it('find the referring users id', function () {
-        this.User.findOne
-          .calledWith({ referal_id: this.referal_id })
-          .should.equal(true)
-      })
-
-      it('should not assign the user a bonus', function () {
-        this.FeaturesUpdater.promises.refreshFeatures.called.should.equal(false)
-      })
-    })
-  })
-})

+ 89 - 0
services/web/test/unit/src/SplitTests/SplitTestMiddleware.test.mjs

@@ -0,0 +1,89 @@
+import { vi } from 'vitest'
+import sinon from 'sinon'
+import MockResponse from '../helpers/MockResponse.js'
+import MockRequest from '../helpers/MockRequest.js'
+
+const modulePath = '../../../../app/src/Features/SplitTests/SplitTestMiddleware'
+
+describe('SplitTestMiddleware', function () {
+  beforeEach(async function (ctx) {
+    vi.doMock(
+      '../../../../app/src/Features/SplitTests/SplitTestHandler.js',
+      () => ({
+        default: (ctx.SplitTestHandler = {
+          promises: {
+            getAssignment: sinon.stub().resolves(),
+          },
+        }),
+      })
+    )
+
+    ctx.SplitTestMiddleware = (await import(modulePath)).default
+
+    ctx.req = new MockRequest()
+    ctx.res = new MockResponse()
+    ctx.next = sinon.stub()
+  })
+
+  it('assign multiple split test variants in locals', async function (ctx) {
+    ctx.SplitTestHandler.promises.getAssignment
+      .withArgs(ctx.req, 'ui-overhaul')
+      .resolves({
+        variant: 'default',
+      })
+    ctx.SplitTestHandler.promises.getAssignment
+      .withArgs(ctx.req, 'other-test')
+      .resolves({
+        variant: 'foobar',
+      })
+
+    const middleware = ctx.SplitTestMiddleware.loadAssignmentsInLocals([
+      'ui-overhaul',
+      'other-test',
+    ])
+    await middleware(ctx.req, ctx.res, ctx.next)
+
+    sinon.assert.calledWith(
+      ctx.SplitTestHandler.promises.getAssignment,
+      ctx.req,
+      ctx.res,
+      'ui-overhaul'
+    )
+    sinon.assert.calledWith(
+      ctx.SplitTestHandler.promises.getAssignment,
+      ctx.req,
+      ctx.res,
+      'other-test'
+    )
+    sinon.assert.calledOnce(ctx.next)
+  })
+
+  it('assign no split test variant in locals', async function (ctx) {
+    const middleware = ctx.SplitTestMiddleware.loadAssignmentsInLocals([])
+
+    await middleware(ctx.req, ctx.res, ctx.next)
+
+    sinon.assert.notCalled(ctx.SplitTestHandler.promises.getAssignment)
+    sinon.assert.calledOnce(ctx.next)
+  })
+
+  it('exception thrown by assignment does not fail the request', async function (ctx) {
+    ctx.SplitTestHandler.promises.getAssignment
+      .withArgs(ctx.req, ctx.res, 'some-test')
+      .throws(new Error('failure'))
+
+    const middleware = ctx.SplitTestMiddleware.loadAssignmentsInLocals([
+      'some-test',
+    ])
+
+    await middleware(ctx.req, ctx.res, ctx.next)
+
+    sinon.assert.calledWith(
+      ctx.SplitTestHandler.promises.getAssignment,
+      ctx.req,
+      ctx.res,
+      'some-test'
+    )
+    sinon.assert.calledOnce(ctx.next)
+  })
+})

+ 0 - 89
services/web/test/unit/src/SplitTests/SplitTestMiddlewareTests.js

@@ -1,89 +0,0 @@
-const SandboxedModule = require('sandboxed-module')
-const path = require('path')
-const modulePath = path.join(
-  __dirname,
-  '../../../../app/src/Features/SplitTests/SplitTestMiddleware'
-)
-const sinon = require('sinon')
-const MockResponse = require('../helpers/MockResponse')
-const MockRequest = require('../helpers/MockRequest')
-
-describe('SplitTestMiddleware', function () {
-  beforeEach(function () {
-    this.SplitTestMiddleware = SandboxedModule.require(modulePath, {
-      requires: {
-        './SplitTestHandler': (this.SplitTestHandler = {
-          promises: {
-            getAssignment: sinon.stub().resolves(),
-          },
-        }),
-      },
-    })
-
-    this.req = new MockRequest()
-    this.res = new MockResponse()
-    this.next = sinon.stub()
-  })
-
-  it('assign multiple split test variants in locals', async function () {
-    this.SplitTestHandler.promises.getAssignment
-      .withArgs(this.req, 'ui-overhaul')
-      .resolves({
-        variant: 'default',
-      })
-    this.SplitTestHandler.promises.getAssignment
-      .withArgs(this.req, 'other-test')
-      .resolves({
-        variant: 'foobar',
-      })
-
-    const middleware = this.SplitTestMiddleware.loadAssignmentsInLocals([
-      'ui-overhaul',
-      'other-test',
-    ])
-    await middleware(this.req, this.res, this.next)
-
-    sinon.assert.calledWith(
-      this.SplitTestHandler.promises.getAssignment,
-      this.req,
-      this.res,
-      'ui-overhaul'
-    )
-    sinon.assert.calledWith(
-      this.SplitTestHandler.promises.getAssignment,
-      this.req,
-      this.res,
-      'other-test'
-    )
-    sinon.assert.calledOnce(this.next)
-  })
-
-  it('assign no split test variant in locals', async function () {
-    const middleware = this.SplitTestMiddleware.loadAssignmentsInLocals([])
-
-    await middleware(this.req, this.res, this.next)
-
-    sinon.assert.notCalled(this.SplitTestHandler.promises.getAssignment)
-    sinon.assert.calledOnce(this.next)
-  })
-
-  it('exception thrown by assignment does not fail the request', async function () {
-    this.SplitTestHandler.promises.getAssignment
-      .withArgs(this.req, this.res, 'some-test')
-      .throws(new Error('failure'))
-
-    const middleware = this.SplitTestMiddleware.loadAssignmentsInLocals([
-      'some-test',
-    ])
-
-    await middleware(this.req, this.res, this.next)
-
-    sinon.assert.calledWith(
-      this.SplitTestHandler.promises.getAssignment,
-      this.req,
-      this.res,
-      'some-test'
-    )
-    sinon.assert.calledOnce(this.next)
-  })
-})

Разница между файлами не показана из-за своего большого размера
+ 330 - 274
services/web/test/unit/src/Subscription/SubscriptionGroupHandler.test.mjs


Некоторые файлы не были показаны из-за большого количества измененных файлов