Browse Source

Merge pull request #10793 from overleaf/mj-split-test-cleanup

Split test clean-up

GitOrigin-RevId: 7dd6178487022cbefcbc85797dacc3f3fbfa17e2
Mathias Jakobsen 3 years ago
parent
commit
38cc3394e3
27 changed files with 53 additions and 622 deletions
  1. 0 76
      services/web/app/src/Features/Project/ProjectController.js
  2. 2 20
      services/web/app/src/Features/Project/ProjectListController.js
  3. 1 16
      services/web/app/src/Features/StaticPages/HomeController.js
  4. 1 22
      services/web/app/src/Features/Subscription/SubscriptionController.js
  5. 1 7
      services/web/app/src/Features/User/UserCreator.js
  6. 6 2
      services/web/app/src/Features/User/UserPrimaryEmailCheckHandler.js
  7. 7 8
      services/web/app/views/project/editor/left-menu.pug
  8. 0 1
      services/web/app/views/project/editor/meta.pug
  9. 1 4
      services/web/app/views/subscriptions/_plans_page_mixins.pug
  10. 0 357
      services/web/app/views/subscriptions/new.pug
  11. 1 4
      services/web/app/views/subscriptions/plans-marketing/_group_plan_modal.pug
  12. 1 4
      services/web/app/views/subscriptions/plans-marketing/_mixins.pug
  13. 1 5
      services/web/app/views/subscriptions/plans-marketing/_tables.pug
  14. 1 12
      services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx
  15. 0 8
      services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx
  16. 0 2
      services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js
  17. 0 2
      services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js
  18. 0 2
      services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js
  19. 0 33
      services/web/frontend/js/shared/components/stop-on-first-error-badge.tsx
  20. 5 6
      services/web/frontend/js/shared/context/local-compile-context.js
  21. 1 0
      services/web/frontend/js/shared/context/user-context.js
  22. 9 30
      services/web/test/acceptance/src/PrimaryEmailCheckTests.js
  23. 5 0
      services/web/test/acceptance/src/SessionTests.js
  24. 1 0
      services/web/test/acceptance/src/helpers/User.js
  25. 3 0
      services/web/test/frontend/helpers/editor-providers.js
  26. 5 0
      services/web/test/unit/src/Project/ProjectControllerTests.js
  27. 1 1
      services/web/test/unit/src/Subscription/SubscriptionControllerTests.js

+ 0 - 76
services/web/app/src/Features/Project/ProjectController.js

@@ -552,24 +552,6 @@ const ProjectController = {
             }
           )
         },
-        primaryEmailCheckActive(cb) {
-          SplitTestHandler.getAssignment(
-            req,
-            res,
-            'primary-email-check',
-            (err, assignment) => {
-              if (err) {
-                logger.warn(
-                  { err },
-                  'failed to get "primary-email-check" split test assignment'
-                )
-                cb(null, false)
-              } else {
-                cb(null, assignment.variant === 'active')
-              }
-            }
-          )
-        },
         survey(cb) {
           SurveyHandler.getSurvey(userId, (err, survey) => {
             if (err) {
@@ -591,14 +573,12 @@ const ProjectController = {
           notifications,
           user,
           userEmailsData,
-          primaryEmailCheckActive,
           groupsAndEnterpriseBannerAssignment,
           userIsMemberOfGroupSubscription,
         } = results
 
         if (
           user &&
-          primaryEmailCheckActive &&
           UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user)
         ) {
           return res.redirect('/user/emails/primary-email-check')
@@ -997,37 +977,6 @@ const ProjectController = {
             }
           )
         },
-        dictionaryEditorAssignment(cb) {
-          SplitTestHandler.getAssignment(
-            req,
-            res,
-            'dictionary-editor',
-            {},
-            (error, assignment) => {
-              // do not fail editor load if assignment fails
-              if (error) {
-                cb(null, { variant: 'default' })
-              } else {
-                cb(null, assignment)
-              }
-            }
-          )
-        },
-        interstitialPaymentFromPaywallAssignment(cb) {
-          SplitTestHandler.getAssignment(
-            req,
-            res,
-            'interstitial-payment-from-paywall',
-            (error, assignment) => {
-              // do not fail editor load if assignment fails
-              if (error) {
-                cb(null, { variant: 'default' })
-              } else {
-                cb(null, assignment)
-              }
-            }
-          )
-        },
         latexLogParserAssignment(cb) {
           SplitTestHandler.getAssignment(
             req,
@@ -1043,25 +992,6 @@ const ProjectController = {
             }
           )
         },
-        compileTimeWarningAssignment: [
-          'user',
-          (results, cb) => {
-            if (results.user?.features?.compileTimeout <= 60) {
-              SplitTestHandler.getAssignment(
-                req,
-                res,
-                'compile-time-warning',
-                {},
-                () => {
-                  // do not fail editor load if assignment fails
-                  cb()
-                }
-              )
-            } else {
-              cb()
-            }
-          },
-        ],
         linkSharingUpgradePromptAssignment(cb) {
           SplitTestHandler.getAssignment(
             req,
@@ -1152,7 +1082,6 @@ const ProjectController = {
           brandVariation,
           newSourceEditorAssignment,
           pdfjsAssignment,
-          dictionaryEditorAssignment,
           editorLeftMenuAssignment,
         }
       ) => {
@@ -1251,10 +1180,6 @@ const ProjectController = {
                 : ['all']
             const galileoPromptWords = req.query?.galileoPromptWords || ''
 
-            const dictionaryEditorEnabled =
-              !Features.hasFeature('saas') ||
-              dictionaryEditorAssignment?.variant === 'enabled'
-
             // Persistent upgrade prompts
             // in header & in share project modal
             const showUpgradePrompt =
@@ -1329,7 +1254,6 @@ const ProjectController = {
               wsUrl,
               showSupport: Features.hasFeature('support'),
               pdfjsVariant: pdfjsAssignment.variant,
-              dictionaryEditorEnabled,
               debugPdfDetach,
               showNewSourceEditorOption,
               showSymbolPalette,

+ 2 - 20
services/web/app/src/Features/Project/ProjectListController.js

@@ -124,26 +124,8 @@ async function projectListReactPage(req, res, next) {
       logger.err({ err: error, userId }, 'Failed to load the active survey')
     }
 
-    try {
-      const assignment = await SplitTestHandler.promises.getAssignment(
-        req,
-        res,
-        'primary-email-check'
-      )
-      const primaryEmailCheckActive = assignment.variant === 'active'
-
-      if (
-        user &&
-        primaryEmailCheckActive &&
-        UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user)
-      ) {
-        return res.redirect('/user/emails/primary-email-check')
-      }
-    } catch (error) {
-      logger.warn(
-        { err: error },
-        'failed to get "primary-email-check" split test assignment'
-      )
+    if (user && UserPrimaryEmailCheckHandler.requiresPrimaryEmailCheck(user)) {
+      return res.redirect('/user/emails/primary-email-check')
     }
   }
 

+ 1 - 16
services/web/app/src/Features/StaticPages/HomeController.js

@@ -43,22 +43,7 @@ module.exports = HomeController = {
 
   async home(req, res) {
     if (Features.hasFeature('homepage') && homepageExists) {
-      try {
-        const homeRegistration = await SplitTestHandler.promises.getAssignment(
-          req,
-          res,
-          'home-registration'
-        )
-        const removeRegistration = homeRegistration.variant
-        return res.render('external/home/v2', {
-          removeRegistration,
-        })
-      } catch (err) {
-        logger.error({ err }, err.message)
-        return res.render('external/home/v2', {
-          removeRegistration: 'default',
-        })
-      }
+      return res.render('external/home/v2')
     } else {
       return res.redirect('/login')
     }

+ 1 - 22
services/web/app/src/Features/Subscription/SubscriptionController.js

@@ -80,17 +80,6 @@ async function plansPage(req, res) {
 
   AnalyticsManager.recordEventForSession(req.session, 'plans-page-view')
 
-  const standardPlanNameAssignment =
-    await SplitTestHandler.promises.getAssignment(
-      req,
-      res,
-      'standard-plan-name'
-    )
-
-  const useNewPlanName =
-    standardPlanNameAssignment &&
-    standardPlanNameAssignment.variant === 'new-plan-name'
-
   const template = newPlansPageVariantV2
     ? 'subscriptions/plans-marketing-v2'
     : 'subscriptions/plans-marketing'
@@ -108,7 +97,6 @@ async function plansPage(req, res) {
     groupPlanModalOptions,
     groupPlanModalDefaults,
     newPlansPageVariantV2,
-    useNewPlanName,
     initialLocalizedGroupPrice:
       SubscriptionHelper.generateInitialLocalizedGroupPrice(
         recommendedCurrency
@@ -151,13 +139,6 @@ async function paymentPage(req, res) {
       if (recommendedCurrency && currency == null) {
         currency = recommendedCurrency
       }
-      const assignment = await SplitTestHandler.promises.getAssignment(
-        req,
-        res,
-        'payment-page'
-      )
-      const useUpdatedPaymentPage =
-        assignment && assignment.variant === 'updated-payment-page'
 
       const refreshedPaymentPageAssignment =
         await SplitTestHandler.promises.getAssignment(
@@ -177,9 +158,7 @@ async function paymentPage(req, res) {
 
       const template = useRefreshedPaymentPage
         ? 'subscriptions/new-refreshed'
-        : useUpdatedPaymentPage
-        ? 'subscriptions/new-updated'
-        : 'subscriptions/new'
+        : 'subscriptions/new-updated'
 
       res.render(template, {
         title: 'subscribe',

+ 1 - 7
services/web/app/src/Features/User/UserCreator.js

@@ -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')
@@ -38,13 +37,8 @@ async function _addAffiliation(user, affiliationOptions) {
 
 async function recordRegistrationEvent(user) {
   try {
-    const homeRegistrationAssignment =
-      await SplitTestHandler.promises.getAssignmentForUser(
-        user._id,
-        'home-registration'
-      )
     const segmentation = {
-      'home-registration': homeRegistrationAssignment.variant,
+      'home-registration': 'default',
     }
     if (user.thirdPartyIdentifiers && user.thirdPartyIdentifiers.length > 0) {
       segmentation.provider = user.thirdPartyIdentifiers[0].providerId

+ 6 - 2
services/web/app/src/Features/User/UserPrimaryEmailCheckHandler.js

@@ -6,8 +6,12 @@ function requiresPrimaryEmailCheck({
   lastPrimaryEmailCheck,
   signUpDate,
 }) {
-  const hasExpired = date =>
-    Date.now() - date.getTime() > Settings.primary_email_check_expiration
+  const hasExpired = date => {
+    if (!date) {
+      return true
+    }
+    return Date.now() - date.getTime() > Settings.primary_email_check_expiration
+  }
 
   const primaryEmailConfirmedAt = emails.find(
     emailEntry => emailEntry.email === email

+ 7 - 8
services/web/app/views/project/editor/left-menu.pug

@@ -117,15 +117,14 @@ aside#left-menu.full-size(
 								value=language.code
 							)= language.name
 
-			if dictionaryEditorEnabled
-				.form-controls(ng-controller="DictionaryModalController")
-					label #{translate("dictionary")}
-					button.btn.btn-secondary.btn-xs(ng-click="openModal()") #{translate("edit")}
+			.form-controls(ng-controller="DictionaryModalController")
+				label #{translate("dictionary")}
+				button.btn.btn-secondary.btn-xs(ng-click="openModal()") #{translate("edit")}
 
-					dictionary-modal(
-						handle-hide="handleHide"
-						show="show"
-					)
+				dictionary-modal(
+					handle-hide="handleHide"
+					show="show"
+				)
 
 			.form-controls
 				label(for="autoComplete") #{translate("auto_complete")}

+ 0 - 1
services/web/app/views/project/editor/meta.pug

@@ -28,7 +28,6 @@ meta(name="ol-galileoFeatures" data-type="json" content=galileoFeatures)
 meta(name="ol-detachRole" data-type="string" content=detachRole)
 meta(name="ol-allowedImageNames" data-type="json" content=allowedImageNames)
 meta(name="ol-languages" data-type="json" content=languages)
-meta(name="ol-dictionaryEditorEnabled" data-type="boolean" content=dictionaryEditorEnabled)
 meta(name="ol-editorThemes" data-type="json" content=editorThemes)
 meta(name="ol-legacyEditorThemes" data-type="json" content=legacyEditorThemes)
 meta(name="ol-showUpgradePrompt" data-type="boolean" content=showUpgradePrompt)

+ 1 - 4
services/web/app/views/subscriptions/_plans_page_mixins.pug

@@ -206,10 +206,7 @@ mixin allCardsAndControls(controlsRowSpaced, listLocation)
 							.best-value
 								strong #{translate('best_value')}
 							.card-header
-								if (useNewPlanName)
-									h2 #{translate("standard")}
-								else
-									h2 #{translate("collaborator")}
+								h2 #{translate("standard")}
 							h5.tagline #{translate("tagline_collaborator")}
 							.circle
 								+price_collaborator

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

@@ -1,357 +0,0 @@
-extends ../layout
-
-include ./_new_mixins
-
-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 head-scripts
-	script(type="text/javascript", nonce=scriptNonce, src="https://js.recurly.com/v4/recurly.js")
-
-block content
-	main.content.content-alt#main-content
-		.container(ng-controller="NewSubscriptionController" ng-cloak)
-			.row.card-group
-				.col-md-5.col-md-push-4
-					.card.card-highlighted.card-border(ng-hide="threeDSecureFlow")
-						.alert.alert-danger(ng-show="recurlyLoadError")
-							strong #{translate('payment_provider_unreachable_error')}
-						.page-header(ng-hide="recurlyLoadError")
-							.row
-								.col-xs-9
-									h2 {{planName}}
-								.col-xs-3
-									div.dropdown.changePlanButton.pull-right(ng-cloak, dropdown)
-										a.btn.btn-default.dropdown-toggle(
-											href="#",
-											data-toggle="dropdown",
-											dropdown-toggle
-										)
-											| {{currencyCode}} ({{allCurrencies[currencyCode]['symbol']}})
-											span.caret
-										ul.dropdown-menu(role="menu")
-											li(ng-repeat="(currency, value) in availableCurrencies")
-												a(
-													ng-click="changeCurrency(currency)",
-												) {{currency}} ({{value['symbol']}})
-							.row(ng-if="planCode == 'student-annual' || planCode == 'student-monthly' || planCode == 'student_free_trial_7_days'")
-								.col-xs-12
-									p.student-disclaimer #{translate('student_disclaimer')}
-
-							hr.thin
-							.row
-								.col-md-12.text-center
-									div(ng-if="trialLength")
-										span !{translate("first_few_days_free", {trialLen:'{{trialLength}}'})}
-										span(ng-if="coupon.discountMonths && coupon.discountRate") &nbsp; -  {{coupon.discountMonths}} #{translate("month")}s {{coupon.discountRate}}% Off
-
-									div(ng-if="recurlyPrice")
-										- var priceVars = { price: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}"};
-										span(ng-if="!coupon.singleUse && monthlyBilling")
-											| !{translate("x_price_per_month", priceVars, ['strong'] )}
-										span(ng-if="!coupon.singleUse && !monthlyBilling")
-											| !{translate("x_price_per_year", priceVars, ['strong'] )}
-										span(ng-if="coupon.singleUse && monthlyBilling")
-											| !{translate("x_price_for_first_month", priceVars, ['strong'] )}
-										span(ng-if="coupon.singleUse && !monthlyBilling")
-											| !{translate("x_price_for_first_year", priceVars, ['strong'] )}
-
-									div(ng-if="coupon && coupon.normalPrice")
-										- var noDiscountPriceAngularExp = "{{ availableCurrencies[currencyCode]['symbol']}}{{coupon.normalPrice | number:2 }}";
-										span.small(ng-if="!coupon.singleUse && monthlyBilling")
-											| !{translate("normally_x_price_per_month", { price: noDiscountPriceAngularExp } )}
-										span.small(ng-if="!coupon.singleUse && !monthlyBilling")
-											| !{translate("normally_x_price_per_year", { price: noDiscountPriceAngularExp } )}
-										span.small(ng-if="coupon.singleUse && monthlyBilling")
-											| !{translate("then_x_price_per_month", { price: noDiscountPriceAngularExp } )}
-										span.small(ng-if="coupon.singleUse && !monthlyBilling")
-											| !{translate("then_x_price_per_year", { price: noDiscountPriceAngularExp } )}
-
-						.row(ng-hide="recurlyLoadError")
-							div()
-								.col-md-12()
-									form(
-										name="simpleCCForm"
-										novalidate
-									)
-
-										div.payment-method-toggle
-											a.payment-method-toggle-switch(
-												href
-												ng-click="setPaymentMethod('credit_card');"
-												ng-class="paymentMethod.value === 'credit_card' ? 'payment-method-toggle-switch-selected' : ''"
-											)
-												i.fa.fa-cc-mastercard.fa-2x(aria-hidden="true")
-												span &nbsp;
-												i.fa.fa-cc-visa.fa-2x(aria-hidden="true")
-												span &nbsp;
-												i.fa.fa-cc-amex.fa-2x(aria-hidden="true")
-												span.sr-only Pay with Mastercard, Visa, or Amex
-											a.payment-method-toggle-switch(
-												href
-												ng-click="setPaymentMethod('paypal');"
-												ng-class="paymentMethod.value === 'paypal' ? 'payment-method-toggle-switch-selected' : ''"
-											)
-												i.fa.fa-cc-paypal.fa-2x(aria-hidden="true")
-												span.sr-only Pay with PayPal
-
-										.alert.alert-warning.small(ng-show="genericError")
-											strong {{genericError}}
-
-										.alert.alert-warning.small(ng-show="couponError")
-											strong {{couponError}}
-
-										div(ng-show="paymentMethod.value === 'credit_card'")
-											.row
-												.col-xs-6
-													.form-group(ng-class="validation.errorFields.first_name || inputHasError(simpleCCForm.firstName) ? 'has-error' : ''")
-														label(for="first-name") #{translate('first_name')}
-														input#first-name.form-control(
-															type="text"
-															maxlength='255'
-															data-recurly="first_name"
-															name="firstName"
-															ng-model="data.first_name"
-															required
-														)
-														span.input-feedback-message(ng-if="simpleCCForm.firstName.$error.required") #{translate('this_field_is_required')}
-												.col-xs-6
-													.form-group(ng-class="validation.errorFields.last_name || inputHasError(simpleCCForm.lastName)? 'has-error' : ''")
-														label(for="last-name") #{translate('last_name')}
-														input#last-name.form-control(
-															type="text"
-															maxlength='255'
-															data-recurly="last_name"
-															name="lastName"
-															ng-model="data.last_name"
-															required
-														)
-														span.input-feedback-message(ng-if="simpleCCForm.lastName.$error.required") #{translate('this_field_is_required')}
-
-											.form-group(ng-class="validation.errorFields.number ? 'has-error' : ''")
-												label(for="card-no") #{translate("credit_card_number")}
-												div#card-no(
-													type="text"
-													name="ccNumber"
-													data-recurly='number'
-												)
-
-											.row
-												.col-xs-3
-													.form-group.has-feedback(ng-class="validation.errorFields.month ? 'has-error' : ''")
-														label(for="month").capitalised #{translate("month")}
-														div(
-															type="number"
-															name="month"
-															data-recurly="month"
-														)
-												.col-xs-3
-													.form-group.has-feedback(ng-class="validation.errorFields.year ? 'has-error' : ''")
-														label(for="year").capitalised #{translate("year")}
-														div(
-															type="number"
-															name="year"
-															data-recurly="year"
-														)
-
-												.col-xs-6
-													.form-group.has-feedback(ng-class="validation.errorFields.cvv ? 'has-error' : ''")
-														label #{translate("security_code")}
-														div(
-															type="number"
-															ng-model="data.cvv"
-															data-recurly="cvv"
-															name="cvv"
-															cc-format-sec-code
-														)
-														.form-control-feedback
-															a.form-helper(
-																href
-																tabindex="-1"
-																tooltip-template="'cvv-tooltip-tpl.html'"
-																tooltip-trigger="mouseenter"
-																tooltip-append-to-body="true"
-															) ?
-
-										div
-											.row
-												.col-xs-12
-													.form-group(ng-class="validation.errorFields.address1 || inputHasError(simpleCCForm.address1) ? 'has-error' : ''")
-														label(for="address-line-1") #{translate('address_line_1')}
-														input#address-line-1.form-control(
-															type="text"
-															maxlength="50"
-															data-recurly="address1"
-															name="address1"
-															ng-model="data.address1"
-															required
-														)
-														span.input-feedback-message(ng-if="simpleCCForm.address1.$error.required") #{translate('this_field_is_required')}
-
-											.row
-												.col-xs-12
-													.form-group.has-feedback(ng-class="validation.errorFields.address2 ? 'has-error' : ''")
-														label(for="address-line-2") #{translate('address_line_2')}
-														input#address-line-2.form-control(
-															type="text"
-															maxlength="50"
-															data-recurly="address2"
-															name="address2"
-															ng-model="data.address2"
-														)
-
-											.row
-												.col-xs-4
-													.form-group(ng-class="validation.errorFields.postal_code || inputHasError(simpleCCForm.postalCode) ? 'has-error' : ''")
-														label(for="postal-code") #{translate('postal_code')}
-														input#postal-code.form-control(
-															type="text"
-															maxlength="20"
-															data-recurly="postal_code"
-															name="postalCode"
-															ng-model="data.postal_code"
-															required
-														)
-														span.input-feedback-message(ng-if="simpleCCForm.postalCode.$error.required") #{translate('this_field_is_required')}
-
-												.col-xs-8
-													.form-group(ng-class="validation.errorFields.country || inputHasError(simpleCCForm.country) ? 'has-error' : ''")
-														label(for="country") #{translate('country')}
-														select#country.form-control(
-															data-recurly="country"
-															ng-model="data.country"
-															name="country"
-															ng-change="updateCountry()"
-															ng-selected="{{country.code == data.country}}"
-															ng-model-options="{ debounce: 200 }"
-															required
-														)
-															option(value='', disabled) #{translate("country")}
-															option(value='-', disabled) --------------
-															option(ng-repeat="country in countries" ng-bind-html="country.name" value="{{country.code}}")
-														span.input-feedback-message(ng-if="simpleCCForm.country.$error.required") #{translate('this_field_is_required')}
-
-											.form-group
-												.checkbox
-													label
-														input(
-															type="checkbox"
-															ng-model="ui.addCompanyDetails"
-														)
-														|
-														| #{translate("add_company_details")}
-
-											.form-group(ng-show="ui.addCompanyDetails")
-												label(for="company-name") #{translate("company_name")}
-												input#company-name.form-control(
-													type="text"
-													name="companyName"
-													ng-model="data.company"
-												)
-
-											.form-group(ng-show="ui.addCompanyDetails && taxes.length")
-												label(for="vat-number") #{translate("vat_number")}
-												input#vat-number.form-control(
-													type="text"
-													name="vatNumber"
-													ng-model="data.vat_number"
-													ng-blur="applyVatNumber()"
-												)
-
-											if (showCouponField)
-												.form-group
-													label(for="coupon-code") #{translate('coupon_code')}
-													input#coupon-code.form-control(
-														type="text"
-														ng-blur="applyCoupon()"
-														ng-model="data.coupon"
-													)
-
-											p(ng-if="paymentMethod.value === 'paypal'") #{translate("paypal_upgrade")}
-
-											div.price-breakdown(
-												ng-show="taxes.length"
-											)
-												- var priceBreakdownVars = { total: "{{ availableCurrencies[currencyCode]['symbol'] }}{{ recurlyPrice.total }}", subtotal: "{{availableCurrencies[currencyCode]['symbol']}}{{ recurlyPrice.subtotal }}", tax: "{{availableCurrencies[currencyCode]['symbol']}}{{ recurlyPrice.tax }}" };
-												hr.thin
-												span
-													| Total:
-													|
-													span(ng-if="!coupon.singleUse && monthlyBilling")
-														| !{translate("x_price_per_month_tax", priceBreakdownVars, ['strong'] )}
-													span(ng-if="!coupon.singleUse && !monthlyBilling")
-														| !{translate("x_price_per_year_tax", priceBreakdownVars, ['strong'] )}
-													span(ng-if="coupon.singleUse && monthlyBilling")
-														| !{translate("x_price_for_first_month_tax", priceBreakdownVars, ['strong'] )}
-													span(ng-if="coupon.singleUse && !monthlyBilling")
-														| !{translate("x_price_for_first_year_tax", priceBreakdownVars, ['strong'] )}
-												hr.thin
-
-											div.payment-submit
-												button.btn.btn-primary.btn-block(
-													ng-click="submit()"
-													ng-disabled="processing  || !isFormValid(simpleCCForm);"
-												)
-													span(ng-show="processing")
-														i.fa.fa-spinner.fa-spin(aria-hidden="true")
-														span.sr-only #{translate('processing')}
-														| &nbsp;
-													span(ng-if="paymentMethod.value === 'credit_card'")
-														| {{ trialLength ? '#{translate("upgrade_cc_btn")}' : '#{translate("upgrade_now")}'}}
-													span(ng-if="paymentMethod.value !== 'credit_card'") #{translate("upgrade_paypal_btn")}
-
-											p.tos-agreement-notice !{translate("by_subscribing_you_agree_to_our_terms_of_service", {}, [{name: 'a', attrs: {href: '/legal#Terms', target:'_blank', rel:'noopener noreferrer'}}])}
-
-					div.three-d-secure-container.card.card-highlighted.card-border(ng-show="threeDSecureFlow")
-						.alert.alert-info.small(aria-live="assertive")
-							strong #{translate('card_must_be_authenticated_by_3dsecure')}
-						div.three-d-secure-recurly-container
-
-
-
-				.col-md-3.col-md-pull-4
-					.card.card-first
-						.paymentPageFeatures
-							h3 #{translate("unlimited_projects")}
-							p #{translate("create_unlimited_projects")}
-
-							h3
-								if plan.features.collaborators == -1
-									- var collaboratorCount = 'Unlimited'
-								else
-									- var collaboratorCount = plan.features.collaborators
-								if plan.features.collaborators == 1
-									| #{translate("collabs_per_proj_single", {collabcount:collaboratorCount})}
-								else
-									| #{translate("collabs_per_proj", {collabcount:collaboratorCount})}
-							p #{translate("work_on_single_version")}. #{translate("view_collab_edits_in_real_time")}
-
-							h3 #{translate("full_doc_history")}
-							p.track-changes-example
-								| #{translate("see_what_has_been")} #[span.added #{translate("added")}]
-								| &nbsp;#{translate("and")} #[span.removed #{translate("removed")}].
-							p
-								| #{translate("restore_to_any_older_version")}.
-
-							h3 #{translate("sync_to_dropbox")}
-							p
-								| #{translate("acces_work_from_anywhere")}.
-								| #{translate("work_offline_and_sync_with_dropbox")}.
-
-							hr
-
-							p.small.text-center(ng-non-bindable) !{translate("cancel_anytime", { appName:'{{settings.appName}}' })} 
-
-	script(type="text/javascript", nonce=scriptNonce).
-		ga('send', 'event', 'pageview', 'payment_form', "#{plan_code}")
-
-	script(
-		type="text/ng-template"
-		id="cvv-tooltip-tpl.html"
-	)
-		p !{translate("for_visa_mastercard_and_discover", {}, ['strong', 'strong', 'strong'])}
-		p !{translate("for_american_express", {}, ['strong', 'strong', 'strong'])}
-
-	+studentCheckModal

+ 1 - 4
services/web/app/views/subscriptions/plans-marketing/_group_plan_modal.pug

@@ -51,10 +51,7 @@ div.modal.fade(tabindex="-1" role="dialog" data-ol-group-plan-modal)
 												value=plan_code.code
 												data-ol-group-plan-code=plan_code.code
 											)
-											if (useNewPlanName && plan_code.code === 'collaborator')
-												span Standard
-											else
-												span #{plan_code.display}
+											span #{plan_code.display}
 								.form-group
 									label(for='size')
 										| Number of users

+ 1 - 4
services/web/app/views/subscriptions/plans-marketing/_mixins.pug

@@ -245,10 +245,7 @@ mixin allCardsAndControls(controlsRowSpaced, listLocation)
 								.best-value
 									strong #{translate('best_value')}
 								.card-header
-									if (useNewPlanName)
-										h2 #{translate("standard")}
-									else
-										h2 #{translate("collaborator")}
+									h2 #{translate("standard")}
 								.card-byline
 									h5.tagline #{translate("tagline_collaborator")}
 								.circle

+ 1 - 5
services/web/app/views/subscriptions/plans-marketing/_tables.pug

@@ -5,11 +5,7 @@ mixin table_premium
 			th
 			th #{translate("free")}
 			th #{translate("personal")}
-			th
-				if (useNewPlanName)
-					| #{translate("standard")}
-				else
-					| #{translate("collaborator")}
+			th #{translate("standard")}
 				.outer.outer-top
 					.outer-content
 						.best-value

+ 1 - 12
services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx

@@ -2,7 +2,6 @@ import { useCallback } from 'react'
 import { useTranslation } from 'react-i18next'
 import { Alert, Button, Modal } from 'react-bootstrap'
 import Icon from '../../../shared/components/icon'
-import SplitTestBadge from '../../../shared/components/split-test-badge'
 import Tooltip from '../../../shared/components/tooltip'
 import useAsync from '../../../shared/hooks/use-async'
 import { postJSON } from '../../../infrastructure/fetch-json'
@@ -38,17 +37,7 @@ export default function DictionaryModalContent({
   return (
     <>
       <Modal.Header closeButton>
-        <Modal.Title>
-          {t('edit_dictionary')}{' '}
-          <SplitTestBadge
-            splitTestName="dictionary-editor"
-            displayOnVariants={['enabled']}
-            tooltip={{
-              placement: 'bottom',
-              className: 'tooltip-wide',
-            }}
-          />
-        </Modal.Title>
+        <Modal.Title>{t('edit_dictionary')}</Modal.Title>
       </Modal.Header>
 
       <Modal.Body>

+ 0 - 8
services/web/frontend/js/features/editor-left-menu/components/settings/settings-dictionary.tsx

@@ -1,19 +1,11 @@
 import { useState } from 'react'
 import { Button } from 'react-bootstrap'
 import { useTranslation } from 'react-i18next'
-import getMeta from '../../../../utils/meta'
 import DictionaryModal from '../../../dictionary/components/dictionary-modal'
 
 export default function SettingsDictionary() {
   const { t } = useTranslation()
   const [showModal, setShowModal] = useState(false)
-  const dictionaryEditorEnabled = getMeta(
-    'ol-dictionaryEditorEnabled'
-  ) as boolean
-
-  if (!dictionaryEditorEnabled) {
-    return null
-  }
 
   return (
     <div className="form-group left-menu-setting">

+ 0 - 2
services/web/frontend/js/features/pdf-preview/components/pdf-preview-error.js

@@ -5,7 +5,6 @@ import { Button } from 'react-bootstrap'
 import PdfLogEntry from './pdf-log-entry'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
 import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
-import StopOnFirstErrorBadge from '../../../shared/components/stop-on-first-error-badge'
 
 function PdfPreviewError({ error }) {
   const { t } = useTranslation()
@@ -254,7 +253,6 @@ function TimedOutLogEntry() {
                   />,
                 ]}
               />{' '}
-              <StopOnFirstErrorBadge placement="bottom" />
             </>
           )}
         </li>

+ 0 - 2
services/web/frontend/js/features/pdf-preview/components/stop-on-first-error-prompt.js

@@ -4,7 +4,6 @@ import { Button } from 'react-bootstrap'
 import PdfLogEntry from './pdf-log-entry'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
 import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
-import StopOnFirstErrorBadge from '../../../shared/components/stop-on-first-error-badge'
 
 export default function StopOnFirstErrorPrompt() {
   const { t } = useTranslation()
@@ -22,7 +21,6 @@ export default function StopOnFirstErrorPrompt() {
   return (
     <PdfLogEntry
       headerTitle={t('stop_on_first_error_enabled_title')}
-      headerIcon={<StopOnFirstErrorBadge placement="right" />}
       formattedContent={
         <>
           <Trans

+ 0 - 2
services/web/frontend/js/features/preview/components/preview-logs-pane-max-entries.js

@@ -6,7 +6,6 @@ import PreviewLogEntryHeader from './preview-log-entry-header'
 import Icon from '../../../shared/components/icon'
 import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context'
 import { useStopOnFirstError } from '../../../shared/hooks/use-stop-on-first-error'
-import StopOnFirstErrorBadge from '../../../shared/components/stop-on-first-error-badge'
 
 function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
   const { t } = useTranslation()
@@ -54,7 +53,6 @@ function PreviewLogsPaneMaxEntries({ totalEntries, entriesShown, hasErrors }) {
                     ),
                   }}
                 />{' '}
-                <StopOnFirstErrorBadge placement="bottom" />
               </p>
               <p>{t('log_entry_maximum_entries_see_full_logs')}</p>
             </>

+ 0 - 33
services/web/frontend/js/shared/components/stop-on-first-error-badge.tsx

@@ -1,33 +0,0 @@
-import Tooltip from './tooltip'
-
-type Props = {
-  placement: string
-}
-
-export default function StopOnFirstErrorBadge({ placement }: Props) {
-  const content = (
-    <>
-      We are testing the “Stop on first error” compilation mode.
-      <br />
-      Click to give feedback
-    </>
-  )
-
-  return (
-    <Tooltip
-      id="stop-on-first-error-badge"
-      description={content}
-      overlayProps={{ placement, delayHide: 100 }}
-      tooltipProps={{ className: 'tooltip-wide' }}
-    >
-      <a
-        href="https://forms.gle/7M8821o5RDZrFKoF6"
-        target="_blank"
-        rel="noopener noreferrer"
-        className="badge info-badge"
-      >
-        <span className="sr-only">{content}</span>
-      </a>
-    </Tooltip>
-  )
-}

+ 5 - 6
services/web/frontend/js/shared/context/local-compile-context.js

@@ -27,8 +27,8 @@ import { useIdeContext } from './ide-context'
 import { useProjectContext } from './project-context'
 import { useEditorContext } from './editor-context'
 import { buildFileList } from '../../features/pdf-preview/util/file-list'
-import { useSplitTestContext } from './split-test-context'
 import { useLayoutContext } from './layout-context'
+import { useUserContext } from './user-context'
 
 export const LocalCompileContext = createContext()
 
@@ -86,10 +86,10 @@ export function LocalCompileProvider({ children }) {
 
   const { _id: projectId, rootDocId } = useProjectContext()
 
-  const { splitTestVariants } = useSplitTestContext()
-
   const { pdfPreviewOpen } = useLayoutContext()
 
+  const { features } = useUserContext()
+
   // whether a compile is in progress
   const [compiling, setCompiling] = useState(false)
 
@@ -279,8 +279,7 @@ export function LocalCompileProvider({ children }) {
   }, [compiledOnce, currentDoc, compiler])
 
   useEffect(() => {
-    const compileTimeWarningEnabled =
-      splitTestVariants['compile-time-warning'] === 'show-upgrade-prompt'
+    const compileTimeWarningEnabled = features?.compileTimeout <= 60
 
     if (compileTimeWarningEnabled && compiling && isProjectOwner) {
       const timeout = window.setTimeout(() => {
@@ -291,7 +290,7 @@ export function LocalCompileProvider({ children }) {
         window.clearTimeout(timeout)
       }
     }
-  }, [compiling, isProjectOwner, splitTestVariants])
+  }, [compiling, isProjectOwner, features])
 
   // handle the data returned from a compile request
   // note: this should _only_ run when `data` changes,

+ 1 - 0
services/web/frontend/js/shared/context/user-context.js

@@ -22,6 +22,7 @@ UserContext.Provider.propTypes = {
         mendeley: PropTypes.boolean,
         zotero: PropTypes.boolean,
         references: PropTypes.boolean,
+        compileTimeout: PropTypes.number,
       }),
       refProviders: PropTypes.shape({
         mendeley: PropTypes.any,

+ 9 - 30
services/web/test/acceptance/src/PrimaryEmailCheckTests.js

@@ -1,36 +1,15 @@
 const UserHelper = require('./helpers/UserHelper')
 const Settings = require('@overleaf/settings')
 const { expect } = require('chai')
-const SplitTestManager = require('../../../app/src/Features/SplitTests/SplitTestManager')
 const Features = require('../../../app/src/infrastructure/Features')
 
-// While the split test is in progress this must be appended to URLs during tests
-const SPLIT_TEST_QUERY = '?primary-email-check=active'
-
 describe('PrimaryEmailCheck', function () {
   let userHelper
 
-  // Create the primary-email-check split test because this is now required for the query string override to work. See
-  // https://github.com/overleaf/internal/pull/7545#discussion_r848575736
   before(async function () {
     if (!Features.hasFeature('saas')) {
       this.skip()
     }
-
-    await SplitTestManager.createSplitTest({
-      name: 'primary-email-check',
-      configuration: {
-        active: true,
-        analyticsEnabled: true,
-        phase: 'release',
-        variants: [
-          {
-            name: 'active',
-            rolloutPercent: 0,
-          },
-        ],
-      },
-    })
   })
 
   beforeEach(async function () {
@@ -43,13 +22,13 @@ describe('PrimaryEmailCheck', function () {
   describe('redirections', function () {
     describe('when the user has signed up recently', function () {
       it("shouldn't be redirected from project list to the primary email check page", async function () {
-        const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
+        const response = await userHelper.fetch('/project')
         expect(response.status).to.equal(200)
       })
 
       it('should be redirected from the primary email check page to the project list', async function () {
         const response = await userHelper.fetch(
-          '/user/emails/primary-email-check' + SPLIT_TEST_QUERY
+          '/user/emails/primary-email-check'
         )
         expect(response.status).to.equal(302)
         expect(response.headers.get('location')).to.equal(
@@ -67,13 +46,13 @@ describe('PrimaryEmailCheck', function () {
       })
 
       it("shouldn't be redirected from project list to the primary email check page", async function () {
-        const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
+        const response = await userHelper.fetch('/project')
         expect(response.status).to.equal(200)
       })
 
       it('should be redirected from the primary email check page to the project list', async function () {
         const response = await userHelper.fetch(
-          '/user/emails/primary-email-check' + SPLIT_TEST_QUERY
+          '/user/emails/primary-email-check'
         )
         expect(response.status).to.equal(302)
         expect(response.headers.get('location')).to.equal(
@@ -98,13 +77,13 @@ describe('PrimaryEmailCheck', function () {
       })
 
       it("shouldn't be redirected from project list to the primary email check page", async function () {
-        const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
+        const response = await userHelper.fetch('/project')
         expect(response.status).to.equal(200)
       })
 
       it('should be redirected from the primary email check page to the project list', async function () {
         const response = await userHelper.fetch(
-          '/user/emails/primary-email-check' + SPLIT_TEST_QUERY
+          '/user/emails/primary-email-check'
         )
         expect(response.status).to.equal(302)
         expect(response.headers.get('location')).to.equal(
@@ -122,7 +101,7 @@ describe('PrimaryEmailCheck', function () {
       })
 
       it('should be redirected from project list to the primary email check page', async function () {
-        const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
+        const response = await userHelper.fetch('/project')
         expect(response.status).to.equal(302)
         expect(response.headers.get('location')).to.equal(
           UserHelper.url('/user/emails/primary-email-check').toString()
@@ -149,7 +128,7 @@ describe('PrimaryEmailCheck', function () {
       })
 
       checkResponse = await userHelper.fetch(
-        '/user/emails/primary-email-check' + SPLIT_TEST_QUERY,
+        '/user/emails/primary-email-check',
         { method: 'POST' }
       )
     })
@@ -162,7 +141,7 @@ describe('PrimaryEmailCheck', function () {
     })
 
     it("shouldn't be redirected from project list to the primary email check page any longer", async function () {
-      const response = await userHelper.fetch('/project' + SPLIT_TEST_QUERY)
+      const response = await userHelper.fetch('/project')
       expect(response.status).to.equal(200)
     })
 

+ 5 - 0
services/web/test/acceptance/src/SessionTests.js

@@ -73,6 +73,7 @@ describe('Sessions', function () {
       // set up second session for this user
       this.user2 = new User()
       this.user2.email = this.user1.email
+      this.user2.emails = this.user1.emails
       this.user2.password = this.user1.password
     })
 
@@ -197,9 +198,11 @@ describe('Sessions', function () {
       // set up second session for this user
       this.user2 = new User()
       this.user2.email = this.user1.email
+      this.user2.emails = this.user1.emails
       this.user2.password = this.user1.password
       this.user3 = new User()
       this.user3.email = this.user1.email
+      this.user3.emails = this.user1.emails
       this.user3.password = this.user1.password
     })
 
@@ -321,9 +324,11 @@ describe('Sessions', function () {
       // set up second session for this user
       this.user2 = new User()
       this.user2.email = this.user1.email
+      this.user2.emails = this.user1.emails
       this.user2.password = this.user1.password
       this.user3 = new User()
       this.user3.email = this.user1.email
+      this.user3.emails = this.user1.emails
       this.user3.password = this.user1.password
       async.series([this.user2.login.bind(this.user2)], done)
     })

+ 1 - 0
services/web/test/acceptance/src/helpers/User.js

@@ -19,6 +19,7 @@ class User {
       {
         email: options.email || `acceptance-test-${count}@example.com`,
         createdAt: new Date(),
+        confirmedAt: options.confirmedAt,
       },
     ]
     this.email = this.emails[0].email

+ 3 - 0
services/web/test/frontend/helpers/editor-providers.js

@@ -105,6 +105,9 @@ export function EditorProviders({
     metadataManager,
   }
 
+  // Add details for useUserContext
+  window.metaAttributesCache.set('ol-user', { ...user, features })
+
   return (
     <SplitTestProvider>
       <IdeProvider ide={window._ide}>

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

@@ -71,6 +71,9 @@ describe('ProjectController', function () {
     this.EditorController = { renameProject: sinon.stub() }
     this.InactiveProjectManager = { reactivateProjectIfRequired: sinon.stub() }
     this.ProjectUpdateHandler = { markAsOpened: sinon.stub() }
+    this.UserPrimaryEmailCheckHandler = {
+      requiresPrimaryEmailCheck: sinon.stub().returns(false),
+    }
     this.ProjectGetter = {
       findAllUsersProjects: sinon.stub(),
       getProject: sinon.stub(),
@@ -193,6 +196,8 @@ describe('ProjectController', function () {
         },
         '../Institutions/InstitutionsFeatures': this.InstitutionsFeatures,
         '../Survey/SurveyHandler': this.SurveyHandler,
+        '../User/UserPrimaryEmailCheckHandler':
+          this.UserPrimaryEmailCheckHandler,
         './ProjectAuditLogHandler': this.ProjectAuditLogHandler,
       },
     })

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

@@ -275,7 +275,7 @@ describe('SubscriptionController', function () {
       describe('with a valid plan code', function () {
         it('should render the new subscription page', function (done) {
           this.res.render = (page, opts) => {
-            page.should.equal('subscriptions/new')
+            page.should.equal('subscriptions/new-updated')
             done()
           }
           this.SubscriptionController.paymentPage(this.req, this.res)