Tw email confirmation notification (#14088)

* Edited notification copy for user with affilation that ha commons account

* edited translation key to have overleaf professional appear in bold and switched to using trans component

* changed anchor tag to button and added additional check to ensure affiliation exists before accessing institution name

* add tests for commons user confirmation notification

* edit user email context test to facilitate test data changes

* edit license in fake test data

* edit test to check text of notification for non commons user

GitOrigin-RevId: 6700ceaa066f099ce593283887fa81d72ad624b6
This commit is contained in:
Tyna William
2023-08-14 08:44:16 -04:00
committed by Copybot
parent 38cb2dbd1e
commit dd71c9924e
7 changed files with 134 additions and 6 deletions

View File

@@ -516,6 +516,7 @@
"institution_account": "",
"institution_acct_successfully_linked_2": "",
"institution_and_role": "",
"institution_has_overleaf_subscription": "",
"institution_templates": "",
"institutional_leavers_survey_notification": "",
"integrations": "",
@@ -704,6 +705,7 @@
"ok": "",
"on": "",
"on_free_plan_upgrade_to_access_features": "",
"one_step_away_from_professional_features": "",
"only_group_admin_or_managers_can_delete_your_account": "",
"open_file": "",
"open_link": "",
@@ -866,6 +868,7 @@
"republish": "",
"resend": "",
"resend_confirmation_email": "",
"resend_email": "",
"resending_confirmation_email": "",
"resolve": "",
"resolved_comments": "",

View File

@@ -11,7 +11,6 @@ function ReconfirmationInfo() {
) as UserEmailData[]
const userEmails = getMeta('ol-userEmails', []) as UserEmailData[]
const reconfirmedViaSAML = getMeta('ol-reconfirmedViaSAML') as string
return (
<>
{allInReconfirmNotificationPeriods.map(userEmail =>

View File

@@ -1,4 +1,4 @@
import { useTranslation } from 'react-i18next'
import { Trans, useTranslation } from 'react-i18next'
import { Button } from 'react-bootstrap'
import Notification from '../notification'
import Icon from '../../../../../shared/components/icon'
@@ -35,6 +35,28 @@ const ssoAvailable = ({ samlProviderId, affiliation }: UserEmailData) => {
return false
}
function emailHasLicenceAfterConfirming(emailData: UserEmailData) {
if (emailData.confirmedAt) {
return false
}
if (!emailData.affiliation) {
return false
}
const affiliation = emailData.affiliation
const institution = affiliation.institution
if (!institution) {
return false
}
if (!institution.confirmed) {
return false
}
if (affiliation.pastReconfirmDate) {
return false
}
return affiliation.institution.commonsAccount
}
const showConfirmEmail = (userEmail: UserEmailData) => {
const { emailConfirmationDisabled } = getMeta(
'ol-ExposedSettings'
@@ -63,9 +85,48 @@ function ConfirmEmailNotification({ userEmail }: { userEmail: UserEmailData }) {
return null
}
if (emailHasLicenceAfterConfirming(userEmail)) {
return (
<Notification bsStyle="info">
<Notification.Body data-testid="notification-body">
{isLoading ? (
<>
<Icon type="spinner" spin /> {t('resending_confirmation_email')}
&hellip;
</>
) : isError ? (
<div aria-live="polite">{getUserFacingMessage(error)}</div>
) : (
<>
<Trans
i18nKey="one_step_away_from_professional_features"
components={[<strong />]} // eslint-disable-line react/jsx-key
/>
<button
className="pull-right btn btn-info btn-sm"
onClick={() => handleResendConfirmationEmail(userEmail)}
>
{t('resend_email')}
</button>
<br />
<Trans
i18nKey="institution_has_overleaf_subscription"
values={{
institutionName: userEmail.affiliation?.institution.name,
emailAddress: userEmail.email,
}}
components={[<strong />]} // eslint-disable-line react/jsx-key
/>
</>
)}
</Notification.Body>
</Notification>
)
}
return (
<Notification bsStyle="warning">
<Notification.Body>
<Notification.Body data-testid="pro-notification-body">
{isLoading ? (
<>
<Icon type="spinner" spin /> {t('resending_confirmation_email')}

View File

@@ -813,6 +813,7 @@
"institution_acct_successfully_linked_2": "Your <0>__appName__</0> account was successfully linked to your <0>__institutionName__</0> institutional account.",
"institution_and_role": "Institution and role",
"institution_email_new_to_app": "Your <b>__institutionName__</b> email (<b>__email__</b>) is new to __appName__.",
"institution_has_overleaf_subscription": "<0>__institutionName__</0> has an Overleaf subscription. Click the confirmation link sent to __emailAddress__ to upgrade to <0>Overleaf Professional</0>.",
"institution_templates": "Institution Templates",
"institutional": "Institutional",
"institutional_leavers_survey_notification": "Provide some quick feedback to receive a 25% discount on an annual subscription!",
@@ -1140,6 +1141,7 @@
"one_collaborator": "Only one collaborator",
"one_free_collab": "One free collaborator",
"one_minute": "1 minute",
"one_step_away_from_professional_features": "You are one step away from accessing <0>Overleaf Professional features</0>!",
"one_user": "1 user",
"online_latex_editor": "Online LaTeX Editor",
"only_group_admin_or_managers_can_delete_your_account": "Only your group admin or group managers will be able to delete your account.",
@@ -1402,6 +1404,7 @@
"required": "Required",
"resend": "Resend",
"resend_confirmation_email": "Resend confirmation email",
"resend_email": "Resend email",
"resending_confirmation_email": "Resending confirmation email",
"reset_password": "Reset Password",
"reset_your_password": "Reset your password",

View File

@@ -5,12 +5,14 @@ import {
render,
screen,
waitForElementToBeRemoved,
within,
} from '@testing-library/react'
import fetchMock from 'fetch-mock'
import { merge, cloneDeep } from 'lodash'
import {
professionalUserData,
unconfirmedUserData,
unconfirmedCommonsUserData,
} from '../../settings/fixtures/test-user-email-data'
import {
notification,
@@ -508,9 +510,6 @@ describe('<UserNotifications />', function () {
emailConfirmationDisabled: false,
})
window.metaAttributesCache.set('ol-userEmails', [unconfirmedUserData])
renderWithinProjectListProvider(ConfirmEmail)
await fetchMock.flush(true)
})
afterEach(function () {
@@ -518,8 +517,16 @@ describe('<UserNotifications />', function () {
})
it('sends successfully', async function () {
renderWithinProjectListProvider(ConfirmEmail)
await fetchMock.flush(true)
fetchMock.post('/user/emails/resend_confirmation', 200)
const email = unconfirmedUserData.email
const notificationBody = screen.getByTestId('pro-notification-body')
expect(notificationBody.textContent).to.contain(
`Please confirm your email ${email} by clicking on the link in the confirmation email`
)
const resendButton = screen.getByRole('button', { name: /resend/i })
fireEvent.click(resendButton)
@@ -534,6 +541,8 @@ describe('<UserNotifications />', function () {
})
it('fails to send', async function () {
renderWithinProjectListProvider(ConfirmEmail)
await fetchMock.flush(true)
fetchMock.post('/user/emails/resend_confirmation', 500)
const resendButton = screen.getByRole('button', { name: /resend/i })
@@ -546,6 +555,25 @@ describe('<UserNotifications />', function () {
expect(fetchMock.called()).to.be.true
screen.getByText(/something went wrong/i)
})
it('shows notification for commons account', async function () {
window.metaAttributesCache.set('ol-userEmails', [
unconfirmedCommonsUserData,
])
renderWithinProjectListProvider(ConfirmEmail)
await fetchMock.flush(true)
const alert = screen.getByRole('alert')
const email = unconfirmedCommonsUserData.email
const notificationBody = within(alert).getByTestId('notification-body')
expect(notificationBody.textContent).to.contain(
'You are one step away from accessing Overleaf Professional features'
)
expect(notificationBody.textContent).to.contain(
`Overleaf has an Overleaf subscription. Click the confirmation link sent to ${email} to upgrade to Overleaf Professional`
)
})
})
describe('<Affiliation/>', function () {

View File

@@ -12,6 +12,7 @@ import {
professionalUserData,
unconfirmedUserData,
fakeUsersData,
unconfirmedCommonsUserData,
} from '../fixtures/test-user-email-data'
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
@@ -53,6 +54,7 @@ describe('UserEmailContext', function () {
'bar@overleaf.com': confirmedUserData,
'baz@overleaf.com': unconfirmedUserData,
'foo@overleaf.com': professionalUserData,
'qux@overleaf.com': unconfirmedCommonsUserData,
})
expect(result.current.state.data.linkedInstitutionIds).to.have.lengthOf(0)

View File

@@ -44,6 +44,37 @@ export const professionalUserData: UserEmailData & {
default: true,
}
export const unconfirmedCommonsUserData: UserEmailData & {
affiliation: Affiliation
} = {
affiliation: {
cachedConfirmedAt: null,
cachedEntitlement: null,
cachedLastDayToReconfirm: null,
cachedPastReconfirmDate: false,
cachedReconfirmedAt: null,
department: 'Art History',
institution: {
commonsAccount: true,
confirmed: true,
id: 1,
isUniversity: false,
maxConfirmationMonths: null,
name: 'Overleaf',
ssoEnabled: false,
ssoBeta: false,
},
inReconfirmNotificationPeriod: false,
inferred: false,
licence: 'free',
pastReconfirmDate: false,
portal: { slug: '', templates_count: 1 },
role: 'Reader',
},
email: 'qux@overleaf.com',
default: true,
}
export const ssoUserData: UserEmailData = {
affiliation: {
cachedConfirmedAt: '2022-02-03T11:46:28.249Z',
@@ -79,4 +110,5 @@ export const fakeUsersData = [
{ ...confirmedUserData },
{ ...unconfirmedUserData },
{ ...professionalUserData },
{ ...unconfirmedCommonsUserData },
]