mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-28 22:23:17 -05:00
Merge pull request #11888 from overleaf/jel-subscription-dash-cancel-plan
[web] Migrate cancel subscription to React GitOrigin-RevId: 8482fd61894c8011b4c980263ae1d41f396233c6
This commit is contained in:
parent
817d1cffaf
commit
ebc04e4a9d
23 changed files with 750 additions and 48 deletions
|
@ -102,7 +102,7 @@ div(ng-controller="RecurlySubscriptionController")
|
||||||
|
|
||||||
div(ng-show="showDowngrade")
|
div(ng-show="showDowngrade")
|
||||||
div(ng-controller="ChangePlanFormController")
|
div(ng-controller="ChangePlanFormController")
|
||||||
p !{translate("interested_in_cheaper_personal_plan",{price:'{{personalDisplayPrice}}'})}
|
p !{translate("interested_in_cheaper_personal_plan", {price:'{{personalDisplayPrice}}'}, ['strong'] )}
|
||||||
p
|
p
|
||||||
button(type="submit", ng-click="downgradeToPaidPersonal()", ng-disabled='inflight').btn.btn-primary #{translate("yes_move_me_to_personal_plan")}
|
button(type="submit", ng-click="downgradeToPaidPersonal()", ng-disabled='inflight').btn.btn-primary #{translate("yes_move_me_to_personal_plan")}
|
||||||
p
|
p
|
||||||
|
|
|
@ -73,6 +73,7 @@
|
||||||
"can_now_relink_dropbox": "",
|
"can_now_relink_dropbox": "",
|
||||||
"cancel": "",
|
"cancel": "",
|
||||||
"cancel_anytime": "",
|
"cancel_anytime": "",
|
||||||
|
"cancel_my_account": "",
|
||||||
"cancel_your_subscription": "",
|
"cancel_your_subscription": "",
|
||||||
"cannot_invite_non_user": "",
|
"cannot_invite_non_user": "",
|
||||||
"cannot_invite_self": "",
|
"cannot_invite_self": "",
|
||||||
|
@ -335,6 +336,7 @@
|
||||||
"group_plan_with_name_tooltip": "",
|
"group_plan_with_name_tooltip": "",
|
||||||
"group_subscription": "",
|
"group_subscription": "",
|
||||||
"have_an_extra_backup": "",
|
"have_an_extra_backup": "",
|
||||||
|
"have_more_days_to_try": "",
|
||||||
"headers": "",
|
"headers": "",
|
||||||
"help": "",
|
"help": "",
|
||||||
"hide_document_preamble": "",
|
"hide_document_preamble": "",
|
||||||
|
@ -363,8 +365,10 @@
|
||||||
"hotkey_toggle_track_changes": "",
|
"hotkey_toggle_track_changes": "",
|
||||||
"hotkey_undo": "",
|
"hotkey_undo": "",
|
||||||
"hotkeys": "",
|
"hotkeys": "",
|
||||||
|
"i_want_to_stay": "",
|
||||||
"if_error_persists_try_relinking_provider": "",
|
"if_error_persists_try_relinking_provider": "",
|
||||||
"ignore_validation_errors": "",
|
"ignore_validation_errors": "",
|
||||||
|
"ill_take_it": "",
|
||||||
"import_from_github": "",
|
"import_from_github": "",
|
||||||
"import_to_sharelatex": "",
|
"import_to_sharelatex": "",
|
||||||
"imported_from_another_project_at_date": "",
|
"imported_from_another_project_at_date": "",
|
||||||
|
@ -512,6 +516,7 @@
|
||||||
"no_projects": "",
|
"no_projects": "",
|
||||||
"no_search_results": "",
|
"no_search_results": "",
|
||||||
"no_symbols_found": "",
|
"no_symbols_found": "",
|
||||||
|
"no_thanks_cancel_now": "",
|
||||||
"normal": "",
|
"normal": "",
|
||||||
"normally_x_price_per_month": "",
|
"normally_x_price_per_month": "",
|
||||||
"normally_x_price_per_year": "",
|
"normally_x_price_per_year": "",
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export default function ActionButtonText({
|
||||||
|
inflight,
|
||||||
|
buttonText,
|
||||||
|
}: {
|
||||||
|
inflight: boolean
|
||||||
|
buttonText: string
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return <>{!inflight ? buttonText : t('processing_uppercase') + '…'}</>
|
||||||
|
}
|
|
@ -4,12 +4,13 @@ import { PriceExceptions } from '../../../shared/price-exceptions'
|
||||||
import { useSubscriptionDashboardContext } from '../../../../context/subscription-dashboard-context'
|
import { useSubscriptionDashboardContext } from '../../../../context/subscription-dashboard-context'
|
||||||
import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
|
import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
|
||||||
import { CancelSubscriptionButton } from './cancel-subscription-button'
|
import { CancelSubscriptionButton } from './cancel-subscription-button'
|
||||||
import { CancelSubscription } from './cancel-subscription'
|
import { CancelSubscription } from './cancel-plan/cancel-subscription'
|
||||||
import { PendingPlanChange } from './pending-plan-change'
|
import { PendingPlanChange } from './pending-plan-change'
|
||||||
import { TrialEnding } from './trial-ending'
|
import { TrialEnding } from './trial-ending'
|
||||||
import { ChangePlan } from './change-plan/change-plan'
|
import { ChangePlan } from './change-plan/change-plan'
|
||||||
import { PendingAdditionalLicenses } from './pending-additional-licenses'
|
import { PendingAdditionalLicenses } from './pending-additional-licenses'
|
||||||
import { ContactSupportToChangeGroupPlan } from './contact-support-to-change-group-plan'
|
import { ContactSupportToChangeGroupPlan } from './contact-support-to-change-group-plan'
|
||||||
|
import isInFreeTrial from '../../../../util/is-in-free-trial'
|
||||||
|
|
||||||
export function ActiveSubscription({
|
export function ActiveSubscription({
|
||||||
subscription,
|
subscription,
|
||||||
|
@ -72,10 +73,9 @@ export function ActiveSubscription({
|
||||||
{(!subscription.pendingPlan ||
|
{(!subscription.pendingPlan ||
|
||||||
subscription.pendingPlan.name === subscription.plan.name) &&
|
subscription.pendingPlan.name === subscription.plan.name) &&
|
||||||
subscription.plan.groupPlan && <ContactSupportToChangeGroupPlan />}
|
subscription.plan.groupPlan && <ContactSupportToChangeGroupPlan />}
|
||||||
{subscription.recurly.trial_ends_at &&
|
{isInFreeTrial(subscription.recurly.trial_ends_at) &&
|
||||||
subscription.recurly.trialEndsAtFormatted && (
|
subscription.recurly.trialEndsAtFormatted && (
|
||||||
<TrialEnding
|
<TrialEnding
|
||||||
trialEndsAt={subscription.recurly.trial_ends_at}
|
|
||||||
trialEndsAtFormatted={subscription.recurly.trialEndsAtFormatted}
|
trialEndsAtFormatted={subscription.recurly.trialEndsAtFormatted}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -0,0 +1,183 @@
|
||||||
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
import { postJSON } from '../../../../../../../infrastructure/fetch-json'
|
||||||
|
import useAsync from '../../../../../../../shared/hooks/use-async'
|
||||||
|
import { useSubscriptionDashboardContext } from '../../../../../context/subscription-dashboard-context'
|
||||||
|
import {
|
||||||
|
cancelSubscriptionUrl,
|
||||||
|
redirectAfterCancelSubscriptionUrl,
|
||||||
|
} from '../../../../../data/subscription-url'
|
||||||
|
import canExtendTrial from '../../../../../util/can-extend-trial'
|
||||||
|
import showDowngradeOption from '../../../../../util/show-downgrade-option'
|
||||||
|
import ActionButtonText from '../../../action-button-text'
|
||||||
|
import GenericErrorAlert from '../../../generic-error-alert'
|
||||||
|
import ExtendTrialButton from './extend-trial-button'
|
||||||
|
|
||||||
|
function ConfirmCancelSubscriptionButton({
|
||||||
|
buttonClass,
|
||||||
|
buttonText,
|
||||||
|
handleCancelSubscription,
|
||||||
|
isLoadingCancel,
|
||||||
|
isSuccessCancel,
|
||||||
|
isButtonDisabled,
|
||||||
|
}: {
|
||||||
|
buttonClass: string
|
||||||
|
buttonText: string
|
||||||
|
handleCancelSubscription: () => void
|
||||||
|
isLoadingCancel: boolean
|
||||||
|
isSuccessCancel: boolean
|
||||||
|
isButtonDisabled: boolean
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className={`btn ${buttonClass}`}
|
||||||
|
onClick={handleCancelSubscription}
|
||||||
|
disabled={isButtonDisabled}
|
||||||
|
>
|
||||||
|
<ActionButtonText
|
||||||
|
inflight={isSuccessCancel || isLoadingCancel}
|
||||||
|
buttonText={buttonText}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function NotCancelOption({
|
||||||
|
isButtonDisabled,
|
||||||
|
isLoadingSecondaryAction,
|
||||||
|
isSuccessSecondaryAction,
|
||||||
|
showExtendFreeTrial,
|
||||||
|
runAsyncSecondaryAction,
|
||||||
|
}: {
|
||||||
|
isButtonDisabled: boolean
|
||||||
|
isLoadingSecondaryAction: boolean
|
||||||
|
isSuccessSecondaryAction: boolean
|
||||||
|
showExtendFreeTrial: boolean
|
||||||
|
runAsyncSecondaryAction: (promise: Promise<unknown>) => Promise<unknown>
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
const { setShowCancellation } = useSubscriptionDashboardContext()
|
||||||
|
|
||||||
|
if (showExtendFreeTrial) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
<Trans
|
||||||
|
i18nKey="have_more_days_to_try"
|
||||||
|
values={{
|
||||||
|
days: 14,
|
||||||
|
}}
|
||||||
|
components={[
|
||||||
|
// eslint-disable-next-line react/jsx-key
|
||||||
|
<strong />,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<ExtendTrialButton
|
||||||
|
isButtonDisabled={isButtonDisabled}
|
||||||
|
isLoadingSecondaryAction={isLoadingSecondaryAction}
|
||||||
|
isSuccessSecondaryAction={isSuccessSecondaryAction}
|
||||||
|
runAsyncSecondaryAction={runAsyncSecondaryAction}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeepPlan() {
|
||||||
|
setShowCancellation(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
<button
|
||||||
|
className="btn btn-secondary-info btn-secondary"
|
||||||
|
onClick={handleKeepPlan}
|
||||||
|
>
|
||||||
|
{t('i_want_to_stay')}
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CancelSubscription() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { personalSubscription } = useSubscriptionDashboardContext()
|
||||||
|
|
||||||
|
const {
|
||||||
|
isLoading: isLoadingCancel,
|
||||||
|
isError: isErrorCancel,
|
||||||
|
isSuccess: isSuccessCancel,
|
||||||
|
runAsync: runAsyncCancel,
|
||||||
|
} = useAsync()
|
||||||
|
const {
|
||||||
|
isLoading: isLoadingSecondaryAction,
|
||||||
|
isError: isErrorSecondaryAction,
|
||||||
|
isSuccess: isSuccessSecondaryAction,
|
||||||
|
runAsync: runAsyncSecondaryAction,
|
||||||
|
} = useAsync()
|
||||||
|
|
||||||
|
const isButtonDisabled =
|
||||||
|
isLoadingCancel ||
|
||||||
|
isLoadingSecondaryAction ||
|
||||||
|
isSuccessSecondaryAction ||
|
||||||
|
isSuccessCancel
|
||||||
|
|
||||||
|
if (!personalSubscription || !('recurly' in personalSubscription)) return null
|
||||||
|
|
||||||
|
async function handleCancelSubscription() {
|
||||||
|
try {
|
||||||
|
await runAsyncCancel(postJSON(cancelSubscriptionUrl))
|
||||||
|
window.location.assign(redirectAfterCancelSubscriptionUrl)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const showExtendFreeTrial = canExtendTrial(
|
||||||
|
personalSubscription.plan.planCode,
|
||||||
|
personalSubscription.plan.groupPlan,
|
||||||
|
personalSubscription.recurly.trial_ends_at
|
||||||
|
)
|
||||||
|
|
||||||
|
const showDowngrade = showDowngradeOption(
|
||||||
|
personalSubscription.plan.planCode,
|
||||||
|
personalSubscription.plan.groupPlan,
|
||||||
|
personalSubscription.recurly.trial_ends_at
|
||||||
|
)
|
||||||
|
|
||||||
|
let confirmCancelButtonText = t('cancel_my_account')
|
||||||
|
let confirmCancelButtonClass = 'btn-primary'
|
||||||
|
if (showExtendFreeTrial || showDowngrade) {
|
||||||
|
confirmCancelButtonText = t('no_thanks_cancel_now')
|
||||||
|
confirmCancelButtonClass = 'btn-inline-link'
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="text-center">
|
||||||
|
<p>
|
||||||
|
<strong>{t('wed_love_you_to_stay')}</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{(isErrorCancel || isErrorSecondaryAction) && <GenericErrorAlert />}
|
||||||
|
|
||||||
|
<NotCancelOption
|
||||||
|
showExtendFreeTrial={showExtendFreeTrial}
|
||||||
|
isButtonDisabled={isButtonDisabled}
|
||||||
|
isLoadingSecondaryAction={isLoadingSecondaryAction}
|
||||||
|
isSuccessSecondaryAction={isSuccessSecondaryAction}
|
||||||
|
runAsyncSecondaryAction={runAsyncSecondaryAction}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmCancelSubscriptionButton
|
||||||
|
buttonClass={confirmCancelButtonClass}
|
||||||
|
buttonText={confirmCancelButtonText}
|
||||||
|
isButtonDisabled={isButtonDisabled}
|
||||||
|
handleCancelSubscription={handleCancelSubscription}
|
||||||
|
isSuccessCancel={isSuccessCancel}
|
||||||
|
isLoadingCancel={isLoadingCancel}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { putJSON } from '../../../../../../../infrastructure/fetch-json'
|
||||||
|
import { extendTrialUrl } from '../../../../../data/subscription-url'
|
||||||
|
import ActionButtonText from '../../../action-button-text'
|
||||||
|
|
||||||
|
export default function ExtendTrialButton({
|
||||||
|
isButtonDisabled,
|
||||||
|
isLoadingSecondaryAction,
|
||||||
|
isSuccessSecondaryAction,
|
||||||
|
runAsyncSecondaryAction,
|
||||||
|
}: {
|
||||||
|
isButtonDisabled: boolean
|
||||||
|
isLoadingSecondaryAction: boolean
|
||||||
|
isSuccessSecondaryAction: boolean
|
||||||
|
runAsyncSecondaryAction: (promise: Promise<unknown>) => Promise<unknown>
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const buttonText = t('ill_take_it')
|
||||||
|
|
||||||
|
async function handleExtendTrial() {
|
||||||
|
try {
|
||||||
|
await runAsyncSecondaryAction(putJSON(extendTrialUrl))
|
||||||
|
window.location.reload()
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
onClick={handleExtendTrial}
|
||||||
|
disabled={isButtonDisabled}
|
||||||
|
>
|
||||||
|
<ActionButtonText
|
||||||
|
inflight={isLoadingSecondaryAction || isSuccessSecondaryAction}
|
||||||
|
buttonText={buttonText}
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,15 +0,0 @@
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
export function CancelSubscription() {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
return (
|
|
||||||
<div className="text-center">
|
|
||||||
<p>
|
|
||||||
<strong>{t('wed_love_you_to_stay')}</strong>
|
|
||||||
</p>
|
|
||||||
{/* todo: showExtendFreeTrial */}
|
|
||||||
{/* todo: showDowngrade */}
|
|
||||||
{/* todo: showBasicCancel */}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,15 +1,10 @@
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
|
|
||||||
export function TrialEnding({
|
export function TrialEnding({
|
||||||
trialEndsAt,
|
|
||||||
trialEndsAtFormatted,
|
trialEndsAtFormatted,
|
||||||
}: {
|
}: {
|
||||||
trialEndsAt: string
|
|
||||||
trialEndsAtFormatted: string
|
trialEndsAtFormatted: string
|
||||||
}) {
|
}) {
|
||||||
const endDate = new Date(trialEndsAt)
|
|
||||||
if (endDate.getTime() < Date.now()) return null
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<p>
|
<p>
|
||||||
<Trans
|
<Trans
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
export const subscriptionUpdateUrl = '/user/subscription/update'
|
export const subscriptionUpdateUrl = '/user/subscription/update'
|
||||||
export const cancelPendingSubscriptionChangeUrl =
|
export const cancelPendingSubscriptionChangeUrl =
|
||||||
'/user/subscription/cancel-pending'
|
'/user/subscription/cancel-pending'
|
||||||
|
export const cancelSubscriptionUrl = '/user/subscription/cancel'
|
||||||
|
export const redirectAfterCancelSubscriptionUrl = '/user/subscription/canceled'
|
||||||
|
export const extendTrialUrl = '/user/subscription/extend'
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import freeTrialExpiresUnderSevenDays from './free-trial-expires-under-seven-days'
|
||||||
|
import isMonthlyCollaboratorPlan from './is-monthly-collaborator-plan'
|
||||||
|
|
||||||
|
export default function canExtendTrial(
|
||||||
|
planCode: string,
|
||||||
|
isGroupPlan?: boolean,
|
||||||
|
trialEndsAt?: string | null
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
isMonthlyCollaboratorPlan(planCode, isGroupPlan) &&
|
||||||
|
freeTrialExpiresUnderSevenDays(trialEndsAt)
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
export default function freeTrialExpiresUnderSevenDays(
|
||||||
|
trialEndsAt?: string | null
|
||||||
|
) {
|
||||||
|
if (!trialEndsAt) return false
|
||||||
|
|
||||||
|
const sevenDaysTime = new Date()
|
||||||
|
sevenDaysTime.setDate(sevenDaysTime.getDate() + 7)
|
||||||
|
const freeTrialEndDate = new Date(trialEndsAt)
|
||||||
|
|
||||||
|
return new Date() < freeTrialEndDate && freeTrialEndDate < sevenDaysTime
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
export default function isInFreeTrial(trialEndsAt?: string | null) {
|
||||||
|
if (!trialEndsAt) return false
|
||||||
|
|
||||||
|
const endDate = new Date(trialEndsAt)
|
||||||
|
|
||||||
|
if (endDate.getTime() < Date.now()) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
export default function isMonthlyCollaboratorPlan(
|
||||||
|
planCode: string,
|
||||||
|
isGroupPlan?: boolean
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
planCode.indexOf('collaborator') !== -1 &&
|
||||||
|
planCode.indexOf('ann') === -1 &&
|
||||||
|
!isGroupPlan
|
||||||
|
)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import isInFreeTrial from './is-in-free-trial'
|
||||||
|
import isMonthlyCollaboratorPlan from './is-monthly-collaborator-plan'
|
||||||
|
|
||||||
|
export default function showDowngradeOption(
|
||||||
|
planCode: string,
|
||||||
|
isGroupPlan?: boolean,
|
||||||
|
trialEndsAt?: string | null
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
isMonthlyCollaboratorPlan(planCode, isGroupPlan) &&
|
||||||
|
!isInFreeTrial(trialEndsAt)
|
||||||
|
)
|
||||||
|
}
|
|
@ -737,8 +737,7 @@
|
||||||
"institutional_login_unknown": "Sorry, we don’t know which institution issued that email address. You can browse our <a href=\"__link__\">list of institutions</a> to find yours, or you can use one of the other options below.",
|
"institutional_login_unknown": "Sorry, we don’t know which institution issued that email address. You can browse our <a href=\"__link__\">list of institutions</a> to find yours, or you can use one of the other options below.",
|
||||||
"integrations": "Integrations",
|
"integrations": "Integrations",
|
||||||
"interested_in": "Interested in",
|
"interested_in": "Interested in",
|
||||||
"interested_in_cheaper_personal_plan": "Would you be interested in the cheaper <strong>__price__</strong> Personal plan?",
|
"interested_in_cheaper_personal_plan": "Would you be interested in the cheaper <0>__price__</0> Personal plan?",
|
||||||
"invalid": "Invalid",
|
|
||||||
"invalid_element_name": "Could not copy your project because of filenames containing invalid characters\r\n(such as asterisks, slashes or control characters). Please rename the files and\r\ntry again.",
|
"invalid_element_name": "Could not copy your project because of filenames containing invalid characters\r\n(such as asterisks, slashes or control characters). Please rename the files and\r\ntry again.",
|
||||||
"invalid_email": "An email address is invalid",
|
"invalid_email": "An email address is invalid",
|
||||||
"invalid_file_name": "Invalid File Name",
|
"invalid_file_name": "Invalid File Name",
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
import { expect } from 'chai'
|
import { expect } from 'chai'
|
||||||
import { fireEvent, screen } from '@testing-library/react'
|
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||||
import * as eventTracking from '../../../../../../../../frontend/js/infrastructure/event-tracking'
|
import * as eventTracking from '../../../../../../../../frontend/js/infrastructure/event-tracking'
|
||||||
import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
|
import { RecurlySubscription } from '../../../../../../../../types/subscription/dashboard/subscription'
|
||||||
import {
|
import {
|
||||||
annualActiveSubscription,
|
annualActiveSubscription,
|
||||||
groupActiveSubscription,
|
groupActiveSubscription,
|
||||||
groupActiveSubscriptionWithPendingLicenseChange,
|
groupActiveSubscriptionWithPendingLicenseChange,
|
||||||
|
monthlyActiveCollaborator,
|
||||||
pendingSubscriptionChange,
|
pendingSubscriptionChange,
|
||||||
|
trialCollaboratorSubscription,
|
||||||
trialSubscription,
|
trialSubscription,
|
||||||
} from '../../../../fixtures/subscriptions'
|
} from '../../../../fixtures/subscriptions'
|
||||||
import sinon from 'sinon'
|
import sinon from 'sinon'
|
||||||
import { cleanUpContext } from '../../../../helpers/render-with-subscription-dash-context'
|
import { cleanUpContext } from '../../../../helpers/render-with-subscription-dash-context'
|
||||||
import { renderActiveSubscription } from '../../../../helpers/render-active-subscription'
|
import { renderActiveSubscription } from '../../../../helpers/render-active-subscription'
|
||||||
import { cloneDeep } from 'lodash'
|
import { cloneDeep } from 'lodash'
|
||||||
|
import fetchMock from 'fetch-mock'
|
||||||
|
import {
|
||||||
|
cancelSubscriptionUrl,
|
||||||
|
extendTrialUrl,
|
||||||
|
} from '../../../../../../../../frontend/js/features/subscription/data/subscription-url'
|
||||||
|
|
||||||
describe('<ActiveSubscription />', function () {
|
describe('<ActiveSubscription />', function () {
|
||||||
let sendMBSpy: sinon.SinonSpy
|
let sendMBSpy: sinon.SinonSpy
|
||||||
|
@ -186,30 +193,220 @@ describe('<ActiveSubscription />', function () {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('shows cancel UI and sends event', function () {
|
describe('cancel plan', function () {
|
||||||
renderActiveSubscription(annualActiveSubscription)
|
const locationStub = sinon.stub()
|
||||||
// before button clicked
|
const reloadStub = sinon.stub()
|
||||||
screen.getByText(
|
const originalLocation = window.location
|
||||||
'Your subscription will remain active until the end of your billing period',
|
|
||||||
{ exact: false }
|
|
||||||
)
|
|
||||||
const dates = screen.getAllByText(
|
|
||||||
annualActiveSubscription.recurly.nextPaymentDueAt,
|
|
||||||
{
|
|
||||||
exact: false,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
expect(dates.length).to.equal(2)
|
|
||||||
|
|
||||||
const button = screen.getByRole('button', {
|
beforeEach(function () {
|
||||||
name: 'Cancel Your Subscription',
|
Object.defineProperty(window, 'location', {
|
||||||
|
value: { assign: locationStub, reload: reloadStub },
|
||||||
|
})
|
||||||
})
|
})
|
||||||
fireEvent.click(button)
|
|
||||||
expect(sendMBSpy).to.be.calledOnceWith(
|
|
||||||
'subscription-page-cancel-button-click'
|
|
||||||
)
|
|
||||||
|
|
||||||
screen.getByText('We’d love you to stay')
|
afterEach(function () {
|
||||||
|
Object.defineProperty(window, 'location', {
|
||||||
|
value: originalLocation,
|
||||||
|
})
|
||||||
|
fetchMock.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
function showConfirmCancelUI() {
|
||||||
|
const button = screen.getByRole('button', {
|
||||||
|
name: 'Cancel Your Subscription',
|
||||||
|
})
|
||||||
|
fireEvent.click(button)
|
||||||
|
}
|
||||||
|
|
||||||
|
it('shows cancel UI and sends event', function () {
|
||||||
|
renderActiveSubscription(annualActiveSubscription)
|
||||||
|
// before button clicked
|
||||||
|
screen.getByText(
|
||||||
|
'Your subscription will remain active until the end of your billing period',
|
||||||
|
{ exact: false }
|
||||||
|
)
|
||||||
|
const dates = screen.getAllByText(
|
||||||
|
annualActiveSubscription.recurly.nextPaymentDueAt,
|
||||||
|
{
|
||||||
|
exact: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(dates.length).to.equal(2)
|
||||||
|
|
||||||
|
showConfirmCancelUI()
|
||||||
|
|
||||||
|
expect(sendMBSpy).to.be.calledOnceWith(
|
||||||
|
'subscription-page-cancel-button-click'
|
||||||
|
)
|
||||||
|
|
||||||
|
screen.getByText('We’d love you to stay')
|
||||||
|
screen.getByRole('button', { name: 'Cancel my subscription' })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('cancels subscription and redirects page', async function () {
|
||||||
|
const endPointResponse = {
|
||||||
|
status: 200,
|
||||||
|
}
|
||||||
|
fetchMock.post(cancelSubscriptionUrl, endPointResponse)
|
||||||
|
renderActiveSubscription(annualActiveSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
const button = screen.getByRole('button', {
|
||||||
|
name: 'Cancel my subscription',
|
||||||
|
})
|
||||||
|
fireEvent.click(button)
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(locationStub).to.have.been.called
|
||||||
|
})
|
||||||
|
sinon.assert.calledWithMatch(locationStub, '/user/subscription/canceled')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('shows an error message if canceling subscription failed', async function () {
|
||||||
|
const endPointResponse = {
|
||||||
|
status: 500,
|
||||||
|
}
|
||||||
|
fetchMock.post(cancelSubscriptionUrl, endPointResponse)
|
||||||
|
renderActiveSubscription(annualActiveSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
const button = screen.getByRole('button', {
|
||||||
|
name: 'Cancel my subscription',
|
||||||
|
})
|
||||||
|
fireEvent.click(button)
|
||||||
|
await screen.findByText('Sorry, something went wrong. ', {
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
screen.getByText('Please try again. ', { exact: false })
|
||||||
|
screen.getByText('If the problem continues please contact us.', {
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('disables cancels subscription button after clicking and updates text', async function () {
|
||||||
|
renderActiveSubscription(annualActiveSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: 'I want to stay',
|
||||||
|
})
|
||||||
|
const button = screen.getByRole('button', {
|
||||||
|
name: 'Cancel my subscription',
|
||||||
|
})
|
||||||
|
fireEvent.click(button)
|
||||||
|
|
||||||
|
const cancelButtton = screen.getByRole('button', {
|
||||||
|
name: 'Processing…',
|
||||||
|
}) as HTMLButtonElement
|
||||||
|
expect(cancelButtton.disabled).to.be.true
|
||||||
|
|
||||||
|
expect(screen.queryByText('Cancel my subscription')).to.be.null
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('extend trial', function () {
|
||||||
|
const cancelButtonText = 'No thanks, I still want to cancel'
|
||||||
|
const extendTrialButtonText = 'I’ll take it!'
|
||||||
|
it('shows alternate cancel subscription button text for cancel button and option to extend trial', function () {
|
||||||
|
renderActiveSubscription(trialCollaboratorSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
screen.getByText('Have another', { exact: false })
|
||||||
|
screen.getByText('14 days', { exact: false })
|
||||||
|
screen.getByText('on your Trial!', { exact: false })
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: cancelButtonText,
|
||||||
|
})
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: extendTrialButtonText,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('disables both buttons and updates text for when trial button clicked', function () {
|
||||||
|
renderActiveSubscription(trialCollaboratorSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
const extendTrialButton = screen.getByRole('button', {
|
||||||
|
name: extendTrialButtonText,
|
||||||
|
})
|
||||||
|
fireEvent.click(extendTrialButton)
|
||||||
|
|
||||||
|
const buttons = screen.getAllByRole('button')
|
||||||
|
expect(buttons.length).to.equal(2)
|
||||||
|
expect(buttons[0].getAttribute('disabled')).to.equal('')
|
||||||
|
expect(buttons[1].getAttribute('disabled')).to.equal('')
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: cancelButtonText,
|
||||||
|
})
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: 'Processing…',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('disables both buttons and updates text for when cancel button clicked', function () {
|
||||||
|
renderActiveSubscription(trialCollaboratorSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
const cancelButtton = screen.getByRole('button', {
|
||||||
|
name: cancelButtonText,
|
||||||
|
})
|
||||||
|
fireEvent.click(cancelButtton)
|
||||||
|
|
||||||
|
const buttons = screen.getAllByRole('button')
|
||||||
|
expect(buttons.length).to.equal(2)
|
||||||
|
expect(buttons[0].getAttribute('disabled')).to.equal('')
|
||||||
|
expect(buttons[1].getAttribute('disabled')).to.equal('')
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: 'Processing…',
|
||||||
|
})
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: extendTrialButtonText,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not show option to extend trial when not a collaborator trial', function () {
|
||||||
|
const trialPlan = cloneDeep(trialCollaboratorSubscription)
|
||||||
|
trialPlan.plan.planCode = 'anotherplan'
|
||||||
|
renderActiveSubscription(trialPlan)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('button', {
|
||||||
|
name: extendTrialButtonText,
|
||||||
|
})
|
||||||
|
).to.be.null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not show option to extend trial when a collaborator trial but does not expire in 7 days', function () {
|
||||||
|
const trialPlan = cloneDeep(trialCollaboratorSubscription)
|
||||||
|
trialPlan.recurly.trial_ends_at = null
|
||||||
|
renderActiveSubscription(trialPlan)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
expect(
|
||||||
|
screen.queryByRole('button', {
|
||||||
|
name: extendTrialButtonText,
|
||||||
|
})
|
||||||
|
).to.be.null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('reloads page after the succesful request to extend trial', async function () {
|
||||||
|
const endPointResponse = {
|
||||||
|
status: 200,
|
||||||
|
}
|
||||||
|
fetchMock.put(extendTrialUrl, endPointResponse)
|
||||||
|
renderActiveSubscription(trialCollaboratorSubscription)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
const extendTrialButton = screen.getByRole('button', {
|
||||||
|
name: extendTrialButtonText,
|
||||||
|
})
|
||||||
|
fireEvent.click(extendTrialButton)
|
||||||
|
// page is reloaded on success
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(reloadStub).to.have.been.called
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('downgrade plan', function () {
|
||||||
|
it('shows alternate cancel subscription button text', function () {
|
||||||
|
renderActiveSubscription(monthlyActiveCollaborator)
|
||||||
|
showConfirmCancelUI()
|
||||||
|
screen.getByRole('button', {
|
||||||
|
name: 'No thanks, I still want to cancel',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('group plans', function () {
|
describe('group plans', function () {
|
||||||
|
|
|
@ -432,3 +432,90 @@ export const customSubscription: CustomSubscription = {
|
||||||
},
|
},
|
||||||
customAccount: true,
|
customAccount: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const trialCollaboratorSubscription: RecurlySubscription = {
|
||||||
|
manager_ids: ['abc123'],
|
||||||
|
member_ids: [],
|
||||||
|
invited_emails: [],
|
||||||
|
groupPlan: false,
|
||||||
|
membersLimit: 0,
|
||||||
|
_id: 'def456',
|
||||||
|
admin_id: 'abc123',
|
||||||
|
teamInvites: [],
|
||||||
|
planCode: 'collaborator_free_trial_7_days',
|
||||||
|
recurlySubscription_id: 'ghi789',
|
||||||
|
plan: {
|
||||||
|
planCode: 'collaborator_free_trial_7_days',
|
||||||
|
name: 'Standard (Collaborator)',
|
||||||
|
price_in_cents: 2300,
|
||||||
|
featureDescription: [],
|
||||||
|
hideFromUsers: true,
|
||||||
|
},
|
||||||
|
recurly: {
|
||||||
|
tax: 0,
|
||||||
|
taxRate: 0,
|
||||||
|
billingDetailsLink: '/user/subscription/recurly/billing-details',
|
||||||
|
accountManagementLink: '/user/subscription/recurly/account-management',
|
||||||
|
additionalLicenses: 0,
|
||||||
|
totalLicenses: 0,
|
||||||
|
nextPaymentDueAt: sevenDaysFromTodayFormatted,
|
||||||
|
currency: 'USD',
|
||||||
|
state: 'active',
|
||||||
|
trialEndsAtFormatted: sevenDaysFromTodayFormatted,
|
||||||
|
trial_ends_at: new Date(sevenDaysFromToday).toString(),
|
||||||
|
activeCoupons: [],
|
||||||
|
account: {
|
||||||
|
has_canceled_subscription: {
|
||||||
|
_: 'false',
|
||||||
|
$: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
has_past_due_invoice: {
|
||||||
|
_: 'false',
|
||||||
|
$: {
|
||||||
|
type: 'boolean',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
displayPrice: '$21.00',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const monthlyActiveCollaborator: RecurlySubscription = {
|
||||||
|
manager_ids: ['abc123'],
|
||||||
|
member_ids: [],
|
||||||
|
invited_emails: [],
|
||||||
|
groupPlan: false,
|
||||||
|
membersLimit: 0,
|
||||||
|
_id: 'def456',
|
||||||
|
admin_id: 'abc123',
|
||||||
|
teamInvites: [],
|
||||||
|
planCode: 'collaborator',
|
||||||
|
recurlySubscription_id: 'ghi789',
|
||||||
|
plan: {
|
||||||
|
planCode: 'collaborator',
|
||||||
|
name: 'Standard (Collaborator)',
|
||||||
|
price_in_cents: 212300900,
|
||||||
|
featureDescription: [],
|
||||||
|
},
|
||||||
|
recurly: {
|
||||||
|
tax: 0,
|
||||||
|
taxRate: 0,
|
||||||
|
billingDetailsLink: '/user/subscription/recurly/billing-details',
|
||||||
|
accountManagementLink: '/user/subscription/recurly/account-management',
|
||||||
|
additionalLicenses: 0,
|
||||||
|
totalLicenses: 0,
|
||||||
|
nextPaymentDueAt,
|
||||||
|
currency: 'USD',
|
||||||
|
state: 'active',
|
||||||
|
trialEndsAtFormatted: null,
|
||||||
|
trial_ends_at: null,
|
||||||
|
activeCoupons: [],
|
||||||
|
account: {
|
||||||
|
has_canceled_subscription: { _: 'false', $: { type: 'boolean' } },
|
||||||
|
has_past_due_invoice: { _: 'false', $: { type: 'boolean' } },
|
||||||
|
},
|
||||||
|
displayPrice: '$21.00',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import canExtendTrial from '../../../../../frontend/js/features/subscription/util/can-extend-trial'
|
||||||
|
|
||||||
|
describe('canExtendTrial', function () {
|
||||||
|
const d = new Date()
|
||||||
|
d.setDate(d.getDate() + 6)
|
||||||
|
|
||||||
|
it('returns false when no trial end date', function () {
|
||||||
|
expect(canExtendTrial('collab')).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when a plan code without "collaborator" ', function () {
|
||||||
|
expect(canExtendTrial('test', false, d.toString())).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with trial date in future but has "collaborator" and "ann" in plan code', function () {
|
||||||
|
expect(canExtendTrial('collaborator-annual', false, d.toString())).to.be
|
||||||
|
.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with trial date in future and plan code has "collaborator" and no "ann" but is a group plan', function () {
|
||||||
|
expect(canExtendTrial('collaborator', true, d.toString())).to.be.false
|
||||||
|
})
|
||||||
|
it('returns true when on a plan with "collaborator" and without "ann" and trial date in future', function () {
|
||||||
|
expect(canExtendTrial('collaborator', false, d.toString())).to.be.true
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import freeTrialExpiresUnderSevenDays from '../../../../../frontend/js/features/subscription/util/free-trial-expires-under-seven-days'
|
||||||
|
|
||||||
|
describe('freeTrialExpiresUnderSevenDays', function () {
|
||||||
|
it('returns false when no date sent', function () {
|
||||||
|
expect(freeTrialExpiresUnderSevenDays()).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when date is null', function () {
|
||||||
|
expect(freeTrialExpiresUnderSevenDays(null)).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when date is in the past', function () {
|
||||||
|
expect(freeTrialExpiresUnderSevenDays('2000-02-16T17:59:07.000Z')).to.be
|
||||||
|
.false
|
||||||
|
})
|
||||||
|
it('returns true when date is in 6 days', function () {
|
||||||
|
const d = new Date()
|
||||||
|
d.setDate(d.getDate() + 6)
|
||||||
|
expect(freeTrialExpiresUnderSevenDays(d.toString())).to.be.true
|
||||||
|
})
|
||||||
|
it('returns false when date is in 8 days', function () {
|
||||||
|
const d = new Date()
|
||||||
|
d.setDate(d.getDate() + 8)
|
||||||
|
expect(freeTrialExpiresUnderSevenDays(d.toString())).to.be.false
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import isInFreeTrial from '../../../../../frontend/js/features/subscription/util/is-in-free-trial'
|
||||||
|
const dateformat = require('dateformat')
|
||||||
|
|
||||||
|
describe('isInFreeTrial', function () {
|
||||||
|
it('returns false when no date sent', function () {
|
||||||
|
expect(isInFreeTrial()).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when date is null', function () {
|
||||||
|
expect(isInFreeTrial(null)).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when date is in the past', function () {
|
||||||
|
expect(isInFreeTrial('2000-02-16T17:59:07.000Z')).to.be.false
|
||||||
|
})
|
||||||
|
it('returns true when date is in the future', function () {
|
||||||
|
const today = new Date()
|
||||||
|
const sevenDaysFromToday = new Date().setDate(today.getDate() + 7)
|
||||||
|
const sevenDaysFromTodayFormatted = dateformat(
|
||||||
|
sevenDaysFromToday,
|
||||||
|
'dS mmmm yyyy'
|
||||||
|
)
|
||||||
|
expect(isInFreeTrial(sevenDaysFromTodayFormatted)).to.be.true
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import isMonthlyCollaboratorPlan from '../../../../../frontend/js/features/subscription/util/is-monthly-collaborator-plan'
|
||||||
|
|
||||||
|
describe('isMonthlyCollaboratorPlan', function () {
|
||||||
|
it('returns false when a plan code without "collaborator" ', function () {
|
||||||
|
expect(isMonthlyCollaboratorPlan('test', false)).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with "collaborator" and "ann"', function () {
|
||||||
|
expect(isMonthlyCollaboratorPlan('collaborator-annual', false)).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with "collaborator" and without "ann" but is a group plan', function () {
|
||||||
|
expect(isMonthlyCollaboratorPlan('collaborator', true)).to.be.false
|
||||||
|
})
|
||||||
|
it('returns true when on a plan with non-group "collaborator" monthly plan', function () {
|
||||||
|
expect(isMonthlyCollaboratorPlan('collaborator', false)).to.be.true
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { expect } from 'chai'
|
||||||
|
import showDowngradeOption from '../../../../../frontend/js/features/subscription/util/show-downgrade-option'
|
||||||
|
const dateformat = require('dateformat')
|
||||||
|
|
||||||
|
describe('showDowngradeOption', function () {
|
||||||
|
const today = new Date()
|
||||||
|
const sevenDaysFromToday = new Date().setDate(today.getDate() + 7)
|
||||||
|
const sevenDaysFromTodayFormatted = dateformat(
|
||||||
|
sevenDaysFromToday,
|
||||||
|
'dS mmmm yyyy'
|
||||||
|
)
|
||||||
|
|
||||||
|
it('returns false when no trial end date', function () {
|
||||||
|
expect(showDowngradeOption('collab')).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when a plan code without "collaborator" ', function () {
|
||||||
|
expect(showDowngradeOption('test', false, sevenDaysFromTodayFormatted)).to
|
||||||
|
.be.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with trial date in future but has "collaborator" and "ann" in plan code', function () {
|
||||||
|
expect(
|
||||||
|
showDowngradeOption(
|
||||||
|
'collaborator-annual',
|
||||||
|
false,
|
||||||
|
sevenDaysFromTodayFormatted
|
||||||
|
)
|
||||||
|
).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with trial date in future and plan code has "collaborator" and no "ann" but is a group plan', function () {
|
||||||
|
expect(
|
||||||
|
showDowngradeOption('collaborator', true, sevenDaysFromTodayFormatted)
|
||||||
|
).to.be.false
|
||||||
|
})
|
||||||
|
it('returns false when on a plan with "collaborator" and without "ann" and trial date in future', function () {
|
||||||
|
expect(
|
||||||
|
showDowngradeOption('collaborator', false, sevenDaysFromTodayFormatted)
|
||||||
|
).to.be.false
|
||||||
|
})
|
||||||
|
it('returns true when on a plan with "collaborator" and without "ann" and no trial date', function () {
|
||||||
|
expect(showDowngradeOption('collaborator', false)).to.be.true
|
||||||
|
})
|
||||||
|
it('returns true when on a plan with "collaborator" and without "ann" and trial date is in the past', function () {
|
||||||
|
expect(
|
||||||
|
showDowngradeOption('collaborator', false, '2000-02-16T17:59:07.000Z')
|
||||||
|
).to.be.true
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue