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

Split test clean-up

GitOrigin-RevId: 7dd6178487022cbefcbc85797dacc3f3fbfa17e2
This commit is contained in:
Mathias Jakobsen 2022-12-20 13:01:54 +00:00 committed by Copybot
parent f6c1e2738d
commit 38cc3394e3
27 changed files with 53 additions and 622 deletions

View file

@ -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,

View file

@ -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')
}
}

View file

@ -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')
}

View file

@ -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',

View file

@ -7,7 +7,6 @@ const UserDeleter = require('./UserDeleter')
const UserGetter = require('./UserGetter')
const UserUpdater = require('./UserUpdater')
const Analytics = require('../Analytics/AnalyticsManager')
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
const UserOnboardingEmailManager = require('./UserOnboardingEmailManager')
const UserPostRegistrationAnalyticsManager = require('./UserPostRegistrationAnalyticsManager')
const OError = require('@overleaf/o-error')
@ -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

View file

@ -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

View file

@ -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")}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>

View file

@ -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">

View file

@ -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>

View file

@ -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

View file

@ -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>
</>

View file

@ -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>
)
}

View file

@ -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,

View file

@ -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,

View file

@ -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)
})

View file

@ -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)
})

View file

@ -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

View file

@ -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}>

View file

@ -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,
},
})

View file

@ -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)