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

Merge pull request #11147 from overleaf/ii-subscriptions-pages-react-split-test

[web] React subscription split test

GitOrigin-RevId: 6656b3895030bc677483a3e30d5e998f5f7d1458
ilkin-overleaf 3 лет назад
Родитель
Сommit
7e68b4f0d5
22 измененных файлов с 527 добавлено и 52 удалено
  1. 259 4
      services/web/app/src/Features/Subscription/SubscriptionController.js
  2. 88 35
      services/web/app/src/Features/UserMembership/UserMembershipController.js
  3. 7 0
      services/web/app/views/subscriptions/canceled-subscription-react.pug
  4. 0 0
      services/web/app/views/subscriptions/canceled-subscription.pug
  5. 17 0
      services/web/app/views/subscriptions/dashboard-react.pug
  6. 14 0
      services/web/app/views/subscriptions/new-react.pug
  7. 7 0
      services/web/app/views/subscriptions/successful-subscription-react.pug
  8. 0 0
      services/web/app/views/subscriptions/successful-subscription.pug
  9. 12 0
      services/web/app/views/user_membership/index-react.pug
  10. 5 0
      services/web/frontend/js/features/membership/components/groups-root.tsx
  11. 13 0
      services/web/frontend/js/features/subscription/components/canceled-subscription/root.tsx
  12. 13 0
      services/web/frontend/js/features/subscription/components/dashboard/root.tsx
  13. 13 0
      services/web/frontend/js/features/subscription/components/new/root.tsx
  14. 13 0
      services/web/frontend/js/features/subscription/components/successful-subscription/root.tsx
  15. 7 0
      services/web/frontend/js/pages/user/membership/groups.js
  16. 11 0
      services/web/frontend/js/pages/user/subscription/base.js
  17. 8 0
      services/web/frontend/js/pages/user/subscription/canceled-subscription.js
  18. 8 0
      services/web/frontend/js/pages/user/subscription/dashboard.js
  19. 8 0
      services/web/frontend/js/pages/user/subscription/new.js
  20. 8 0
      services/web/frontend/js/pages/user/subscription/successful-subscription.js
  21. 1 1
      services/web/test/unit/src/Subscription/SubscriptionControllerTests.js
  22. 15 12
      services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js

+ 259 - 4
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -104,8 +104,106 @@ async function plansPage(req, res) {
   })
   })
 }
 }
 
 
-// get to show the recurly.js page
 async function paymentPage(req, res) {
 async function paymentPage(req, res) {
+  try {
+    const assignment = await SplitTestHandler.promises.getAssignment(
+      req,
+      res,
+      'subscription-pages-react'
+    )
+    // get to show the recurly.js page
+    if (assignment.variant === 'active') {
+      await _paymentReactPage(req, res)
+    } else {
+      await _paymentAngularPage(req, res)
+    }
+  } catch (error) {
+    logger.warn(
+      { err: error },
+      'failed to get "subscription-pages-react" split test assignment'
+    )
+    await _paymentAngularPage(req, res)
+  }
+}
+
+/**
+ * @param {import("express").Request} req
+ * @param {import("express").Response} res
+ * @returns {Promise<void>}
+ */
+async function _paymentReactPage(req, res) {
+  const user = SessionManager.getSessionUser(req.session)
+  const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
+  if (!plan) {
+    return HttpErrorHandler.unprocessableEntity(req, res, 'Plan not found')
+  }
+  const hasSubscription =
+    await LimitationsManager.promises.userHasV1OrV2Subscription(user)
+  if (hasSubscription) {
+    res.redirect('/user/subscription?hasSubscription=true')
+  } else {
+    // LimitationsManager.userHasV2Subscription only checks Mongo. Double check with
+    // Recurly as well at this point (we don't do this most places for speed).
+    const valid =
+      await SubscriptionHandler.promises.validateNoSubscriptionInRecurly(
+        user._id
+      )
+    if (!valid) {
+      res.redirect('/user/subscription?hasSubscription=true')
+    } else {
+      let currency = null
+      if (req.query.currency) {
+        const queryCurrency = req.query.currency.toUpperCase()
+        if (GeoIpLookup.isValidCurrencyParam(queryCurrency)) {
+          currency = queryCurrency
+        }
+      }
+      const { currencyCode: recommendedCurrency, countryCode } =
+        await GeoIpLookup.promises.getCurrencyCode(
+          (req.query ? req.query.ip : undefined) || req.ip
+        )
+      if (recommendedCurrency && currency == null) {
+        currency = recommendedCurrency
+      }
+
+      const refreshedPaymentPageAssignment =
+        await SplitTestHandler.promises.getAssignment(
+          req,
+          res,
+          'payment-page-refresh'
+        )
+      const useRefreshedPaymentPage =
+        refreshedPaymentPageAssignment &&
+        refreshedPaymentPageAssignment.variant === 'refreshed-payment-page'
+
+      await SplitTestHandler.promises.getAssignment(
+        req,
+        res,
+        'student-check-modal'
+      )
+
+      // TODO
+      const template = useRefreshedPaymentPage
+        ? 'subscriptions/new-react'
+        : 'subscriptions/new-react'
+
+      res.render(template, {
+        title: 'subscribe',
+        currency,
+        countryCode,
+        plan,
+        recurlyConfig: JSON.stringify({
+          currency,
+          subdomain: Settings.apis.recurly.subdomain,
+        }),
+        showCouponField: !!req.query.scf,
+        showVatField: !!req.query.svf,
+      })
+    }
+  }
+}
+
+async function _paymentAngularPage(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
   const plan = PlansLocator.findLocalPlanInSettings(req.query.planCode)
   if (!plan) {
   if (!plan) {
@@ -177,6 +275,84 @@ async function paymentPage(req, res) {
 }
 }
 
 
 async function userSubscriptionPage(req, res) {
 async function userSubscriptionPage(req, res) {
+  try {
+    const assignment = await SplitTestHandler.promises.getAssignment(
+      req,
+      res,
+      'subscription-pages-react'
+    )
+    if (assignment.variant === 'active') {
+      await _userSubscriptionReactPage(req, res)
+    } else {
+      await _userSubscriptionAngularPage(req, res)
+    }
+  } catch (error) {
+    logger.warn(
+      { err: error },
+      'failed to get "subscription-pages-react" split test assignment'
+    )
+    await _userSubscriptionAngularPage(req, res)
+  }
+}
+
+/**
+ * @param {import("express").Request} req
+ * @param {import("express").Response} res
+ * @returns {Promise<void>}
+ */
+async function _userSubscriptionReactPage(req, res) {
+  const user = SessionManager.getSessionUser(req.session)
+  const results =
+    await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
+      user
+    )
+  const {
+    personalSubscription,
+    memberGroupSubscriptions,
+    managedGroupSubscriptions,
+    currentInstitutionsWithLicence,
+    managedInstitutions,
+    managedPublishers,
+    v1SubscriptionStatus,
+  } = results
+  const hasSubscription =
+    await LimitationsManager.promises.userHasV1OrV2Subscription(user)
+  const fromPlansPage = req.query.hasSubscription
+  const plans = SubscriptionViewModelBuilder.buildPlansList(
+    personalSubscription ? personalSubscription.plan : undefined
+  )
+
+  AnalyticsManager.recordEventForSession(req.session, 'subscription-page-view')
+
+  const cancelButtonAssignment = await SplitTestHandler.promises.getAssignment(
+    req,
+    res,
+    'subscription-cancel-button'
+  )
+
+  const cancelButtonNewCopy = cancelButtonAssignment?.variant === 'new-copy'
+
+  const data = {
+    title: 'your_subscription',
+    plans,
+    groupPlans: GroupPlansData,
+    user,
+    hasSubscription,
+    fromPlansPage,
+    personalSubscription,
+    memberGroupSubscriptions,
+    managedGroupSubscriptions,
+    managedInstitutions,
+    managedPublishers,
+    v1SubscriptionStatus,
+    currentInstitutionsWithLicence,
+    groupPlanModalOptions,
+    cancelButtonNewCopy,
+  }
+  res.render('subscriptions/dashboard-react', data)
+}
+
+async function _userSubscriptionAngularPage(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const results =
   const results =
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
@@ -302,6 +478,52 @@ async function createSubscription(req, res) {
 }
 }
 
 
 async function successfulSubscription(req, res) {
 async function successfulSubscription(req, res) {
+  try {
+    const assignment = await SplitTestHandler.promises.getAssignment(
+      req,
+      res,
+      'subscription-pages-react'
+    )
+    if (assignment.variant === 'active') {
+      await _successfulSubscriptionReact(req, res)
+    } else {
+      await _successfulSubscriptionAngular(req, res)
+    }
+  } catch (error) {
+    logger.warn(
+      { err: error },
+      'failed to get "subscription-pages-react" split test assignment'
+    )
+    await _successfulSubscriptionAngular(req, res)
+  }
+}
+
+/**
+ * @param {import("express").Request} req
+ * @param {import("express").Response} res
+ * @returns {Promise<void>}
+ */
+async function _successfulSubscriptionReact(req, res) {
+  const user = SessionManager.getSessionUser(req.session)
+  const { personalSubscription } =
+    await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
+      user
+    )
+
+  const postCheckoutRedirect = req.session?.postCheckoutRedirect
+
+  if (!personalSubscription) {
+    res.redirect('/user/subscription/plans')
+  } else {
+    res.render('subscriptions/successful-subscription-react', {
+      title: 'thank_you',
+      personalSubscription,
+      postCheckoutRedirect,
+    })
+  }
+}
+
+async function _successfulSubscriptionAngular(req, res) {
   const user = SessionManager.getSessionUser(req.session)
   const user = SessionManager.getSessionUser(req.session)
   const { personalSubscription } =
   const { personalSubscription } =
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
     await SubscriptionViewModelBuilder.promises.buildUsersSubscriptionViewModel(
@@ -313,7 +535,7 @@ async function successfulSubscription(req, res) {
   if (!personalSubscription) {
   if (!personalSubscription) {
     res.redirect('/user/subscription/plans')
     res.redirect('/user/subscription/plans')
   } else {
   } else {
-    res.render('subscriptions/successful_subscription', {
+    res.render('subscriptions/successful-subscription', {
       title: 'thank_you',
       title: 'thank_you',
       personalSubscription,
       personalSubscription,
       postCheckoutRedirect,
       postCheckoutRedirect,
@@ -337,8 +559,41 @@ function cancelSubscription(req, res, next) {
   })
   })
 }
 }
 
 
-function canceledSubscription(req, res, next) {
-  return res.render('subscriptions/canceled_subscription', {
+async function canceledSubscription(req, res, next) {
+  try {
+    const assignment = await SplitTestHandler.promises.getAssignment(
+      req,
+      res,
+      'subscription-pages-react'
+    )
+    if (assignment.variant === 'active') {
+      await _canceledSubscriptionReact(req, res, next)
+    } else {
+      await _canceledSubscriptionAngular(req, res, next)
+    }
+  } catch (error) {
+    logger.warn(
+      { err: error },
+      'failed to get "subscription-pages-react" split test assignment'
+    )
+    await _canceledSubscriptionAngular(req, res, next)
+  }
+}
+
+/**
+ * @param {import("express").Request} req
+ * @param {import("express").Response} res
+ * @param {import("express").NextFunction} next
+ * @returns {Promise<void>}
+ */
+function _canceledSubscriptionReact(req, res, next) {
+  return res.render('subscriptions/canceled-subscription-react', {
+    title: 'subscription_canceled',
+  })
+}
+
+function _canceledSubscriptionAngular(req, res, next) {
+  return res.render('subscriptions/canceled-subscription', {
     title: 'subscription_canceled',
     title: 'subscription_canceled',
   })
   })
 }
 }

+ 88 - 35
services/web/app/src/Features/UserMembership/UserMembershipController.js

@@ -16,42 +16,99 @@ const Errors = require('../Errors/Errors')
 const EmailHelper = require('../Helpers/EmailHelper')
 const EmailHelper = require('../Helpers/EmailHelper')
 const { csvAttachment } = require('../../infrastructure/Response')
 const { csvAttachment } = require('../../infrastructure/Response')
 const { UserIsManagerError } = require('./UserMembershipErrors')
 const { UserIsManagerError } = require('./UserMembershipErrors')
+const SplitTestHandler = require('../SplitTests/SplitTestHandler')
 const CSVParser = require('json2csv').Parser
 const CSVParser = require('json2csv').Parser
+const logger = require('@overleaf/logger')
 
 
-module.exports = {
-  index(req, res, next) {
-    const { entity, entityConfig } = req
-    return entity.fetchV1Data(function (error, entity) {
-      if (error != null) {
-        return next(error)
+async function index(req, res, next) {
+  try {
+    const assignment = await SplitTestHandler.promises.getAssignment(
+      req,
+      res,
+      'subscription-pages-react'
+    )
+    if (assignment.variant === 'active') {
+      await _indexReact(req, res, next)
+    } else {
+      await _indexAngular(req, res, next)
+    }
+  } catch (error) {
+    logger.warn(
+      { err: error },
+      'failed to get "subscription-pages-react" split test assignment'
+    )
+    await _indexAngular(req, res, next)
+  }
+}
+
+function _indexReact(req, res, next) {
+  const { entity, entityConfig } = req
+  return entity.fetchV1Data(function (error, entity) {
+    if (error != null) {
+      return next(error)
+    }
+    return UserMembershipHandler.getUsers(
+      entity,
+      entityConfig,
+      function (error, users) {
+        let entityName
+        if (error != null) {
+          return next(error)
+        }
+        const entityPrimaryKey =
+          entity[entityConfig.fields.primaryKey].toString()
+        if (entityConfig.fields.name) {
+          entityName = entity[entityConfig.fields.name]
+        }
+        return res.render('user_membership/index-react', {
+          name: entityName,
+          users,
+          groupSize: entityConfig.hasMembersLimit
+            ? entity.membersLimit
+            : undefined,
+          translations: entityConfig.translations,
+          paths: entityConfig.pathsFor(entityPrimaryKey),
+        })
       }
       }
-      return UserMembershipHandler.getUsers(
-        entity,
-        entityConfig,
-        function (error, users) {
-          let entityName
-          if (error != null) {
-            return next(error)
-          }
-          const entityPrimaryKey =
-            entity[entityConfig.fields.primaryKey].toString()
-          if (entityConfig.fields.name) {
-            entityName = entity[entityConfig.fields.name]
-          }
-          return res.render('user_membership/index', {
-            name: entityName,
-            users,
-            groupSize: entityConfig.hasMembersLimit
-              ? entity.membersLimit
-              : undefined,
-            translations: entityConfig.translations,
-            paths: entityConfig.pathsFor(entityPrimaryKey),
-          })
+    )
+  })
+}
+
+function _indexAngular(req, res, next) {
+  const { entity, entityConfig } = req
+  return entity.fetchV1Data(function (error, entity) {
+    if (error != null) {
+      return next(error)
+    }
+    return UserMembershipHandler.getUsers(
+      entity,
+      entityConfig,
+      function (error, users) {
+        let entityName
+        if (error != null) {
+          return next(error)
         }
         }
-      )
-    })
-  },
+        const entityPrimaryKey =
+          entity[entityConfig.fields.primaryKey].toString()
+        if (entityConfig.fields.name) {
+          entityName = entity[entityConfig.fields.name]
+        }
+        return res.render('user_membership/index', {
+          name: entityName,
+          users,
+          groupSize: entityConfig.hasMembersLimit
+            ? entity.membersLimit
+            : undefined,
+          translations: entityConfig.translations,
+          paths: entityConfig.pathsFor(entityPrimaryKey),
+        })
+      }
+    )
+  })
+}
 
 
+module.exports = {
+  index,
   add(req, res, next) {
   add(req, res, next) {
     const { entity, entityConfig } = req
     const { entity, entityConfig } = req
     const email = EmailHelper.parseEmail(req.body.email)
     const email = EmailHelper.parseEmail(req.body.email)
@@ -96,7 +153,6 @@ module.exports = {
       }
       }
     )
     )
   },
   },
-
   remove(req, res, next) {
   remove(req, res, next) {
     const { entity, entityConfig } = req
     const { entity, entityConfig } = req
     const { userId } = req.params
     const { userId } = req.params
@@ -135,7 +191,6 @@ module.exports = {
       }
       }
     )
     )
   },
   },
-
   exportCsv(req, res, next) {
   exportCsv(req, res, next) {
     const { entity, entityConfig } = req
     const { entity, entityConfig } = req
     const fields = ['email', 'last_logged_in_at', 'last_active_at']
     const fields = ['email', 'last_logged_in_at', 'last_active_at']
@@ -152,14 +207,12 @@ module.exports = {
       }
       }
     )
     )
   },
   },
-
   new(req, res, next) {
   new(req, res, next) {
     return res.render('user_membership/new', {
     return res.render('user_membership/new', {
       entityName: req.params.name,
       entityName: req.params.name,
       entityId: req.params.id,
       entityId: req.params.id,
     })
     })
   },
   },
-
   create(req, res, next) {
   create(req, res, next) {
     const entityId = req.params.id
     const entityId = req.params.id
     const entityConfig = req.entityConfig
     const entityConfig = req.entityConfig

+ 7 - 0
services/web/app/views/subscriptions/canceled-subscription-react.pug

@@ -0,0 +1,7 @@
+extends ../layout-marketing
+
+block entrypointVar
+	- entrypoint = 'pages/user/subscription/canceled-subscription'
+
+block content
+	main.content.content-alt#subscription-canceled-root

+ 0 - 0
services/web/app/views/subscriptions/canceled_subscription.pug → services/web/app/views/subscriptions/canceled-subscription.pug


+ 17 - 0
services/web/app/views/subscriptions/dashboard-react.pug

@@ -0,0 +1,17 @@
+extends ../layout-marketing
+
+block entrypointVar
+	- entrypoint = 'pages/user/subscription/dashboard'
+
+block append meta
+	meta(name="ol-managedInstitutions", data-type="json", content=managedInstitutions)
+	meta(name="ol-planCodesChangingAtTermEnd", data-type="json", content=plans.planCodesChangingAtTermEnd)
+	if (personalSubscription && personalSubscription.recurly)
+		meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey)
+		meta(name="ol-subscription" data-type="json" content=personalSubscription)
+		meta(name="ol-recommendedCurrency" content=personalSubscription.recurly.currency)
+		meta(name="ol-groupPlans" data-type="json" content=groupPlans)
+		meta(name="ol-groupPlanModalOptions" data-type="json" content=groupPlanModalOptions)
+
+block content
+	main.content.content-alt#subscription-dashboard-root

+ 14 - 0
services/web/app/views/subscriptions/new-react.pug

@@ -0,0 +1,14 @@
+extends ../layout-marketing
+
+include ./_new_mixins
+
+block entrypointVar
+	- entrypoint = 'pages/user/subscription/new'
+
+block append meta
+	meta(name="ol-countryCode" content=countryCode)
+	meta(name="ol-recurlyApiKey" content=settings.apis.recurly.publicKey)
+	meta(name="ol-recommendedCurrency" content=String(currency).slice(0,3))
+
+block content
+	main.content.content-alt#subscription-new-root

+ 7 - 0
services/web/app/views/subscriptions/successful-subscription-react.pug

@@ -0,0 +1,7 @@
+extends ../layout-marketing
+
+block entrypointVar
+	- entrypoint = 'pages/user/subscription/successful-subscription'
+
+block content
+	main.content.content-alt#subscription-success-root

+ 0 - 0
services/web/app/views/subscriptions/successful_subscription.pug → services/web/app/views/subscriptions/successful-subscription.pug


+ 12 - 0
services/web/app/views/user_membership/index-react.pug

@@ -0,0 +1,12 @@
+extends ../layout-marketing
+
+block entrypointVar
+	- entrypoint = 'pages/user/membership/groups'
+
+block append meta
+	meta(name="ol-users", data-type="json", content=users)
+	meta(name="ol-paths", data-type="json", content=paths)
+	meta(name="ol-groupSize", data-type="json", content=groupSize)
+
+block content
+	main.content.content-alt#subscription-manage-groups-root

+ 5 - 0
services/web/frontend/js/features/membership/components/groups-root.tsx

@@ -0,0 +1,5 @@
+function Root() {
+  return <h2>React Manage Group Subscription</h2>
+}
+
+export default Root

+ 13 - 0
services/web/frontend/js/features/subscription/components/canceled-subscription/root.tsx

@@ -0,0 +1,13 @@
+import useWaitForI18n from '../../../../shared/hooks/use-wait-for-i18n'
+
+function Root() {
+  const { isReady } = useWaitForI18n()
+
+  if (!isReady) {
+    return null
+  }
+
+  return <h2>React Subscription Canceled</h2>
+}
+
+export default Root

+ 13 - 0
services/web/frontend/js/features/subscription/components/dashboard/root.tsx

@@ -0,0 +1,13 @@
+import useWaitForI18n from '../../../../shared/hooks/use-wait-for-i18n'
+
+function Root() {
+  const { isReady } = useWaitForI18n()
+
+  if (!isReady) {
+    return null
+  }
+
+  return <h2>React Subscription Dashboard</h2>
+}
+
+export default Root

+ 13 - 0
services/web/frontend/js/features/subscription/components/new/root.tsx

@@ -0,0 +1,13 @@
+import useWaitForI18n from '../../../../shared/hooks/use-wait-for-i18n'
+
+function Root() {
+  const { isReady } = useWaitForI18n()
+
+  if (!isReady) {
+    return null
+  }
+
+  return <h2>React Subscription New</h2>
+}
+
+export default Root

+ 13 - 0
services/web/frontend/js/features/subscription/components/successful-subscription/root.tsx

@@ -0,0 +1,13 @@
+import useWaitForI18n from '../../../../shared/hooks/use-wait-for-i18n'
+
+function Root() {
+  const { isReady } = useWaitForI18n()
+
+  if (!isReady) {
+    return null
+  }
+
+  return <h2>React Subscription Success</h2>
+}
+
+export default Root

+ 7 - 0
services/web/frontend/js/pages/user/membership/groups.js

@@ -0,0 +1,7 @@
+import ReactDOM from 'react-dom'
+import Root from '../../../features/membership/components/groups-root'
+
+const element = document.getElementById('subscription-manage-groups-root')
+if (element) {
+  ReactDOM.render(<Root />, element)
+}

+ 11 - 0
services/web/frontend/js/pages/user/subscription/base.js

@@ -0,0 +1,11 @@
+import 'jquery'
+import 'bootstrap'
+import './../../../utils/meta'
+import './../../../utils/webpack-public-path'
+import './../../../infrastructure/error-reporter'
+import './../../../i18n'
+import '../../../cdn-load-test'
+import '../../../features/contact-form'
+import '../../../features/event-tracking'
+import '../../../features/cookie-banner'
+import '../../../features/link-helpers/slow-link'

+ 8 - 0
services/web/frontend/js/pages/user/subscription/canceled-subscription.js

@@ -0,0 +1,8 @@
+import './base'
+import ReactDOM from 'react-dom'
+import Root from '../../../features/subscription/components/canceled-subscription/root'
+
+const element = document.getElementById('subscription-canceled-root')
+if (element) {
+  ReactDOM.render(<Root />, element)
+}

+ 8 - 0
services/web/frontend/js/pages/user/subscription/dashboard.js

@@ -0,0 +1,8 @@
+import './base'
+import ReactDOM from 'react-dom'
+import Root from '../../../features/subscription/components/dashboard/root'
+
+const element = document.getElementById('subscription-dashboard-root')
+if (element) {
+  ReactDOM.render(<Root />, element)
+}

+ 8 - 0
services/web/frontend/js/pages/user/subscription/new.js

@@ -0,0 +1,8 @@
+import './base'
+import ReactDOM from 'react-dom'
+import Root from '../../../features/subscription/components/new/root'
+
+const element = document.getElementById('subscription-new-root')
+if (element) {
+  ReactDOM.render(<Root />, element)
+}

+ 8 - 0
services/web/frontend/js/pages/user/subscription/successful-subscription.js

@@ -0,0 +1,8 @@
+import './base'
+import ReactDOM from 'react-dom'
+import Root from '../../../features/subscription/components/successful-subscription/root'
+
+const element = document.getElementById('subscription-success-root')
+if (element) {
+  ReactDOM.render(<Root />, element)
+}

+ 1 - 1
services/web/test/unit/src/Subscription/SubscriptionControllerTests.js

@@ -399,7 +399,7 @@ describe('SubscriptionController', function () {
         }
         }
       )
       )
       this.res.render = (url, variables) => {
       this.res.render = (url, variables) => {
-        url.should.equal('subscriptions/successful_subscription')
+        url.should.equal('subscriptions/successful-subscription')
         assert.deepEqual(variables, {
         assert.deepEqual(variables, {
           title: 'thank_you',
           title: 'thank_you',
           personalSubscription: 'foo',
           personalSubscription: 'foo',

+ 15 - 12
services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js

@@ -70,12 +70,19 @@ describe('UserMembershipController', function () {
       addUser: sinon.stub().yields(null, this.newUser),
       addUser: sinon.stub().yields(null, this.newUser),
       removeUser: sinon.stub().yields(null),
       removeUser: sinon.stub().yields(null),
     }
     }
+    this.SplitTestHandler = {
+      promises: {
+        getAssignment: sinon.stub().resolves({ variant: 'default' }),
+      },
+      getAssignment: sinon.stub().yields(null, { variant: 'default' }),
+    }
     return (this.UserMembershipController = SandboxedModule.require(
     return (this.UserMembershipController = SandboxedModule.require(
       modulePath,
       modulePath,
       {
       {
         requires: {
         requires: {
           './UserMembershipErrors': { UserIsManagerError },
           './UserMembershipErrors': { UserIsManagerError },
           '../Authentication/SessionManager': this.SessionManager,
           '../Authentication/SessionManager': this.SessionManager,
+          '../SplitTests/SplitTestHandler': this.SplitTestHandler,
           './UserMembershipHandler': this.UserMembershipHandler,
           './UserMembershipHandler': this.UserMembershipHandler,
         },
         },
       }
       }
@@ -88,21 +95,20 @@ describe('UserMembershipController', function () {
       return (this.req.entityConfig = EntityConfigs.group)
       return (this.req.entityConfig = EntityConfigs.group)
     })
     })
 
 
-    it('get users', function (done) {
-      return this.UserMembershipController.index(this.req, {
+    it('get users', async function () {
+      return await this.UserMembershipController.index(this.req, {
         render: () => {
         render: () => {
           sinon.assert.calledWithMatch(
           sinon.assert.calledWithMatch(
             this.UserMembershipHandler.getUsers,
             this.UserMembershipHandler.getUsers,
             this.subscription,
             this.subscription,
             { modelName: 'Subscription' }
             { modelName: 'Subscription' }
           )
           )
-          return done()
         },
         },
       })
       })
     })
     })
 
 
-    it('render group view', function (done) {
-      return this.UserMembershipController.index(this.req, {
+    it('render group view', async function () {
+      return await this.UserMembershipController.index(this.req, {
         render: (viewPath, viewParams) => {
         render: (viewPath, viewParams) => {
           expect(viewPath).to.equal('user_membership/index')
           expect(viewPath).to.equal('user_membership/index')
           expect(viewParams.users).to.deep.equal(this.users)
           expect(viewParams.users).to.deep.equal(this.users)
@@ -111,14 +117,13 @@ describe('UserMembershipController', function () {
           expect(viewParams.paths.addMember).to.equal(
           expect(viewParams.paths.addMember).to.equal(
             `/manage/groups/${this.subscription._id}/invites`
             `/manage/groups/${this.subscription._id}/invites`
           )
           )
-          return done()
         },
         },
       })
       })
     })
     })
 
 
-    it('render group managers view', function (done) {
+    it('render group managers view', async function () {
       this.req.entityConfig = EntityConfigs.groupManagers
       this.req.entityConfig = EntityConfigs.groupManagers
-      return this.UserMembershipController.index(this.req, {
+      return await this.UserMembershipController.index(this.req, {
         render: (viewPath, viewParams) => {
         render: (viewPath, viewParams) => {
           expect(viewPath).to.equal('user_membership/index')
           expect(viewPath).to.equal('user_membership/index')
           expect(viewParams.groupSize).to.equal(undefined)
           expect(viewParams.groupSize).to.equal(undefined)
@@ -127,22 +132,20 @@ describe('UserMembershipController', function () {
             'managers_management'
             'managers_management'
           )
           )
           expect(viewParams.paths.exportMembers).to.be.undefined
           expect(viewParams.paths.exportMembers).to.be.undefined
-          return done()
         },
         },
       })
       })
     })
     })
 
 
-    it('render institution view', function (done) {
+    it('render institution view', async function () {
       this.req.entity = this.institution
       this.req.entity = this.institution
       this.req.entityConfig = EntityConfigs.institution
       this.req.entityConfig = EntityConfigs.institution
-      return this.UserMembershipController.index(this.req, {
+      return await this.UserMembershipController.index(this.req, {
         render: (viewPath, viewParams) => {
         render: (viewPath, viewParams) => {
           expect(viewPath).to.equal('user_membership/index')
           expect(viewPath).to.equal('user_membership/index')
           expect(viewParams.name).to.equal('Test Institution Name')
           expect(viewParams.name).to.equal('Test Institution Name')
           expect(viewParams.groupSize).to.equal(undefined)
           expect(viewParams.groupSize).to.equal(undefined)
           expect(viewParams.translations.title).to.equal('institution_account')
           expect(viewParams.translations.title).to.equal('institution_account')
           expect(viewParams.paths.exportMembers).to.be.undefined
           expect(viewParams.paths.exportMembers).to.be.undefined
-          return done()
         },
         },
       })
       })
     })
     })