mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-11 18:45:25 +00:00
Implement groups & enterprise awareness banner on project dashboard (#10818)
- Implement the banner on both react and non-react project dashboard - Use split test with 4 different variants, `save`, `empower`, `did-you-know`, and `default`, each variant has a different copy, except the `default` which won't show the banner to users GitOrigin-RevId: ee76769dfd38b8e52de8cc0f201c24e41905d016
This commit is contained in:
parent
55375c78bf
commit
bed2596468
11 changed files with 418 additions and 3 deletions
|
@ -517,6 +517,41 @@ const ProjectController = {
|
|||
}
|
||||
)
|
||||
},
|
||||
userIsMemberOfGroupSubscription(cb) {
|
||||
LimitationsManager.userIsMemberOfGroupSubscription(
|
||||
currentUser,
|
||||
(error, isMember) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
{ err: error },
|
||||
'Failed to check whether user is a member of group subscription'
|
||||
)
|
||||
return cb(null, false)
|
||||
}
|
||||
cb(null, isMember)
|
||||
}
|
||||
)
|
||||
},
|
||||
groupsAndEnterpriseBannerAssignment(cb) {
|
||||
SplitTestHandler.getAssignment(
|
||||
req,
|
||||
res,
|
||||
'groups-and-enterprise-banner',
|
||||
(err, assignment) => {
|
||||
if (err) {
|
||||
logger.warn(
|
||||
{ err },
|
||||
'failed to get "groups-and-enterprise-banner" split test assignment'
|
||||
)
|
||||
|
||||
const defaultAssignment = { variant: 'default' }
|
||||
cb(null, defaultAssignment)
|
||||
} else {
|
||||
cb(null, assignment)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
primaryEmailCheckActive(cb) {
|
||||
SplitTestHandler.getAssignment(
|
||||
req,
|
||||
|
@ -552,8 +587,14 @@ const ProjectController = {
|
|||
OError.tag(err, 'error getting data for project list page')
|
||||
return next(err)
|
||||
}
|
||||
const { notifications, user, userEmailsData, primaryEmailCheckActive } =
|
||||
results
|
||||
const {
|
||||
notifications,
|
||||
user,
|
||||
userEmailsData,
|
||||
primaryEmailCheckActive,
|
||||
groupsAndEnterpriseBannerAssignment,
|
||||
userIsMemberOfGroupSubscription,
|
||||
} = results
|
||||
|
||||
if (
|
||||
user &&
|
||||
|
@ -682,6 +723,17 @@ const ProjectController = {
|
|||
)
|
||||
}
|
||||
|
||||
const hasPaidAffiliation = userAffiliations.some(
|
||||
affiliation => affiliation.licence && affiliation.licence !== 'free'
|
||||
)
|
||||
|
||||
// groupsAndEnterpriseBannerAssignment.variant = 'default' | 'empower' | 'save' | 'did-you-know'
|
||||
const showGroupsAndEnterpriseBanner =
|
||||
groupsAndEnterpriseBannerAssignment.variant !== 'default' &&
|
||||
Features.hasFeature('saas') &&
|
||||
!userIsMemberOfGroupSubscription &&
|
||||
!hasPaidAffiliation
|
||||
|
||||
ProjectController._injectProjectUsers(projects, (error, projects) => {
|
||||
if (error != null) {
|
||||
return next(error)
|
||||
|
@ -706,6 +758,9 @@ const ProjectController = {
|
|||
showThinFooter: true, // don't show the fat footer on the projects dashboard, as there's a fixed space available
|
||||
usersBestSubscription: results.usersBestSubscription,
|
||||
survey: results.survey,
|
||||
showGroupsAndEnterpriseBanner,
|
||||
groupsAndEnterpriseBannerVariant:
|
||||
groupsAndEnterpriseBannerAssignment.variant,
|
||||
}
|
||||
|
||||
const paidUser =
|
||||
|
|
|
@ -20,6 +20,7 @@ const { User } = require('../../models/User')
|
|||
const SplitTestHandler = require('../SplitTests/SplitTestHandler')
|
||||
const UserPrimaryEmailCheckHandler = require('../User/UserPrimaryEmailCheckHandler')
|
||||
const UserController = require('../User/UserController')
|
||||
const LimitationsManager = require('../Subscription/LimitationsManager')
|
||||
|
||||
/** @typedef {import("./types").GetProjectsRequest} GetProjectsRequest */
|
||||
/** @typedef {import("./types").GetProjectsResponse} GetProjectsResponse */
|
||||
|
@ -287,6 +288,48 @@ async function projectListReactPage(req, res, next) {
|
|||
status: prefetchedProjectsBlob ? 'success' : 'too-slow',
|
||||
})
|
||||
|
||||
let showGroupsAndEnterpriseBanner = false
|
||||
let groupsAndEnterpriseBannerAssignment
|
||||
|
||||
try {
|
||||
groupsAndEnterpriseBannerAssignment =
|
||||
await SplitTestHandler.promises.getAssignment(
|
||||
req,
|
||||
res,
|
||||
'groups-and-enterprise-banner'
|
||||
)
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
{ err: error },
|
||||
'failed to get "groups-and-enterprise-banner" split test assignment'
|
||||
)
|
||||
}
|
||||
|
||||
let userIsMemberOfGroupSubscription = false
|
||||
|
||||
try {
|
||||
const userIsMemberOfGroupSubscriptionPromise =
|
||||
await LimitationsManager.promises.userIsMemberOfGroupSubscription(user)
|
||||
|
||||
userIsMemberOfGroupSubscription =
|
||||
userIsMemberOfGroupSubscriptionPromise.isMember
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
{ err: error },
|
||||
'Failed to check whether user is a member of group subscription'
|
||||
)
|
||||
}
|
||||
|
||||
const hasPaidAffiliation = userAffiliations.some(
|
||||
affiliation => affiliation.licence && affiliation.licence !== 'free'
|
||||
)
|
||||
|
||||
showGroupsAndEnterpriseBanner =
|
||||
(groupsAndEnterpriseBannerAssignment?.variant ?? 'default') !== 'default' &&
|
||||
Features.hasFeature('saas') &&
|
||||
!userIsMemberOfGroupSubscription &&
|
||||
!hasPaidAffiliation
|
||||
|
||||
res.render('project/list-react', {
|
||||
title: 'your_projects',
|
||||
usersBestSubscription,
|
||||
|
@ -301,6 +344,9 @@ async function projectListReactPage(req, res, next) {
|
|||
tags,
|
||||
portalTemplates,
|
||||
prefetchedProjectsBlob,
|
||||
showGroupsAndEnterpriseBanner,
|
||||
groupsAndEnterpriseBannerVariant:
|
||||
groupsAndEnterpriseBannerAssignment?.variant ?? 'default',
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ block append meta
|
|||
imgUrl: buildImgPath("flags/24/" + suggestedLanguageSubdomainConfig.lngCode + ".png")
|
||||
}))
|
||||
meta(name="ol-currentUrl" data-type="string" content=currentUrl)
|
||||
meta(name="ol-showGroupsAndEnterpriseBanner" data-type="boolean" content=showGroupsAndEnterpriseBanner)
|
||||
meta(name="ol-groupsAndEnterpriseBannerVariant" data-type="string" content=groupsAndEnterpriseBannerVariant)
|
||||
|
||||
block content
|
||||
main.content.content-alt.project-list-react#project-list-root
|
||||
|
|
|
@ -14,6 +14,7 @@ block append meta
|
|||
meta(name="ol-allInReconfirmNotificationPeriods" data-type="json" content=allInReconfirmNotificationPeriods)
|
||||
meta(name="ol-reconfirmedViaSAML" content=reconfirmedViaSAML)
|
||||
meta(name="ol-survey-name" data-type="string" content=(survey ? survey.name : undefined))
|
||||
meta(name="ol-groupsAndEnterpriseBannerVariant" data-type="string" content=groupsAndEnterpriseBannerVariant)
|
||||
|
||||
block content
|
||||
|
||||
|
|
|
@ -251,3 +251,35 @@ include ../../_mixins/reconfirm_affiliation
|
|||
ng-if="userEmail.samlIdentifier && userEmail.samlIdentifier.providerId === reconfirmedViaSAML"
|
||||
)
|
||||
+reconfirmedAffiliationNotification()
|
||||
|
||||
if showGroupsAndEnterpriseBanner
|
||||
- var eventSegmentation = '{"location": "dashboard-banner", "variant":"' + groupsAndEnterpriseBannerVariant + '" }'
|
||||
ul.list-unstyled(
|
||||
ng-controller="GroupsAndEnterpriseBannerController",
|
||||
ng-cloak
|
||||
)
|
||||
li.notification-entry(
|
||||
ng-if="isVariantValid && !hasDismissedGroupsAndEnterpriseBanner && projects.length > 0"
|
||||
event-tracking="groups-and-enterprise-banner-prompt"
|
||||
event-tracking-mb="true"
|
||||
event-tracking-trigger="load"
|
||||
event-segmentation=eventSegmentation
|
||||
)
|
||||
.alert.alert-info
|
||||
.notification-body(ng-switch="groupsAndEnterpriseBannerVariant")
|
||||
span(ng-switch-when="empower") #{translate("empower_your_organization_to_work_in_overleaf")}
|
||||
span(ng-switch-when="save") !{translate("save_money_groups_companies_research_organizations_can_save_money", {}, ['strong'])}
|
||||
span(ng-switch-when="did-you-know") #{translate("did_you_know_that_overleaf_offers")}
|
||||
.notification-action
|
||||
a.pull-right.btn.btn-sm.btn-info(
|
||||
href="/for/contact-sales"
|
||||
target="_blank"
|
||||
event-tracking="groups-and-enterprise-banner-click"
|
||||
event-tracking-mb="true"
|
||||
event-tracking-trigger="click"
|
||||
event-segmentation=eventSegmentation
|
||||
) #{translate("contact_sales")}
|
||||
.notification-close
|
||||
button(ng-click="dismiss()").close.pull-right
|
||||
span(aria-hidden="true") ×
|
||||
span.sr-only #{translate("close")}
|
||||
|
|
|
@ -107,6 +107,7 @@
|
|||
"conflicting_paths_found": "",
|
||||
"connected_users": "",
|
||||
"contact_message_label": "",
|
||||
"contact_sales": "",
|
||||
"contact_us": "",
|
||||
"continue_github_merge": "",
|
||||
"copy": "",
|
||||
|
@ -141,6 +142,7 @@
|
|||
"description": "",
|
||||
"dictionary": "",
|
||||
"did_you_know_institution_providing_professional": "",
|
||||
"did_you_know_that_overleaf_offers": "",
|
||||
"disable_stop_on_first_error": "",
|
||||
"dismiss": "",
|
||||
"dismiss_error_popup": "",
|
||||
|
@ -184,6 +186,7 @@
|
|||
"email_or_password_wrong_try_again": "",
|
||||
"emails_and_affiliations_explanation": "",
|
||||
"emails_and_affiliations_title": "",
|
||||
"empower_your_organization_to_work_in_overleaf": "",
|
||||
"error": "",
|
||||
"error_performing_request": "",
|
||||
"example_project": "",
|
||||
|
@ -555,6 +558,7 @@
|
|||
"revoke": "",
|
||||
"revoke_invite": "",
|
||||
"role": "",
|
||||
"save_money_groups_companies_research_organizations_can_save_money": "",
|
||||
"save_or_cancel-cancel": "",
|
||||
"save_or_cancel-or": "",
|
||||
"save_or_cancel-save": "",
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
import { useCallback, useEffect, useMemo } from 'react'
|
||||
import Notification from './notification'
|
||||
import * as eventTracking from '../../../../infrastructure/event-tracking'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import customLocalStorage from '../../../../infrastructure/local-storage'
|
||||
import { useProjectListContext } from '../../context/project-list-context'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
|
||||
type GroupsAndEnterpriseBannerVariant =
|
||||
| 'default'
|
||||
| 'empower'
|
||||
| 'save'
|
||||
| 'did-you-know'
|
||||
|
||||
export default function GroupsAndEnterpriseBanner() {
|
||||
const { t } = useTranslation()
|
||||
const { totalProjectsCount } = useProjectListContext()
|
||||
const showGroupsAndEnterpriseBanner = getMeta(
|
||||
'ol-showGroupsAndEnterpriseBanner'
|
||||
) as boolean
|
||||
const groupsAndEnterpriseBannerVariant = getMeta(
|
||||
'ol-groupsAndEnterpriseBannerVariant'
|
||||
) as GroupsAndEnterpriseBannerVariant
|
||||
|
||||
const eventTrackingSegmentation = useMemo(
|
||||
() => ({
|
||||
location: 'dashboard-banner-react',
|
||||
variant: groupsAndEnterpriseBannerVariant,
|
||||
page: '/project',
|
||||
}),
|
||||
[groupsAndEnterpriseBannerVariant]
|
||||
)
|
||||
|
||||
const hasDismissedGroupsAndEnterpriseBanner = customLocalStorage.getItem(
|
||||
'has_dismissed_groups_and_enterprise_banner'
|
||||
)
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
customLocalStorage.setItem(
|
||||
'has_dismissed_groups_and_enterprise_banner',
|
||||
true
|
||||
)
|
||||
}, [])
|
||||
|
||||
const handleClickContact = useCallback(() => {
|
||||
eventTracking.sendMB(
|
||||
'groups-and-enterprise-banner-click',
|
||||
eventTrackingSegmentation
|
||||
)
|
||||
}, [eventTrackingSegmentation])
|
||||
|
||||
useEffect(() => {
|
||||
eventTracking.sendMB(
|
||||
'groups-and-enterprise-banner-prompt',
|
||||
eventTrackingSegmentation
|
||||
)
|
||||
}, [eventTrackingSegmentation])
|
||||
|
||||
if (
|
||||
totalProjectsCount === 0 ||
|
||||
hasDismissedGroupsAndEnterpriseBanner ||
|
||||
!showGroupsAndEnterpriseBanner
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
// `getText` function has no default switch case since the whole notification
|
||||
// should not be rendered if the `groupsAndEnterpriseBannerVariant` is not valid
|
||||
if (!isVariantValid(groupsAndEnterpriseBannerVariant)) {
|
||||
return null
|
||||
}
|
||||
|
||||
// this shouldn't ever happens since the value of `showGroupsAndEnterpriseBanner` should be false
|
||||
// if `groupsAndEnterpriseBannerVariant` is 'default'
|
||||
// but just adding this check as an extra measure
|
||||
if (groupsAndEnterpriseBannerVariant === 'default') {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Notification bsStyle="info" onDismiss={handleClose}>
|
||||
<Notification.Body>
|
||||
<span>{getText(groupsAndEnterpriseBannerVariant)}</span>
|
||||
</Notification.Body>
|
||||
<Notification.Action>
|
||||
<a
|
||||
className="pull-right btn btn-info btn-sm"
|
||||
href="/for/contact-sales"
|
||||
target="_blank"
|
||||
onClick={handleClickContact}
|
||||
>
|
||||
{t('contact_sales')}
|
||||
</a>
|
||||
</Notification.Action>
|
||||
</Notification>
|
||||
)
|
||||
}
|
||||
|
||||
function isVariantValid(variant: GroupsAndEnterpriseBannerVariant) {
|
||||
return (
|
||||
variant === 'empower' || variant === 'save' || variant === 'did-you-know'
|
||||
)
|
||||
}
|
||||
|
||||
function getText(variant: GroupsAndEnterpriseBannerVariant) {
|
||||
switch (variant) {
|
||||
case 'empower':
|
||||
return <Trans i18nKey="empower_your_organization_to_work_in_overleaf" />
|
||||
case 'save':
|
||||
return (
|
||||
<Trans
|
||||
i18nKey="save_money_groups_companies_research_organizations_can_save_money"
|
||||
components={
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
[<strong />]
|
||||
}
|
||||
/>
|
||||
)
|
||||
case 'did-you-know':
|
||||
return <Trans i18nKey="did_you_know_that_overleaf_offers" />
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import Common from './groups/common'
|
|||
import Institution from './groups/institution'
|
||||
import ConfirmEmail from './groups/confirm-email'
|
||||
import ReconfirmationInfo from './groups/affiliation/reconfirmation-info'
|
||||
import GroupsAndEnterpriseBanner from './groups-and-enterprise-banner'
|
||||
|
||||
function UserNotifications() {
|
||||
return (
|
||||
|
@ -11,6 +12,7 @@ function UserNotifications() {
|
|||
<Institution />
|
||||
<ConfirmEmail />
|
||||
<ReconfirmationInfo />
|
||||
<GroupsAndEnterpriseBanner />
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import App from '../../base'
|
||||
import getMeta from '../../utils/meta'
|
||||
|
||||
const ExposedSettings = window.ExposedSettings
|
||||
App.controller('NotificationsController', function ($scope, $http) {
|
||||
|
@ -23,6 +24,29 @@ App.controller('NotificationsController', function ($scope, $http) {
|
|||
}
|
||||
})
|
||||
|
||||
App.controller(
|
||||
'GroupsAndEnterpriseBannerController',
|
||||
function ($scope, localStorage) {
|
||||
$scope.hasDismissedGroupsAndEnterpriseBanner = localStorage(
|
||||
'has_dismissed_groups_and_enterprise_banner'
|
||||
)
|
||||
|
||||
$scope.dismiss = () => {
|
||||
localStorage('has_dismissed_groups_and_enterprise_banner', true)
|
||||
$scope.hasDismissedGroupsAndEnterpriseBanner = true
|
||||
}
|
||||
|
||||
$scope.groupsAndEnterpriseBannerVariant = getMeta(
|
||||
'ol-groupsAndEnterpriseBannerVariant'
|
||||
)
|
||||
|
||||
$scope.isVariantValid =
|
||||
$scope.groupsAndEnterpriseBannerVariant === 'save' ||
|
||||
$scope.groupsAndEnterpriseBannerVariant === 'empower' ||
|
||||
$scope.groupsAndEnterpriseBannerVariant === 'did-you-know'
|
||||
}
|
||||
)
|
||||
|
||||
App.controller('ProjectInviteNotificationController', function ($scope, $http) {
|
||||
// Shortcuts for translation keys
|
||||
$scope.projectName = $scope.notification.messageOpts.projectName
|
||||
|
|
|
@ -1940,5 +1940,9 @@
|
|||
"try_out_one_of_our_plans_instead": "Try out one of our plans instead",
|
||||
"browse_plans": "Browse plans",
|
||||
"i_confirm_that_i_am_a_student": "I confirm that I am a student",
|
||||
"a_more_comprehensive_list_of_keyboard_shortcuts": "A more comprehensive list of keyboard shortcuts can be found in <0>this __appName__ project template</0>"
|
||||
"a_more_comprehensive_list_of_keyboard_shortcuts": "A more comprehensive list of keyboard shortcuts can be found in <0>this __appName__ project template</0>",
|
||||
"contact_sales": "Contact Sales",
|
||||
"empower_your_organization_to_work_in_overleaf": "Empower your organization to work in __appName__! Get a group or organizational plan.",
|
||||
"save_money_groups_companies_research_organizations_can_save_money": "<0>Save Money</0>! Groups, Companies and Research Organizations can save money with our Group and Enterprise plans — request information or a quote.",
|
||||
"did_you_know_that_overleaf_offers": "Did you know that __appName__ offers group and organization-wide subscription options? Request information or a quote."
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ import {
|
|||
} from '../../../../../types/project/dashboard/notification'
|
||||
import { DeepPartial } from '../../../../../types/utils'
|
||||
import { Project } from '../../../../../types/project/dashboard/api'
|
||||
import GroupsAndEnterpriseBanner from '../../../../../frontend/js/features/project-list/components/notifications/groups-and-enterprise-banner'
|
||||
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
|
||||
|
||||
const renderWithinProjectListProvider = (Component: React.ComponentType) => {
|
||||
render(<Component />, {
|
||||
|
@ -640,4 +642,125 @@ describe('<UserNotifications />', function () {
|
|||
expect(screen.queryByRole('alert')).to.be.null
|
||||
})
|
||||
})
|
||||
|
||||
describe('<GroupsAndEnterpriseBanner />', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache = window.metaAttributesCache || new Map()
|
||||
localStorage.clear()
|
||||
fetchMock.reset()
|
||||
|
||||
// at least one project is required to show some notifications
|
||||
const projects = [{}] as Project[]
|
||||
fetchMock.post(/\/api\/project/, {
|
||||
status: 200,
|
||||
body: {
|
||||
projects,
|
||||
totalSize: projects.length,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
fetchMock.reset()
|
||||
window.metaAttributesCache = window.metaAttributesCache || new Map()
|
||||
})
|
||||
|
||||
it('does not show the banner for users that are in group or are affiliated or assigned in the `default` variant', async function () {
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', false)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.flush(true)
|
||||
|
||||
expect(screen.queryByRole('link', { name: 'Contact Sales' })).to.be.null
|
||||
})
|
||||
|
||||
it('does not show the banner for users that have already dismissed it', async function () {
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', true)
|
||||
localStorage.setItem('has_dismissed_groups_and_enterprise_banner', true)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.flush(true)
|
||||
|
||||
expect(screen.queryByRole('link', { name: 'Contact Sales' })).to.be.null
|
||||
})
|
||||
|
||||
describe('users that are not in group and are not affiliated', function () {
|
||||
beforeEach(function () {
|
||||
localStorage.clear()
|
||||
fetchMock.reset()
|
||||
|
||||
// at least one project is required to show some notifications
|
||||
const projects = [{}] as Project[]
|
||||
fetchMock.post(/\/api\/project/, {
|
||||
status: 200,
|
||||
body: {
|
||||
projects,
|
||||
totalSize: projects.length,
|
||||
},
|
||||
})
|
||||
|
||||
window.metaAttributesCache.set('ol-showGroupsAndEnterpriseBanner', true)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
fetchMock.reset()
|
||||
window.metaAttributesCache = window.metaAttributesCache || new Map()
|
||||
})
|
||||
|
||||
after(function () {
|
||||
localStorage.clear()
|
||||
})
|
||||
|
||||
it('will show the correct text for the `save` split test variant', async function () {
|
||||
window.metaAttributesCache.set(
|
||||
'ol-groupsAndEnterpriseBannerVariant',
|
||||
'save'
|
||||
)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.flush(true)
|
||||
|
||||
screen.getByText(
|
||||
/Groups, Companies and Research Organizations can save money with our Group and Enterprise plans — request information or a quote./
|
||||
)
|
||||
const link = screen.getByRole('link', { name: 'Contact Sales' })
|
||||
|
||||
expect(link.getAttribute('href')).to.equal(`/for/contact-sales`)
|
||||
})
|
||||
|
||||
it('will show the correct text for the `did-you-know` split test variant', async function () {
|
||||
window.metaAttributesCache.set(
|
||||
'ol-groupsAndEnterpriseBannerVariant',
|
||||
'did-you-know'
|
||||
)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.flush(true)
|
||||
|
||||
screen.getByText(
|
||||
'Did you know that Overleaf offers group and organization-wide subscription options? Request information or a quote.'
|
||||
)
|
||||
const link = screen.getByRole('link', { name: 'Contact Sales' })
|
||||
|
||||
expect(link.getAttribute('href')).to.equal(`/for/contact-sales`)
|
||||
})
|
||||
|
||||
it('will show the correct text for the `empower` split test variant', async function () {
|
||||
window.metaAttributesCache.set(
|
||||
'ol-groupsAndEnterpriseBannerVariant',
|
||||
'empower'
|
||||
)
|
||||
|
||||
renderWithinProjectListProvider(GroupsAndEnterpriseBanner)
|
||||
await fetchMock.flush(true)
|
||||
|
||||
screen.getByText(
|
||||
'Empower your organization to work in Overleaf! Get a group or organizational plan.'
|
||||
)
|
||||
const link = screen.getByRole('link', { name: 'Contact Sales' })
|
||||
|
||||
expect(link.getAttribute('href')).to.equal(`/for/contact-sales`)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue