mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 02:51:57 +02:00
Merge pull request #8178 from overleaf/ta-settings-reconfirmation
[SettingsPage] Add Reconfirmation Prompt GitOrigin-RevId: 2e03ba7f459b32faf6d66f21ba692ca54f62a320
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
"also": "",
|
||||
"anyone_with_link_can_edit": "",
|
||||
"anyone_with_link_can_view": "",
|
||||
"are_you_still_at": "",
|
||||
"ask_proj_owner_to_upgrade_for_git_bridge": "",
|
||||
"ask_proj_owner_to_upgrade_for_longer_compiles": "",
|
||||
"ask_proj_owner_to_upgrade_for_references_search": "",
|
||||
@@ -62,6 +63,7 @@
|
||||
"compile_mode": "",
|
||||
"compile_terminated_by_user": "",
|
||||
"compiling": "",
|
||||
"confirm_affiliation": "",
|
||||
"confirm_new_password": "",
|
||||
"conflicting_paths_found": "",
|
||||
"connected_users": "",
|
||||
@@ -100,14 +102,15 @@
|
||||
"dropbox_sync": "",
|
||||
"dropbox_sync_both": "",
|
||||
"dropbox_sync_description": "",
|
||||
"dropbox_sync_error": "",
|
||||
"dropbox_sync_in": "",
|
||||
"dropbox_sync_out": "",
|
||||
"dropbox_synced": "",
|
||||
"duplicate_file": "",
|
||||
"easily_manage_your_project_files_everywhere": "",
|
||||
"edit_dictionary": "",
|
||||
"edit_dictionary_empty": "",
|
||||
"edit_dictionary_remove": "",
|
||||
"edit_dictionary": "",
|
||||
"editing": "",
|
||||
"editor_and_pdf": "",
|
||||
"editor_only_hide_pdf": "",
|
||||
@@ -273,6 +276,7 @@
|
||||
"n_items_plural": "",
|
||||
"navigate_log_source": "",
|
||||
"navigation": "",
|
||||
"need_to_add_new_primary_before_remove": "",
|
||||
"need_to_leave": "",
|
||||
"need_to_upgrade_for_more_collabs": "",
|
||||
"need_to_upgrade_for_more_collabs_variant": "",
|
||||
@@ -320,9 +324,11 @@
|
||||
"pdf_viewer_error": "",
|
||||
"please_change_primary_to_remove": "",
|
||||
"please_check_your_inbox": "",
|
||||
"please_check_your_inbox_to_confirm": "",
|
||||
"please_compile_pdf_before_download": "",
|
||||
"please_confirm_your_email_before_making_it_default": "",
|
||||
"please_link_before_making_primary": "",
|
||||
"please_reconfirm_institutional_email": "",
|
||||
"please_reconfirm_your_affiliation_before_making_this_primary": "",
|
||||
"please_refresh": "",
|
||||
"please_select_a_file": "",
|
||||
@@ -442,6 +448,7 @@
|
||||
"take_short_survey": "",
|
||||
"template_approved_by_publisher": "",
|
||||
"terminated": "",
|
||||
"thank_you_exclamation": "",
|
||||
"thanks_settings_updated": "",
|
||||
"this_grants_access_to_features_2": "",
|
||||
"this_project_is_public": "",
|
||||
@@ -498,6 +505,7 @@
|
||||
"word_count": "",
|
||||
"work_offline": "",
|
||||
"work_with_non_overleaf_users": "",
|
||||
"your_affiliation_is_confirmed": "",
|
||||
"your_browser_does_not_support_this_feature": "",
|
||||
"your_message": "",
|
||||
"zotero_groups_loading_error": "",
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { UserEmailData } from '../../../../../../types/user-email'
|
||||
import { Row, Col } from 'react-bootstrap'
|
||||
import classNames from 'classnames'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import ReconfirmationInfoSuccess from './reconfirmation-info/reconfirmation-info-success'
|
||||
import ReconfirmationInfoPrompt from './reconfirmation-info/reconfirmation-info-prompt'
|
||||
|
||||
type ReconfirmationInfoProps = {
|
||||
userEmailData: UserEmailData
|
||||
}
|
||||
|
||||
function ReconfirmationInfo({ userEmailData }: ReconfirmationInfoProps) {
|
||||
const reconfirmationRemoveEmail = getMeta(
|
||||
'ol-reconfirmationRemoveEmail'
|
||||
) as string
|
||||
const reconfirmedViaSAML = getMeta('ol-reconfirmedViaSAML') as string
|
||||
|
||||
if (!userEmailData.affiliation) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (
|
||||
userEmailData.samlProviderId &&
|
||||
userEmailData.samlProviderId === reconfirmedViaSAML
|
||||
) {
|
||||
return (
|
||||
<ReconfirmationInfoContentWrapper asAlertInfo>
|
||||
<ReconfirmationInfoSuccess
|
||||
institution={userEmailData.affiliation.institution}
|
||||
/>
|
||||
</ReconfirmationInfoContentWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
if (userEmailData.affiliation.inReconfirmNotificationPeriod) {
|
||||
return (
|
||||
<ReconfirmationInfoContentWrapper
|
||||
asAlertInfo={reconfirmationRemoveEmail === userEmailData.email}
|
||||
>
|
||||
<ReconfirmationInfoPrompt
|
||||
institution={userEmailData.affiliation.institution}
|
||||
primary={userEmailData.default}
|
||||
email={userEmailData.email}
|
||||
/>
|
||||
</ReconfirmationInfoContentWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
type ReconfirmationInfoContentWrapperProps = {
|
||||
asAlertInfo: boolean
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
function ReconfirmationInfoContentWrapper({
|
||||
asAlertInfo,
|
||||
children,
|
||||
}: ReconfirmationInfoContentWrapperProps) {
|
||||
return (
|
||||
<Row>
|
||||
<Col md={12}>
|
||||
<div
|
||||
className={classNames('settings-reconfirm-info', 'small', {
|
||||
'alert alert-info': asAlertInfo,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReconfirmationInfo
|
||||
@@ -0,0 +1,165 @@
|
||||
import { useState, useEffect, useLayoutEffect } from 'react'
|
||||
import useAsync from '../../../../../shared/hooks/use-async'
|
||||
import { postJSON } from '../../../../../infrastructure/fetch-json'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Institution } from '../../../../../../../types/institution'
|
||||
import { Button } from 'react-bootstrap'
|
||||
import { useUserEmailsContext } from '../../../context/user-email-context'
|
||||
import getMeta from '../../../../../utils/meta'
|
||||
import { ExposedSettings } from '../../../../../../../types/exposed-settings'
|
||||
import { ssoAvailableForInstitution } from '../../../utils/sso'
|
||||
import Icon from '../../../../../shared/components/icon'
|
||||
|
||||
type ReconfirmationInfoPromptProps = {
|
||||
email: string
|
||||
primary: boolean
|
||||
institution: Institution
|
||||
}
|
||||
|
||||
function ReconfirmationInfoPrompt({
|
||||
email,
|
||||
primary,
|
||||
institution,
|
||||
}: ReconfirmationInfoPromptProps) {
|
||||
const { t } = useTranslation()
|
||||
const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings
|
||||
const { isLoading, isError, isSuccess, runAsync } = useAsync()
|
||||
const { state, setLoading: setUserEmailsContextLoading } =
|
||||
useUserEmailsContext()
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
const [hasSent, setHasSent] = useState(false)
|
||||
const ssoAvailable = Boolean(ssoAvailableForInstitution(institution))
|
||||
|
||||
useEffect(() => {
|
||||
setUserEmailsContextLoading(isLoading)
|
||||
}, [setUserEmailsContextLoading, isLoading])
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (isSuccess) {
|
||||
setHasSent(true)
|
||||
}
|
||||
}, [isSuccess])
|
||||
|
||||
const handleRequestReconfirmation = () => {
|
||||
if (ssoAvailable) {
|
||||
setIsPending(true)
|
||||
window.location.assign(
|
||||
`${samlInitPath}?university_id=${institution.id}&reconfirm=/user/settings`
|
||||
)
|
||||
} else {
|
||||
runAsync(
|
||||
postJSON('/user/emails/resend_confirmation', {
|
||||
body: {
|
||||
email,
|
||||
},
|
||||
})
|
||||
).catch(console.error)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSent) {
|
||||
return (
|
||||
<div>
|
||||
<Trans
|
||||
i18nKey="please_check_your_inbox_to_confirm"
|
||||
values={{
|
||||
institutionName: institution.name,
|
||||
}}
|
||||
components={
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
[<strong />]
|
||||
}
|
||||
/>{' '}
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Icon type="refresh" spin fw /> {t('sending')}...
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
className="btn-inline-link"
|
||||
disabled={state.isLoading}
|
||||
onClick={handleRequestReconfirmation}
|
||||
>
|
||||
{t('resend_confirmation_email')}
|
||||
</Button>
|
||||
)}
|
||||
<br />
|
||||
{isError && (
|
||||
<div className="text-danger">{t('generic_something_went_wrong')}</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<ReconfirmationInfoPromptText
|
||||
institutionName={institution.name}
|
||||
primary={primary}
|
||||
/>
|
||||
</div>
|
||||
<div className="setting-reconfirm-info-right">
|
||||
<Button
|
||||
bsStyle="info"
|
||||
disabled={state.isLoading || isPending}
|
||||
onClick={handleRequestReconfirmation}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<Icon type="refresh" spin fw /> {t('sending')}...
|
||||
</>
|
||||
) : (
|
||||
t('confirm_affiliation')
|
||||
)}
|
||||
</Button>
|
||||
<br />
|
||||
{isError && (
|
||||
<div className="text-danger">{t('generic_something_went_wrong')}</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type ReconfirmationInfoPromptTextProps = {
|
||||
primary: boolean
|
||||
institutionName: Institution['name']
|
||||
}
|
||||
|
||||
function ReconfirmationInfoPromptText({
|
||||
primary,
|
||||
institutionName,
|
||||
}: ReconfirmationInfoPromptTextProps) {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Icon type="warning" className="me-1 icon-warning" />
|
||||
<Trans
|
||||
i18nKey="are_you_still_at"
|
||||
values={{
|
||||
institutionName,
|
||||
}}
|
||||
components={
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
[<strong />]
|
||||
}
|
||||
/>{' '}
|
||||
<Trans
|
||||
i18nKey="please_reconfirm_institutional_email"
|
||||
components={
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
[<span />]
|
||||
}
|
||||
/>{' '}
|
||||
<a href="/learn/how-to/Institutional_Email_Reconfirmation">
|
||||
{t('learn_more')}
|
||||
</a>
|
||||
<br />
|
||||
{primary ? <i>{t('need_to_add_new_primary_before_remove')}</i> : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReconfirmationInfoPrompt
|
||||
@@ -0,0 +1,29 @@
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Institution } from '../../../../../../../types/institution'
|
||||
|
||||
type ReconfirmationInfoSuccessProps = {
|
||||
institution: Institution
|
||||
}
|
||||
|
||||
function ReconfirmationInfoSuccess({
|
||||
institution,
|
||||
}: ReconfirmationInfoSuccessProps) {
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<div>
|
||||
<Trans
|
||||
i18nKey="your_affiliation_is_confirmed"
|
||||
values={{
|
||||
institutionName: institution.name,
|
||||
}}
|
||||
components={
|
||||
/* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
||||
[<strong />]
|
||||
}
|
||||
/>{' '}
|
||||
{t('thank_you_exclamation')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReconfirmationInfoSuccess
|
||||
@@ -11,6 +11,7 @@ import { useUserEmailsContext } from '../../context/user-email-context'
|
||||
import getMeta from '../../../../utils/meta'
|
||||
import { ExposedSettings } from '../../../../../../types/exposed-settings'
|
||||
import { ssoAvailableForInstitution } from '../../utils/sso'
|
||||
import ReconfirmationInfo from './reconfirmation-info'
|
||||
|
||||
type EmailsRowProps = {
|
||||
userEmailData: UserEmailData
|
||||
@@ -47,6 +48,7 @@ function EmailsRow({ userEmailData }: EmailsRowProps) {
|
||||
{hasSSOAffiliation && (
|
||||
<SSOAffiliationInfo userEmailData={userEmailData} />
|
||||
)}
|
||||
<ReconfirmationInfo userEmailData={userEmailData} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ import EmailsSection from '../../js/features/settings/components/emails-section'
|
||||
import useFetchMock from './../hooks/use-fetch-mock'
|
||||
import {
|
||||
setDefaultMeta,
|
||||
setReconfirmationMeta,
|
||||
defaultSetupMocks,
|
||||
reconfirmationSetupMocks,
|
||||
errorsMocks,
|
||||
} from './helpers/emails'
|
||||
|
||||
@@ -13,6 +15,13 @@ export const EmailsList = args => {
|
||||
return <EmailsSection {...args} />
|
||||
}
|
||||
|
||||
export const ReconfirmationEmailsList = args => {
|
||||
useFetchMock(reconfirmationSetupMocks)
|
||||
setReconfirmationMeta()
|
||||
|
||||
return <EmailsSection {...args} />
|
||||
}
|
||||
|
||||
export const NetworkErrors = args => {
|
||||
useFetchMock(errorsMocks)
|
||||
setDefaultMeta()
|
||||
|
||||
@@ -39,6 +39,71 @@ const fakeUsersData = [
|
||||
default: false,
|
||||
},
|
||||
]
|
||||
const fakeReconfirmationUsersData = [
|
||||
{
|
||||
affiliation: {
|
||||
institution: {
|
||||
confirmed: true,
|
||||
isUniversity: true,
|
||||
id: 4,
|
||||
name: 'Reconfirmable Email Highlighted',
|
||||
},
|
||||
licence: 'pro_plus',
|
||||
inReconfirmNotificationPeriod: true,
|
||||
},
|
||||
email: 'reconfirmation-highlighted@overleaf.com',
|
||||
confirmedAt: '2022-03-09T10:59:44.139Z',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
affiliation: {
|
||||
institution: {
|
||||
confirmed: true,
|
||||
isUniversity: true,
|
||||
id: 4,
|
||||
name: 'Reconfirmable Emails Primary',
|
||||
},
|
||||
licence: 'pro_plus',
|
||||
inReconfirmNotificationPeriod: true,
|
||||
},
|
||||
email: 'reconfirmation-nonsso@overleaf.com',
|
||||
confirmedAt: '2022-03-09T10:59:44.139Z',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
affiliation: {
|
||||
institution: {
|
||||
confirmed: true,
|
||||
ssoEnabled: true,
|
||||
isUniversity: true,
|
||||
id: 3,
|
||||
name: 'Reconfirmable SSO',
|
||||
},
|
||||
licence: 'pro_plus',
|
||||
inReconfirmNotificationPeriod: true,
|
||||
},
|
||||
email: 'reconfirmation-sso@overleaf.com',
|
||||
confirmedAt: '2022-03-09T10:59:44.139Z',
|
||||
samlProviderId: 'reconfirmation-sso-provider-id',
|
||||
default: false,
|
||||
},
|
||||
{
|
||||
affiliation: {
|
||||
institution: {
|
||||
confirmed: true,
|
||||
isUniversity: true,
|
||||
ssoEnabled: true,
|
||||
id: 5,
|
||||
name: 'Reconfirmed SSO',
|
||||
},
|
||||
licence: 'pro_plus',
|
||||
},
|
||||
confirmedAt: '2022-03-09T10:59:44.139Z',
|
||||
email: 'sso@overleaf.com',
|
||||
samlProviderId: 'sso-reconfirmed-provider-id',
|
||||
default: false,
|
||||
},
|
||||
]
|
||||
|
||||
const fakeInstitutions = [
|
||||
{
|
||||
@@ -103,6 +168,14 @@ export function defaultSetupMocks(fetchMock) {
|
||||
})
|
||||
}
|
||||
|
||||
export function reconfirmationSetupMocks(fetchMock) {
|
||||
defaultSetupMocks(fetchMock)
|
||||
fetchMock.get(/\/user\/emails/, fakeReconfirmationUsersData, {
|
||||
delay: MOCK_DELAY,
|
||||
overwriteRoutes: true,
|
||||
})
|
||||
}
|
||||
|
||||
export function errorsMocks(fetchMock) {
|
||||
fetchMock
|
||||
.get(/\/user\/emails/, fakeUsersData, { delay: MOCK_DELAY })
|
||||
@@ -122,3 +195,15 @@ export function setDefaultMeta() {
|
||||
(Date.now() - 1000 * 60 * 60).toString()
|
||||
)
|
||||
}
|
||||
|
||||
export function setReconfirmationMeta() {
|
||||
setDefaultMeta()
|
||||
window.metaAttributesCache.set(
|
||||
'ol-reconfirmationRemoveEmail',
|
||||
'reconfirmation-highlighted@overleaf.com'
|
||||
)
|
||||
window.metaAttributesCache.set(
|
||||
'ol-reconfirmedViaSAML',
|
||||
'sso-reconfirmed-provider-id'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -189,3 +189,25 @@ tbody > tr.affiliations-table-warning-row > td {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.settings-reconfirm-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0px auto @margin-sm auto !important;
|
||||
padding: @padding-md;
|
||||
&:not(.alert-info) {
|
||||
background-color: @ol-blue-gray-0;
|
||||
}
|
||||
|
||||
> *:not(:last-child) {
|
||||
margin-right: @margin-md;
|
||||
}
|
||||
|
||||
.fa-warning {
|
||||
color: @brand-warning;
|
||||
}
|
||||
}
|
||||
|
||||
.setting-reconfirm-info-right {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,14 @@
|
||||
.btn-inline-link {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
background: none;
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
.btn {
|
||||
.btn:not(.btn-inline-link) {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import {
|
||||
fireEvent,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react'
|
||||
import sinon from 'sinon'
|
||||
import { expect } from 'chai'
|
||||
import fetchMock from 'fetch-mock'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import ReconfirmationInfo from '../../../../../../frontend/js/features/settings/components/emails/reconfirmation-info'
|
||||
import { ssoUserData } from '../../fixtures/test-user-email-data'
|
||||
import { UserEmailData } from '../../../../../../types/user-email'
|
||||
import { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
|
||||
|
||||
function renderReconfirmationInfo(data: UserEmailData) {
|
||||
return render(
|
||||
<UserEmailsProvider>
|
||||
<ReconfirmationInfo userEmailData={data} />
|
||||
</UserEmailsProvider>
|
||||
)
|
||||
}
|
||||
|
||||
describe('<ReconfirmationInfo/>', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache = window.metaAttributesCache || new Map()
|
||||
fetchMock.get('/user/emails?ensureAffiliation=true', [])
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
fetchMock.reset()
|
||||
})
|
||||
|
||||
describe('reconfirmed via SAML', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set(
|
||||
'ol-reconfirmedViaSAML',
|
||||
'sso-prof-saml-id'
|
||||
)
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
window.metaAttributesCache = new Map()
|
||||
})
|
||||
|
||||
it('show reconfirmed confirmation', function () {
|
||||
renderReconfirmationInfo(ssoUserData)
|
||||
screen.getByText('SSO University')
|
||||
screen.getByText(/affiliation is confirmed/)
|
||||
screen.getByText(/Thank you!/)
|
||||
})
|
||||
})
|
||||
|
||||
describe('in reconfirm notification period', function () {
|
||||
let inReconfirmUserData: UserEmailData
|
||||
const locationStub = sinon.stub()
|
||||
const originalLocation = window.location
|
||||
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-ExposedSettings', {
|
||||
samlInitPath: '/saml',
|
||||
})
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
assign: locationStub,
|
||||
},
|
||||
})
|
||||
|
||||
inReconfirmUserData = cloneDeep(ssoUserData)
|
||||
if (inReconfirmUserData.affiliation) {
|
||||
inReconfirmUserData.affiliation.inReconfirmNotificationPeriod = true
|
||||
}
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
window.metaAttributesCache = new Map()
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
})
|
||||
})
|
||||
|
||||
it('renders prompt', function () {
|
||||
renderReconfirmationInfo(inReconfirmUserData)
|
||||
screen.getByText(/Are you still at/)
|
||||
screen.getByText('SSO University')
|
||||
screen.getByText(
|
||||
/Please take a moment to confirm your institutional email address/
|
||||
)
|
||||
screen.getByRole('link', { name: 'Learn more' })
|
||||
expect(screen.queryByText(/add a new primary email address/)).to.not.exist
|
||||
})
|
||||
|
||||
it('renders default emails prompt', function () {
|
||||
inReconfirmUserData.default = true
|
||||
renderReconfirmationInfo(inReconfirmUserData)
|
||||
screen.getByText(/add a new primary email address/)
|
||||
})
|
||||
|
||||
describe('SAML reconfirmations', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-ExposedSettings', {
|
||||
hasSamlFeature: true,
|
||||
samlInitPath: '/saml/init',
|
||||
})
|
||||
})
|
||||
|
||||
it('redirects to SAML flow', async function () {
|
||||
renderReconfirmationInfo(inReconfirmUserData)
|
||||
const confirmButton = screen.getByRole('button', {
|
||||
name: 'Confirm Affiliation',
|
||||
}) as HTMLButtonElement
|
||||
|
||||
await waitFor(() => {
|
||||
expect(confirmButton.disabled).to.be.false
|
||||
})
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(confirmButton.disabled).to.be.true
|
||||
})
|
||||
sinon.assert.calledOnce(locationStub)
|
||||
sinon.assert.calledWithMatch(
|
||||
locationStub,
|
||||
'/saml/init?university_id=2&reconfirm=/user/settings'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Email reconfirmations', function () {
|
||||
beforeEach(function () {
|
||||
window.metaAttributesCache.set('ol-ExposedSettings', {
|
||||
hasSamlFeature: false,
|
||||
})
|
||||
fetchMock.post('/user/emails/resend_confirmation', 200)
|
||||
})
|
||||
|
||||
it('sends and resends confirmation email', async function () {
|
||||
renderReconfirmationInfo(inReconfirmUserData)
|
||||
const confirmButton = screen.getByRole('button', {
|
||||
name: 'Confirm Affiliation',
|
||||
}) as HTMLButtonElement
|
||||
|
||||
await waitFor(() => {
|
||||
expect(confirmButton.disabled).to.be.false
|
||||
})
|
||||
fireEvent.click(confirmButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(confirmButton.disabled).to.be.true
|
||||
})
|
||||
expect(fetchMock.called()).to.be.true
|
||||
|
||||
// the confirmation text should now be displayed
|
||||
screen.getByText(/Please check your email inbox to confirm/)
|
||||
|
||||
// try the resend button
|
||||
fetchMock.resetHistory()
|
||||
const resendButton = screen.getByRole('button', {
|
||||
name: 'Resend confirmation email',
|
||||
}) as HTMLButtonElement
|
||||
|
||||
fireEvent.click(resendButton)
|
||||
|
||||
screen.getByText(/Sending/)
|
||||
expect(fetchMock.called()).to.be.true
|
||||
await waitForElementToBeRemoved(() => screen.getByText(/Sending/))
|
||||
screen.getByRole('button', {
|
||||
name: 'Resend confirmation email',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -44,6 +44,37 @@ export const professionalUserData: UserEmailData & {
|
||||
default: true,
|
||||
}
|
||||
|
||||
export const ssoUserData: UserEmailData = {
|
||||
affiliation: {
|
||||
cachedConfirmedAt: '2022-02-03T11:46:28.249Z',
|
||||
cachedEntitlement: null,
|
||||
cachedLastDayToReconfirm: null,
|
||||
cachedPastReconfirmDate: false,
|
||||
cachedReconfirmedAt: null,
|
||||
department: 'Art History',
|
||||
institution: {
|
||||
commonsAccount: true,
|
||||
confirmed: true,
|
||||
id: 2,
|
||||
isUniversity: true,
|
||||
maxConfirmationMonths: 12,
|
||||
name: 'SSO University',
|
||||
ssoEnabled: true,
|
||||
ssoBeta: false,
|
||||
},
|
||||
inReconfirmNotificationPeriod: false,
|
||||
inferred: false,
|
||||
licence: 'pro_plus',
|
||||
pastReconfirmDate: false,
|
||||
portal: { slug: '', templates_count: 0 },
|
||||
role: 'Prof',
|
||||
},
|
||||
confirmedAt: '2022-02-03T11:46:28.249Z',
|
||||
email: 'sso-prof@sso-university.edu',
|
||||
samlProviderId: 'sso-prof-saml-id',
|
||||
default: false,
|
||||
}
|
||||
|
||||
export const fakeUsersData = [
|
||||
{ ...confirmedUserData },
|
||||
{ ...unconfirmedUserData },
|
||||
|
||||
Reference in New Issue
Block a user