mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 14:13:43 +01:00
Show dashboard notification for unconfirmed emails and untrusted secondary emails (#23919)
* Show an aggressive dashboard notification for unconfirmed emails Show a persistent dashboard notification for untrusted secondary emails * For emails before the cutoffDate, start displaying the notification on the deletionDate and show the notification for 90 days * Update the email deletion logic for displaying the email notification and update test * Update test GitOrigin-RevId: 1b0e44f79592292d428c634dc1ec4df9e6ceaeb4
This commit is contained in:
+133
-14
@@ -12,7 +12,9 @@ import { merge, cloneDeep } from 'lodash'
|
||||
import {
|
||||
professionalUserData,
|
||||
unconfirmedUserData,
|
||||
untrustedUserData,
|
||||
unconfirmedCommonsUserData,
|
||||
confirmedUserData,
|
||||
} from '../../settings/fixtures/test-user-email-data'
|
||||
import {
|
||||
notificationDropboxDuplicateProjectNames,
|
||||
@@ -24,7 +26,9 @@ import {
|
||||
} from '../fixtures/notifications-data'
|
||||
import Common from '../../../../../frontend/js/features/project-list/components/notifications/groups/common'
|
||||
import Institution from '../../../../../frontend/js/features/project-list/components/notifications/groups/institution'
|
||||
import ConfirmEmail from '../../../../../frontend/js/features/project-list/components/notifications/groups/confirm-email'
|
||||
import ConfirmEmail, {
|
||||
getEmailDeletionDate,
|
||||
} from '../../../../../frontend/js/features/project-list/components/notifications/groups/confirm-email'
|
||||
import ReconfirmationInfo from '../../../../../frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirmation-info'
|
||||
import { ProjectListProvider } from '../../../../../frontend/js/features/project-list/context/project-list-context'
|
||||
import { SplitTestProvider } from '@/shared/context/split-test-context'
|
||||
@@ -597,40 +601,142 @@ describe('<UserNotifications />', function () {
|
||||
})
|
||||
})
|
||||
|
||||
describe('getEmailDeletionDate', function () {
|
||||
beforeEach(async function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', [
|
||||
confirmedUserData,
|
||||
untrustedUserData,
|
||||
])
|
||||
this.clock = sinon.useFakeTimers(new Date('2025-07-01').getTime())
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
this.clock.restore()
|
||||
})
|
||||
|
||||
it('returns deletion date for unconfirmed email within notification window', function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', [unconfirmedUserData])
|
||||
const signUpDate = '2022-01-01' // Before cutoff '2025-03-03'
|
||||
const emailDeletionDate = getEmailDeletionDate(
|
||||
unconfirmedUserData,
|
||||
signUpDate
|
||||
)
|
||||
expect(emailDeletionDate).to.equal(
|
||||
new Date('2025-09-03').toLocaleDateString()
|
||||
)
|
||||
})
|
||||
|
||||
it('returns false for primary email', function () {
|
||||
const primaryUserData = { ...unconfirmedUserData, default: true }
|
||||
const signUpDate = '2022-01-01'
|
||||
const emailDeletionDate = getEmailDeletionDate(
|
||||
primaryUserData,
|
||||
signUpDate
|
||||
)
|
||||
expect(emailDeletionDate).to.be.false
|
||||
})
|
||||
|
||||
it('returns false for already confirmed email', function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', [confirmedUserData])
|
||||
const signUpDate = '2022-01-01'
|
||||
const emailDeletionDate = getEmailDeletionDate(
|
||||
confirmedUserData,
|
||||
signUpDate
|
||||
)
|
||||
expect(emailDeletionDate).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('<ConfirmEmail/>', function () {
|
||||
beforeEach(async function () {
|
||||
Object.assign(getMeta('ol-ExposedSettings'), {
|
||||
emailConfirmationDisabled: false,
|
||||
})
|
||||
window.metaAttributesCache.set('ol-userEmails', [unconfirmedUserData])
|
||||
window.metaAttributesCache.set('ol-userEmails', [
|
||||
confirmedUserData,
|
||||
untrustedUserData,
|
||||
])
|
||||
window.metaAttributesCache.set(
|
||||
'ol-usersBestSubscription',
|
||||
freeSubscription
|
||||
)
|
||||
window.metaAttributesCache.set('ol-user', {
|
||||
signUpDate: new Date('2024-01-01').toISOString(),
|
||||
})
|
||||
this.clock = sinon.useFakeTimers(new Date('2025-07-01').getTime())
|
||||
})
|
||||
|
||||
afterEach(function () {
|
||||
fetchMock.reset()
|
||||
this.clock.restore()
|
||||
})
|
||||
|
||||
it('sends successfully', async function () {
|
||||
function testUnconfirmedNotification(
|
||||
userEmails: any[],
|
||||
isPrimary: boolean
|
||||
) {
|
||||
it(`sends unconfirmed notification email successfully when email is ${isPrimary ? 'primary' : 'secondary'}`, async function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', userEmails)
|
||||
|
||||
renderWithinProjectListProvider(ConfirmEmail)
|
||||
await fetchMock.flush(true)
|
||||
fetchMock.post('/user/emails/resend_confirmation', 200)
|
||||
|
||||
const email = userEmails[0].email
|
||||
const notificationBody = screen.getByTestId('pro-notification-body')
|
||||
|
||||
if (isPrimary) {
|
||||
expect(notificationBody.textContent).to.contain(
|
||||
`Please confirm your primary email address ${email} by clicking on the link in the confirmation email.`
|
||||
)
|
||||
} else {
|
||||
expect(notificationBody.textContent).to.contain(
|
||||
`Please confirm your secondary email address ${email} by clicking on the link in the confirmation email.`
|
||||
)
|
||||
}
|
||||
|
||||
const resendButton = screen.getByRole('button', { name: /resend/i })
|
||||
fireEvent.click(resendButton)
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByRole('button', { name: /resend/i })
|
||||
)
|
||||
|
||||
expect(fetchMock.called()).to.be.true
|
||||
expect(screen.queryByRole('alert')).to.be.null
|
||||
})
|
||||
}
|
||||
|
||||
testUnconfirmedNotification(
|
||||
[{ email: 'baz@overleaf.com', default: true }],
|
||||
true
|
||||
)
|
||||
|
||||
testUnconfirmedNotification(
|
||||
[{ email: 'baz@overleaf.com', default: false }],
|
||||
false
|
||||
)
|
||||
|
||||
it('sends untrusted notification email successfully', async function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', [untrustedUserData])
|
||||
|
||||
renderWithinProjectListProvider(ConfirmEmail)
|
||||
await fetchMock.flush(true)
|
||||
fetchMock.post('/user/emails/resend_confirmation', 200)
|
||||
|
||||
const email = unconfirmedUserData.email
|
||||
const notificationBody = screen.getByTestId('pro-notification-body')
|
||||
const email = untrustedUserData.email
|
||||
const notificationBody = screen.getByTestId(
|
||||
'not-trusted-notification-body'
|
||||
)
|
||||
expect(notificationBody.textContent).to.contain(
|
||||
`Please confirm your email ${email} by clicking on the link in the confirmation email`
|
||||
`To enhance the security of your Overleaf account, please reconfirm your secondary email address ${email}.`
|
||||
)
|
||||
|
||||
const resendButton = screen.getByRole('button', { name: /resend/i })
|
||||
fireEvent.click(resendButton)
|
||||
|
||||
expect(screen.queryByRole('button', { name: /resend/i })).to.be.null
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByText(/resending confirmation email/i)
|
||||
screen.getByRole('button', { name: /resend/i })
|
||||
)
|
||||
|
||||
expect(fetchMock.called()).to.be.true
|
||||
@@ -638,15 +744,21 @@ describe('<UserNotifications />', function () {
|
||||
})
|
||||
|
||||
it('fails to send', async function () {
|
||||
window.metaAttributesCache.set('ol-userEmails', [unconfirmedUserData])
|
||||
|
||||
renderWithinProjectListProvider(ConfirmEmail)
|
||||
await fetchMock.flush(true)
|
||||
fetchMock.post('/user/emails/resend_confirmation', 500)
|
||||
|
||||
const resendButton = screen.getByRole('button', { name: /resend/i })
|
||||
const resendButtons = screen.getAllByRole('button', { name: /resend/i })
|
||||
const resendButton = resendButtons[0]
|
||||
fireEvent.click(resendButton)
|
||||
const notificationBody = screen.getByTestId('pro-notification-body')
|
||||
|
||||
await waitForElementToBeRemoved(() =>
|
||||
screen.getByText(/resending confirmation email/i)
|
||||
within(notificationBody).getByTestId(
|
||||
'loading-resending-confirmation-email'
|
||||
)
|
||||
)
|
||||
|
||||
expect(fetchMock.called()).to.be.true
|
||||
@@ -689,9 +801,16 @@ describe('<UserNotifications />', function () {
|
||||
const notificationBody = within(alert).getByTestId(
|
||||
'pro-notification-body'
|
||||
)
|
||||
expect(notificationBody.textContent).to.contain(
|
||||
`Please confirm your email ${email} by clicking on the link in the confirmation email`
|
||||
)
|
||||
const isPrimary = unconfirmedCommonsUserData.default
|
||||
if (isPrimary) {
|
||||
expect(notificationBody.textContent).to.contain(
|
||||
`Please confirm your primary email address ${email} by clicking on the link in the confirmation email`
|
||||
)
|
||||
} else {
|
||||
expect(notificationBody.textContent).to.contain(
|
||||
`Please confirm your secondary email address ${email} by clicking on the link in the confirmation email`
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
+1
-9
@@ -92,7 +92,7 @@ describe('<ProjectListRoot />', function () {
|
||||
it('the email confirmation alert is not displayed', async function () {
|
||||
expect(
|
||||
screen.queryByText(
|
||||
'Please confirm your email test@overleaf.com by clicking on the link in the confirmation email'
|
||||
'Please confirm your primary email address test@overleaf.com by clicking on the link in the confirmation email.'
|
||||
)
|
||||
).to.be.null
|
||||
})
|
||||
@@ -1141,13 +1141,5 @@ describe('<ProjectListRoot />', function () {
|
||||
await screen.findByText(copiedProjectName)
|
||||
})
|
||||
})
|
||||
|
||||
describe('notifications', function () {
|
||||
it('email confirmation alert is displayed', async function () {
|
||||
screen.getByText(
|
||||
'Please confirm your email test@overleaf.com by clicking on the link in the confirmation email'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user