mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-15 06:33:55 +01:00
[web] Update secondary email addition: confirm with 6 digits code (#22851)
* Remove `Interstitial` from `ConfirmEmailForm` * Allow adding `affiliationOptions` in `addWithConfirmationCode` * Add confirmationStep in add-email.tsx * Call `getEmails` once a secondary email is added * Fix tests * Lint fix * Style confirm-email-form Figma: https://www.figma.com/design/TWyeImDSZHhkl9akYaGmeb/24.5-Secondary-email-reconfirmation?node-id=1-449&p=f&m=dev * Remove unnecessary `successMessage` and `successButtonText` from hidden ConfirmEmailForm * Remove icon padding * Rename file to confirm-email-form.tsx * Use `OLButton` * Add Cancel button * Update loading states * Remove redundant `className` with variants GitOrigin-RevId: 62b1729cf2299da38f20fa3946273ad0193c7d54
This commit is contained in:
@@ -155,6 +155,12 @@ async function addWithConfirmationCode(req, res) {
|
|||||||
|
|
||||||
const userId = SessionManager.getLoggedInUserId(req.session)
|
const userId = SessionManager.getLoggedInUserId(req.session)
|
||||||
const email = EmailHelper.parseEmail(req.body.email)
|
const email = EmailHelper.parseEmail(req.body.email)
|
||||||
|
const affiliationOptions = {
|
||||||
|
university: req.body.university,
|
||||||
|
role: req.body.role,
|
||||||
|
department: req.body.department,
|
||||||
|
}
|
||||||
|
|
||||||
if (!email) {
|
if (!email) {
|
||||||
return res.sendStatus(422)
|
return res.sendStatus(422)
|
||||||
}
|
}
|
||||||
@@ -195,6 +201,7 @@ async function addWithConfirmationCode(req, res) {
|
|||||||
email,
|
email,
|
||||||
confirmCode,
|
confirmCode,
|
||||||
confirmCodeExpiresTimestamp,
|
confirmCodeExpiresTimestamp,
|
||||||
|
affiliationOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.sendStatus(200)
|
return res.sendStatus(200)
|
||||||
@@ -291,7 +298,7 @@ async function checkSecondaryEmailConfirmationCode(req, res) {
|
|||||||
await UserUpdater.promises.addEmailAddress(
|
await UserUpdater.promises.addEmailAddress(
|
||||||
userId,
|
userId,
|
||||||
req.session.pendingSecondaryEmail.email,
|
req.session.pendingSecondaryEmail.email,
|
||||||
{},
|
req.session.pendingSecondaryEmail.affiliationOptions,
|
||||||
{
|
{
|
||||||
initiatorId: user._id,
|
initiatorId: user._id,
|
||||||
ipAddress: req.ip,
|
ipAddress: req.ip,
|
||||||
@@ -301,7 +308,7 @@ async function checkSecondaryEmailConfirmationCode(req, res) {
|
|||||||
await UserUpdater.promises.confirmEmail(
|
await UserUpdater.promises.confirmEmail(
|
||||||
userId,
|
userId,
|
||||||
req.session.pendingSecondaryEmail.email,
|
req.session.pendingSecondaryEmail.email,
|
||||||
{}
|
req.session.pendingSecondaryEmail.affiliationOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
delete req.session.pendingSecondaryEmail
|
delete req.session.pendingSecondaryEmail
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { ReCaptcha2 } from '../../../../shared/components/recaptcha-2'
|
|||||||
import { useRecaptcha } from '../../../../shared/hooks/use-recaptcha'
|
import { useRecaptcha } from '../../../../shared/hooks/use-recaptcha'
|
||||||
import OLCol from '@/features/ui/components/ol/ol-col'
|
import OLCol from '@/features/ui/components/ol/ol-col'
|
||||||
import { bsVersion } from '@/features/utils/bootstrap-5'
|
import { bsVersion } from '@/features/utils/bootstrap-5'
|
||||||
|
import { ConfirmEmailForm } from '@/features/settings/components/emails/confirm-email-form'
|
||||||
|
|
||||||
function AddEmail() {
|
function AddEmail() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -26,6 +27,7 @@ function AddEmail() {
|
|||||||
() => window.location.hash === '#add-email'
|
() => window.location.hash === '#add-email'
|
||||||
)
|
)
|
||||||
const [newEmail, setNewEmail] = useState('')
|
const [newEmail, setNewEmail] = useState('')
|
||||||
|
const [confirmationStep, setConfirmationStep] = useState(false)
|
||||||
const [newEmailMatchedDomain, setNewEmailMatchedDomain] =
|
const [newEmailMatchedDomain, setNewEmailMatchedDomain] =
|
||||||
useState<DomainInfo | null>(null)
|
useState<DomainInfo | null>(null)
|
||||||
const [countryCode, setCountryCode] = useState<CountryCode | null>(null)
|
const [countryCode, setCountryCode] = useState<CountryCode | null>(null)
|
||||||
@@ -90,7 +92,7 @@ function AddEmail() {
|
|||||||
runAsync(
|
runAsync(
|
||||||
(async () => {
|
(async () => {
|
||||||
const token = await getReCaptchaToken()
|
const token = await getReCaptchaToken()
|
||||||
await postJSON('/user/emails', {
|
await postJSON('/user/emails/secondary', {
|
||||||
body: {
|
body: {
|
||||||
email: newEmail,
|
email: newEmail,
|
||||||
...knownUniversityData,
|
...knownUniversityData,
|
||||||
@@ -101,11 +103,28 @@ function AddEmail() {
|
|||||||
})()
|
})()
|
||||||
)
|
)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
getEmails()
|
setConfirmationStep(true)
|
||||||
})
|
})
|
||||||
.catch(() => {})
|
.catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (confirmationStep) {
|
||||||
|
return (
|
||||||
|
<ConfirmEmailForm
|
||||||
|
confirmationEndpoint="/user/emails/confirm-secondary"
|
||||||
|
resendEndpoint="/user/emails/resend-secondary-confirmation"
|
||||||
|
flow="secondary"
|
||||||
|
email={newEmail}
|
||||||
|
onSuccessfulConfirmation={getEmails}
|
||||||
|
interstitial={false}
|
||||||
|
onCancel={() => {
|
||||||
|
setConfirmationStep(false)
|
||||||
|
setIsFormVisible(false)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!isFormVisible) {
|
if (!isFormVisible) {
|
||||||
return (
|
return (
|
||||||
<Layout isError={isError} error={error}>
|
<Layout isError={isError} error={error}>
|
||||||
|
|||||||
+96
-72
@@ -2,13 +2,13 @@ import { postJSON } from '@/infrastructure/fetch-json'
|
|||||||
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
import useWaitForI18n from '@/shared/hooks/use-wait-for-i18n'
|
||||||
import Notification from '@/shared/components/notification'
|
import Notification from '@/shared/components/notification'
|
||||||
import getMeta from '@/utils/meta'
|
import getMeta from '@/utils/meta'
|
||||||
import { FormEvent, useState } from 'react'
|
import { FormEvent, MouseEventHandler, useState } from 'react'
|
||||||
import { Button } from 'react-bootstrap'
|
|
||||||
import { Trans, useTranslation } from 'react-i18next'
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
import LoadingSpinner from '@/shared/components/loading-spinner'
|
import LoadingSpinner from '@/shared/components/loading-spinner'
|
||||||
import MaterialIcon from '@/shared/components/material-icon'
|
import MaterialIcon from '@/shared/components/material-icon'
|
||||||
import { sendMB } from '@/infrastructure/event-tracking'
|
import { sendMB } from '@/infrastructure/event-tracking'
|
||||||
import { Interstitial } from '@/shared/components/interstitial'
|
import OLFormLabel from '@/features/ui/components/ol/ol-form-label'
|
||||||
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
||||||
|
|
||||||
type Feedback = {
|
type Feedback = {
|
||||||
type: 'input' | 'alert'
|
type: 'input' | 'alert'
|
||||||
@@ -20,8 +20,12 @@ type ConfirmEmailFormProps = {
|
|||||||
confirmationEndpoint: string
|
confirmationEndpoint: string
|
||||||
flow: string
|
flow: string
|
||||||
resendEndpoint: string
|
resendEndpoint: string
|
||||||
successMessage: React.ReactNode
|
successMessage?: React.ReactNode
|
||||||
successButtonText: string
|
successButtonText?: string
|
||||||
|
email?: string
|
||||||
|
onSuccessfulConfirmation?: () => void
|
||||||
|
interstitial: boolean
|
||||||
|
onCancel?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConfirmEmailForm({
|
export function ConfirmEmailForm({
|
||||||
@@ -30,6 +34,10 @@ export function ConfirmEmailForm({
|
|||||||
resendEndpoint,
|
resendEndpoint,
|
||||||
successMessage,
|
successMessage,
|
||||||
successButtonText,
|
successButtonText,
|
||||||
|
email = getMeta('ol-email'),
|
||||||
|
onSuccessfulConfirmation,
|
||||||
|
interstitial,
|
||||||
|
onCancel,
|
||||||
}: ConfirmEmailFormProps) {
|
}: ConfirmEmailFormProps) {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [confirmationCode, setConfirmationCode] = useState('')
|
const [confirmationCode, setConfirmationCode] = useState('')
|
||||||
@@ -37,7 +45,6 @@ export function ConfirmEmailForm({
|
|||||||
const [isConfirming, setIsConfirming] = useState(false)
|
const [isConfirming, setIsConfirming] = useState(false)
|
||||||
const [isResending, setIsResending] = useState(false)
|
const [isResending, setIsResending] = useState(false)
|
||||||
const [successRedirectPath, setSuccessRedirectPath] = useState('')
|
const [successRedirectPath, setSuccessRedirectPath] = useState('')
|
||||||
const email = getMeta('ol-email')
|
|
||||||
const { isReady } = useWaitForI18n()
|
const { isReady } = useWaitForI18n()
|
||||||
|
|
||||||
const errorHandler = (err: any, actionType?: string) => {
|
const errorHandler = (err: any, actionType?: string) => {
|
||||||
@@ -78,33 +85,31 @@ export function ConfirmEmailForm({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitHandler = (e: FormEvent<HTMLFormElement>) => {
|
const submitHandler = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
setIsConfirming(true)
|
setIsConfirming(true)
|
||||||
setFeedback(null)
|
setFeedback(null)
|
||||||
|
|
||||||
postJSON(confirmationEndpoint, {
|
|
||||||
body: {
|
|
||||||
code: confirmationCode,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
setSuccessRedirectPath(data?.redir || '/')
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
errorHandler(err, 'confirm')
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsConfirming(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
sendMB('email-verification-click', {
|
sendMB('email-verification-click', {
|
||||||
button: 'verify',
|
button: 'verify',
|
||||||
flow,
|
flow,
|
||||||
})
|
})
|
||||||
|
try {
|
||||||
|
const data = await postJSON(confirmationEndpoint, {
|
||||||
|
body: { code: confirmationCode },
|
||||||
|
})
|
||||||
|
if (onSuccessfulConfirmation) {
|
||||||
|
onSuccessfulConfirmation()
|
||||||
|
} else {
|
||||||
|
setSuccessRedirectPath(data?.redir || '/')
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
errorHandler(err, 'confirm')
|
||||||
|
} finally {
|
||||||
|
setIsConfirming(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resendHandler = (e: FormEvent<Button>) => {
|
const resendHandler: MouseEventHandler<HTMLButtonElement> = () => {
|
||||||
setIsResending(true)
|
setIsResending(true)
|
||||||
setFeedback(null)
|
setFeedback(null)
|
||||||
|
|
||||||
@@ -138,14 +143,10 @@ export function ConfirmEmailForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isReady) {
|
if (!isReady) {
|
||||||
return (
|
return <LoadingSpinner />
|
||||||
<Interstitial className="confirm-email" showLogo>
|
|
||||||
<LoadingSpinner />
|
|
||||||
</Interstitial>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (successRedirectPath) {
|
if (successRedirectPath && successButtonText && successMessage) {
|
||||||
return (
|
return (
|
||||||
<ConfirmEmailSuccessfullForm
|
<ConfirmEmailSuccessfullForm
|
||||||
successMessage={successMessage}
|
successMessage={successMessage}
|
||||||
@@ -156,8 +157,12 @@ export function ConfirmEmailForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Interstitial className="confirm-email" showLogo>
|
<form
|
||||||
<form onSubmit={submitHandler} onInvalid={invalidFormHandler}>
|
onSubmit={submitHandler}
|
||||||
|
onInvalid={invalidFormHandler}
|
||||||
|
className="confirm-email-form"
|
||||||
|
>
|
||||||
|
<div className="confirm-email-form-inner">
|
||||||
{feedback?.type === 'alert' && (
|
{feedback?.type === 'alert' && (
|
||||||
<Notification
|
<Notification
|
||||||
ariaLive="polite"
|
ariaLive="polite"
|
||||||
@@ -167,10 +172,17 @@ export function ConfirmEmailForm({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<h1 className="h3 interstitial-header">{t('confirm_your_email')}</h1>
|
{interstitial ? (
|
||||||
|
<h1 className="h3 interstitial-header">{t('confirm_your_email')}</h1>
|
||||||
|
) : (
|
||||||
|
<h5 className="h5">{t('confirm_your_email')}</h5>
|
||||||
|
)}
|
||||||
|
|
||||||
<p className="small">{t('enter_the_confirmation_code', { email })}</p>
|
<OLFormLabel htmlFor="one-time-code">
|
||||||
|
{t('enter_the_confirmation_code', { email })}
|
||||||
|
</OLFormLabel>
|
||||||
<input
|
<input
|
||||||
|
id="one-time-code"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={t('enter_6_digit_code')}
|
placeholder={t('enter_6_digit_code')}
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
@@ -194,39 +206,53 @@ export function ConfirmEmailForm({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="form-actions">
|
<div className="form-actions">
|
||||||
<Button
|
<OLButton
|
||||||
disabled={isConfirming || isResending}
|
disabled={isResending}
|
||||||
type="submit"
|
type="submit"
|
||||||
bsStyle={null}
|
isLoading={isConfirming}
|
||||||
className="btn-primary"
|
bs3Props={{
|
||||||
|
loading: isConfirming ? (
|
||||||
|
<>
|
||||||
|
{t('confirming')}
|
||||||
|
<span>…</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('confirm')
|
||||||
|
),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{isConfirming ? (
|
{t('confirm')}
|
||||||
<>
|
</OLButton>
|
||||||
{t('confirming')}
|
<OLButton
|
||||||
<span>…</span>
|
variant="secondary"
|
||||||
</>
|
disabled={isConfirming}
|
||||||
) : (
|
|
||||||
t('confirm')
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
disabled={isConfirming || isResending}
|
|
||||||
onClick={resendHandler}
|
onClick={resendHandler}
|
||||||
bsStyle={null}
|
isLoading={isResending}
|
||||||
className="btn-secondary"
|
bs3Props={{
|
||||||
|
loading: isResending ? (
|
||||||
|
<>
|
||||||
|
{t('resending_confirmation_code')}
|
||||||
|
<span>…</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('resend_confirmation_code')
|
||||||
|
),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{isResending ? (
|
{t('resend_confirmation_code')}
|
||||||
<>
|
</OLButton>
|
||||||
{t('resending_confirmation_code')}
|
{onCancel && (
|
||||||
<span>…</span>
|
<OLButton
|
||||||
</>
|
variant="danger-ghost"
|
||||||
) : (
|
disabled={isConfirming || isResending}
|
||||||
t('resend_confirmation_code')
|
onClick={onCancel}
|
||||||
)}
|
>
|
||||||
</Button>
|
{t('cancel')}
|
||||||
|
</OLButton>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
</Interstitial>
|
</form>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,17 +271,15 @@ function ConfirmEmailSuccessfullForm({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Interstitial className="confirm-email" showLogo>
|
<form onSubmit={submitHandler}>
|
||||||
<form onSubmit={submitHandler}>
|
<div aria-live="polite">{successMessage}</div>
|
||||||
<div aria-live="polite">{successMessage}</div>
|
|
||||||
|
|
||||||
<div className="form-actions">
|
<div className="form-actions">
|
||||||
<Button type="submit" bsStyle={null} className="btn-primary">
|
<OLButton type="submit" variant="primary">
|
||||||
{successButtonText}
|
{successButtonText}
|
||||||
</Button>
|
</OLButton>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</Interstitial>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
+12
-8
@@ -1,5 +1,6 @@
|
|||||||
import { ConfirmEmailForm } from './confirm-email'
|
import { ConfirmEmailForm } from './confirm-email-form'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Interstitial } from '@/shared/components/interstitial'
|
||||||
|
|
||||||
export default function ConfirmSecondaryEmailForm() {
|
export default function ConfirmSecondaryEmailForm() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
@@ -13,12 +14,15 @@ export default function ConfirmSecondaryEmailForm() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfirmEmailForm
|
<Interstitial className="confirm-email" showLogo>
|
||||||
successMessage={successMessage}
|
<ConfirmEmailForm
|
||||||
successButtonText={t('go_to_overleaf')}
|
successMessage={successMessage}
|
||||||
confirmationEndpoint="/user/emails/confirm-secondary"
|
successButtonText={t('go_to_overleaf')}
|
||||||
resendEndpoint="/user/emails/resend-secondary-confirmation"
|
confirmationEndpoint="/user/emails/confirm-secondary"
|
||||||
flow="secondary"
|
resendEndpoint="/user/emails/resend-secondary-confirmation"
|
||||||
/>
|
flow="secondary"
|
||||||
|
interstitial
|
||||||
|
/>
|
||||||
|
</Interstitial>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,10 @@
|
|||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.confirm-email-alert {
|
.confirm-email-alert {
|
||||||
|
|||||||
@@ -203,3 +203,40 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#settings-page-root {
|
||||||
|
.confirm-email-form {
|
||||||
|
background: var(--bg-light-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-email-form-inner {
|
||||||
|
margin: auto;
|
||||||
|
padding: var(--spacing-08);
|
||||||
|
max-width: 480px;
|
||||||
|
|
||||||
|
label {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-03);
|
||||||
|
padding: var(--spacing-02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
margin-top: var(--spacing-05);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-05);
|
||||||
|
|
||||||
|
button {
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger-ghost:not(:hover) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+33
-7
@@ -60,6 +60,19 @@ function resetFetchMock() {
|
|||||||
fetchMock.get('express:/institutions/domains', [])
|
fetchMock.get('express:/institutions/domains', [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function confirmCodeForEmail(email: string) {
|
||||||
|
screen.getByText(`Enter the 6-digit confirmation code sent to ${email}.`)
|
||||||
|
const inputCode = screen.getByLabelText(/6-digit confirmation code/i)
|
||||||
|
fireEvent.change(inputCode, { target: { value: '123456' } })
|
||||||
|
const submitCodeBtn = screen.getByRole<HTMLButtonElement>('button', {
|
||||||
|
name: 'Confirm',
|
||||||
|
})
|
||||||
|
fireEvent.click(submitCodeBtn)
|
||||||
|
await waitForElementToBeRemoved(() =>
|
||||||
|
screen.getByRole('button', { name: /confirming/i })
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
describe('<EmailsSection />', function () {
|
describe('<EmailsSection />', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Object.assign(getMeta('ol-ExposedSettings'), {
|
Object.assign(getMeta('ol-ExposedSettings'), {
|
||||||
@@ -181,7 +194,8 @@ describe('<EmailsSection />', function () {
|
|||||||
resetFetchMock()
|
resetFetchMock()
|
||||||
fetchMock
|
fetchMock
|
||||||
.get('/user/emails?ensureAffiliation=true', [userEmailData])
|
.get('/user/emails?ensureAffiliation=true', [userEmailData])
|
||||||
.post('/user/emails', 200)
|
.post('/user/emails/secondary', 200)
|
||||||
|
.post('/user/emails/confirm-secondary', 200)
|
||||||
|
|
||||||
fireEvent.click(addAnotherEmailBtn)
|
fireEvent.click(addAnotherEmailBtn)
|
||||||
const input = screen.getByLabelText(/email/i)
|
const input = screen.getByLabelText(/email/i)
|
||||||
@@ -206,6 +220,7 @@ describe('<EmailsSection />', function () {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await confirmCodeForEmail(userEmailData.email)
|
||||||
screen.getByText(userEmailData.email)
|
screen.getByText(userEmailData.email)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -222,7 +237,7 @@ describe('<EmailsSection />', function () {
|
|||||||
resetFetchMock()
|
resetFetchMock()
|
||||||
fetchMock
|
fetchMock
|
||||||
.get('/user/emails?ensureAffiliation=true', [])
|
.get('/user/emails?ensureAffiliation=true', [])
|
||||||
.post('/user/emails', 400)
|
.post('/user/emails/secondary', 400)
|
||||||
|
|
||||||
fireEvent.click(addAnotherEmailBtn)
|
fireEvent.click(addAnotherEmailBtn)
|
||||||
const input = screen.getByLabelText(/email/i)
|
const input = screen.getByLabelText(/email/i)
|
||||||
@@ -340,7 +355,8 @@ describe('<EmailsSection />', function () {
|
|||||||
|
|
||||||
fetchMock
|
fetchMock
|
||||||
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
|
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
|
||||||
.post(/\/user\/emails/, 200)
|
.post('/user/emails/secondary', 200)
|
||||||
|
.post('/user/emails/confirm-secondary', 200)
|
||||||
|
|
||||||
await userEvent.click(
|
await userEvent.click(
|
||||||
screen.getByRole('button', {
|
screen.getByRole('button', {
|
||||||
@@ -359,8 +375,12 @@ describe('<EmailsSection />', function () {
|
|||||||
department: customDepartment,
|
department: customDepartment,
|
||||||
})
|
})
|
||||||
|
|
||||||
screen.getByText(userEmailData.email)
|
screen.getByText(
|
||||||
screen.getByText(userEmailData.affiliation.institution.name)
|
`Enter the 6-digit confirmation code sent to ${userEmailData.email}.`
|
||||||
|
)
|
||||||
|
|
||||||
|
await confirmCodeForEmail(userEmailData.email)
|
||||||
|
|
||||||
screen.getByText(userEmailData.affiliation.role!, { exact: false })
|
screen.getByText(userEmailData.affiliation.role!, { exact: false })
|
||||||
screen.getByText(customDepartment, { exact: false })
|
screen.getByText(customDepartment, { exact: false })
|
||||||
})
|
})
|
||||||
@@ -485,7 +505,8 @@ describe('<EmailsSection />', function () {
|
|||||||
|
|
||||||
fetchMock
|
fetchMock
|
||||||
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
|
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
|
||||||
.post(/\/user\/emails/, 200)
|
.post('/user/emails/secondary', 200)
|
||||||
|
.post('/user/emails/confirm-secondary', 200)
|
||||||
|
|
||||||
await userEvent.click(
|
await userEvent.click(
|
||||||
screen.getByRole('button', {
|
screen.getByRole('button', {
|
||||||
@@ -493,6 +514,8 @@ describe('<EmailsSection />', function () {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await confirmCodeForEmail(userEmailData.email)
|
||||||
|
|
||||||
const [[, request]] = fetchMock.calls(/\/user\/emails/)
|
const [[, request]] = fetchMock.calls(/\/user\/emails/)
|
||||||
|
|
||||||
expect(JSON.parse(request?.body?.toString() || '{}')).to.deep.include({
|
expect(JSON.parse(request?.body?.toString() || '{}')).to.deep.include({
|
||||||
@@ -635,7 +658,8 @@ describe('<EmailsSection />', function () {
|
|||||||
|
|
||||||
fetchMock
|
fetchMock
|
||||||
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
|
.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
|
||||||
.post('/user/emails', 200)
|
.post('/user/emails/secondary', 200)
|
||||||
|
.post('/user/emails/confirm-secondary', 200)
|
||||||
|
|
||||||
await userEvent.type(
|
await userEvent.type(
|
||||||
screen.getByRole('textbox', { name: /role/i }),
|
screen.getByRole('textbox', { name: /role/i }),
|
||||||
@@ -651,6 +675,8 @@ describe('<EmailsSection />', function () {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
await confirmCodeForEmail('user@autocomplete.edu')
|
||||||
|
|
||||||
await fetchMock.flush(true)
|
await fetchMock.flush(true)
|
||||||
fetchMock.reset()
|
fetchMock.reset()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user