2022-05-24 03:48:06 -04:00
import { expect } from 'chai'
import { cloneDeep } from 'lodash'
import { renderHook } from '@testing-library/react-hooks'
import {
EmailContextType ,
UserEmailsProvider ,
useUserEmailsContext ,
} from '../../../../../frontend/js/features/settings/context/user-email-context'
import fetchMock from 'fetch-mock'
import {
confirmedUserData ,
professionalUserData ,
unconfirmedUserData ,
fakeUsersData ,
} from '../fixtures/test-user-email-data'
import localStorage from '../../../../../frontend/js/infrastructure/local-storage'
const renderUserEmailsContext = ( ) = >
renderHook ( ( ) = > useUserEmailsContext ( ) , {
wrapper : ( { children } ) = > (
< UserEmailsProvider > { children } < / UserEmailsProvider >
) ,
} )
describe ( 'UserEmailContext' , function ( ) {
beforeEach ( function ( ) {
fetchMock . reset ( )
} )
describe ( 'context bootstrap' , function ( ) {
it ( 'should start with an "in progress" initialisation state' , function ( ) {
const { result } = renderUserEmailsContext ( )
expect ( result . current . isInitializing ) . to . equal ( true )
expect ( result . current . isInitializingSuccess ) . to . equal ( false )
expect ( result . current . isInitializingError ) . to . equal ( false )
} )
it ( 'should start with an empty state' , function ( ) {
const { result } = renderUserEmailsContext ( )
expect ( result . current . state . data . byId ) . to . deep . equal ( { } )
expect ( result . current . state . data . emailAffiliationBeingEdited ) . to . be . null
expect ( result . current . state . data . linkedInstitutionIds ) . to . have . length ( 0 )
} )
it ( 'should load all user emails and update the initialisation state to "success"' , async function ( ) {
fetchMock . get ( /\/user\/emails/ , fakeUsersData )
const { result } = renderUserEmailsContext ( )
await fetchMock . flush ( true )
expect ( fetchMock . calls ( ) ) . to . have . lengthOf ( 1 )
expect ( result . current . state . data . byId ) . to . deep . equal ( {
'bar@overleaf.com' : confirmedUserData ,
'baz@overleaf.com' : unconfirmedUserData ,
'foo@overleaf.com' : professionalUserData ,
} )
2022-05-24 03:48:18 -04:00
expect ( result . current . state . data . linkedInstitutionIds ) . to . have . lengthOf ( 0 )
2022-05-24 03:48:06 -04:00
expect ( result . current . isInitializing ) . to . equal ( false )
expect ( result . current . isInitializingSuccess ) . to . equal ( true )
} )
it ( 'when loading user email fails, it should update the initialisation state to "failed"' , async function ( ) {
fetchMock . get ( /\/user\/emails/ , 500 )
const { result } = renderUserEmailsContext ( )
await fetchMock . flush ( )
expect ( result . current . isInitializing ) . to . equal ( false )
expect ( result . current . isInitializingError ) . to . equal ( true )
} )
describe ( 'state.isLoading' , function ( ) {
it ( 'should be `true` on bootstrap' , function ( ) {
const { result } = renderUserEmailsContext ( )
expect ( result . current . state . isLoading ) . to . equal ( true )
} )
it ( 'should be updated with `setLoading`' , function ( ) {
const { result } = renderUserEmailsContext ( )
result . current . setLoading ( true )
expect ( result . current . state . isLoading ) . to . equal ( true )
result . current . setLoading ( false )
expect ( result . current . state . isLoading ) . to . equal ( false )
} )
} )
} )
describe ( 'context initialised' , function ( ) {
let result : { current : EmailContextType }
beforeEach ( async function ( ) {
fetchMock . get ( /\/user\/emails/ , fakeUsersData )
const value = renderUserEmailsContext ( )
result = value . result
await fetchMock . flush ( true )
} )
describe ( 'getEmails()' , function ( ) {
beforeEach ( async function ( ) {
fetchMock . reset ( )
2022-05-24 03:48:18 -04:00
} )
it ( 'should set `isLoading === true`' , function ( ) {
2022-05-24 03:48:06 -04:00
fetchMock . get ( /\/user\/emails/ , [
{
email : 'new@email.com' ,
default : true ,
} ,
] )
result . current . getEmails ( )
expect ( result . current . state . isLoading ) . to . be . true
} )
it ( 'requests a new set of emails' , async function ( ) {
2022-05-24 03:48:18 -04:00
const emailData = {
email : 'new@email.com' ,
default : true ,
}
fetchMock . get ( /\/user\/emails/ , [ emailData ] )
2022-05-24 03:48:06 -04:00
result . current . getEmails ( )
await fetchMock . flush ( true )
expect ( result . current . state . data . byId ) . to . deep . equal ( {
2022-05-24 03:48:18 -04:00
'new@email.com' : emailData ,
2022-05-24 03:48:06 -04:00
} )
} )
2022-05-24 03:48:18 -04:00
it ( 'should populate `linkedInstitutionIds`' , async function ( ) {
fetchMock . get ( /\/user\/emails/ , [
confirmedUserData ,
{ . . . unconfirmedUserData , samlProviderId : 'saml_provider_1' } ,
{ . . . professionalUserData , samlProviderId : 'saml_provider_2' } ,
] )
const { result } = renderUserEmailsContext ( )
await fetchMock . flush ( true )
expect ( result . current . state . data . linkedInstitutionIds ) . to . deep . equal ( [
'saml_provider_1' ,
'saml_provider_2' ,
] )
} )
2022-05-24 03:48:06 -04:00
} )
describe ( 'makePrimary()' , function ( ) {
it ( 'sets an email as `default`' , function ( ) {
expect ( result . current . state . data . byId [ 'bar@overleaf.com' ] . default ) . to . be
. false
result . current . makePrimary ( 'bar@overleaf.com' )
expect ( result . current . state . data . byId [ 'bar@overleaf.com' ] . default ) . to . be
. true
} )
it ( 'sets `default=false` for the current primary email ' , function ( ) {
expect ( result . current . state . data . byId [ 'foo@overleaf.com' ] . default ) . to . be
. true
result . current . makePrimary ( 'bar@overleaf.com' )
expect ( result . current . state . data . byId [ 'foo@overleaf.com' ] . default ) . to . be
. false
} )
it ( 'produces no effect when passing a non-existing email' , function ( ) {
const emails = cloneDeep ( result . current . state . data . byId )
result . current . makePrimary ( 'non-existing@email.com' )
expect ( result . current . state . data . byId ) . to . deep . equal ( emails )
} )
} )
describe ( 'deleteEmail()' , function ( ) {
it ( 'removes data from the deleted email' , function ( ) {
result . current . deleteEmail ( 'bar@overleaf.com' )
expect ( result . current . state . data . byId [ 'bar@overleaf.com' ] ) . to . be
. undefined
} )
it ( 'produces no effect when passing a non-existing email' , function ( ) {
const emails = cloneDeep ( result . current . state . data . byId )
result . current . deleteEmail ( 'non-existing@email.com' )
expect ( result . current . state . data . byId ) . to . deep . equal ( emails )
} )
} )
describe ( 'setEmailAffiliationBeingEdited()' , function ( ) {
it ( 'sets an email as currently being edited' , function ( ) {
result . current . setEmailAffiliationBeingEdited ( 'bar@overleaf.com' )
expect ( result . current . state . data . emailAffiliationBeingEdited ) . to . equal (
'bar@overleaf.com'
)
result . current . setEmailAffiliationBeingEdited ( null )
expect ( result . current . state . data . emailAffiliationBeingEdited ) . to . be . null
} )
it ( 'produces no effect when passing a non-existing email' , function ( ) {
expect ( result . current . state . data . emailAffiliationBeingEdited ) . to . be . null
result . current . setEmailAffiliationBeingEdited ( 'non-existing@email.com' )
expect ( result . current . state . data . emailAffiliationBeingEdited ) . to . be . null
} )
} )
describe ( 'updateAffiliation()' , function ( ) {
it ( 'updates affiliation data for an email' , function ( ) {
result . current . updateAffiliation (
'foo@overleaf.com' ,
'new role' ,
'new department'
)
expect (
result . current . state . data . byId [ 'foo@overleaf.com' ] . affiliation . role
) . to . equal ( 'new role' )
expect (
result . current . state . data . byId [ 'foo@overleaf.com' ] . affiliation
. department
) . to . equal ( 'new department' )
} )
it ( 'clears an email from currently being edited' , function ( ) {
result . current . setEmailAffiliationBeingEdited ( 'foo@overleaf.com' )
result . current . updateAffiliation (
'foo@overleaf.com' ,
'new role' ,
'new department'
)
expect ( result . current . state . data . emailAffiliationBeingEdited ) . to . be . null
} )
it ( 'produces no effect when passing an email with no affiliation' , function ( ) {
const emails = cloneDeep ( result . current . state . data . byId )
result . current . updateAffiliation (
'bar@overleaf.com' ,
'new role' ,
'new department'
)
expect ( result . current . state . data . byId ) . to . deep . equal ( emails )
} )
it ( 'produces no effect when passing a non-existing email' , function ( ) {
const emails = cloneDeep ( result . current . state . data . byId )
result . current . updateAffiliation (
'non-existing@email.com' ,
'new role' ,
'new department'
)
expect ( result . current . state . data . byId ) . to . deep . equal ( emails )
} )
} )
describe ( 'resetLeaversSurveyExpiration()' , function ( ) {
beforeEach ( function ( ) {
localStorage . removeItem ( 'showInstitutionalLeaversSurveyUntil' )
} )
it ( 'when the leaver has institution license, and there is another email with institution license, it should not reset the survey expiration date' , async function ( ) {
const affiliatedEmail1 = cloneDeep ( professionalUserData )
affiliatedEmail1 . email = 'institution-test@example.com'
affiliatedEmail1 . emailHasInstitutionLicence = true
const affiliatedEmail2 = cloneDeep ( professionalUserData )
affiliatedEmail2 . emailHasInstitutionLicence = true
fetchMock . reset ( )
fetchMock . get ( /\/user\/emails/ , [ affiliatedEmail1 , affiliatedEmail2 ] )
result . current . getEmails ( )
await fetchMock . flush ( true )
// `resetLeaversSurveyExpiration` always happens after deletion
result . current . deleteEmail ( affiliatedEmail1 . email )
result . current . resetLeaversSurveyExpiration ( affiliatedEmail1 )
const expiration = localStorage . getItem (
'showInstitutionalLeaversSurveyUntil'
) as number
expect ( expiration ) . to . be . null
} )
it ( "when the leaver's affiliation is past reconfirmation date, and there is another email with institution license, it should not reset the survey expiration date" , async function ( ) {
const affiliatedEmail1 = cloneDeep ( professionalUserData )
affiliatedEmail1 . email = 'institution-test@example.com'
affiliatedEmail1 . affiliation . pastReconfirmDate = true
const affiliatedEmail2 = cloneDeep ( professionalUserData )
affiliatedEmail2 . emailHasInstitutionLicence = true
fetchMock . reset ( )
fetchMock . get ( /\/user\/emails/ , [ affiliatedEmail1 , affiliatedEmail2 ] )
result . current . getEmails ( )
await fetchMock . flush ( true )
// `resetLeaversSurveyExpiration` always happens after deletion
result . current . deleteEmail ( affiliatedEmail1 . email )
result . current . resetLeaversSurveyExpiration ( affiliatedEmail1 )
const expiration = localStorage . getItem (
'showInstitutionalLeaversSurveyUntil'
) as number
expect ( expiration ) . to . be . null
} )
it ( 'when there are no other emails with institution license, it should reset the survey expiration date' , async function ( ) {
const affiliatedEmail1 = cloneDeep ( professionalUserData )
affiliatedEmail1 . emailHasInstitutionLicence = true
affiliatedEmail1 . email = 'institution-test@example.com'
affiliatedEmail1 . affiliation . pastReconfirmDate = true
fetchMock . reset ( )
fetchMock . get ( /\/user\/emails/ , [ confirmedUserData , affiliatedEmail1 ] )
result . current . getEmails ( )
await fetchMock . flush ( true )
// `resetLeaversSurveyExpiration` always happens after deletion
result . current . deleteEmail ( affiliatedEmail1 . email )
result . current . resetLeaversSurveyExpiration ( affiliatedEmail1 )
expect (
localStorage . getItem ( 'showInstitutionalLeaversSurveyUntil' )
) . to . be . greaterThan ( Date . now ( ) )
} )
it ( "when the leaver has no institution license, it shouldn't reset the survey expiration date" , async function ( ) {
const emailWithInstitutionLicense = cloneDeep ( professionalUserData )
emailWithInstitutionLicense . email = 'institution-licensed@example.com'
emailWithInstitutionLicense . emailHasInstitutionLicence = false
fetchMock . reset ( )
fetchMock . get ( /\/user\/emails/ , [ emailWithInstitutionLicense ] )
result . current . getEmails ( )
await fetchMock . flush ( true )
// `resetLeaversSurveyExpiration` always happens after deletion
result . current . deleteEmail ( emailWithInstitutionLicense . email )
result . current . resetLeaversSurveyExpiration ( professionalUserData )
expect ( localStorage . getItem ( 'showInstitutionalLeaversSurveyUntil' ) ) . to
. be . null
} )
it ( "when the leaver is not past its reconfirmation date, it shouldn't reset the survey expiration date" , async function ( ) {
const emailWithInstitutionLicense = cloneDeep ( professionalUserData )
emailWithInstitutionLicense . email = 'institution-licensed@example.com'
emailWithInstitutionLicense . affiliation . pastReconfirmDate = false
fetchMock . reset ( )
fetchMock . get ( /\/user\/emails/ , [ emailWithInstitutionLicense ] )
result . current . getEmails ( )
await fetchMock . flush ( true )
// `resetLeaversSurveyExpiration` always happens after deletion
result . current . deleteEmail ( emailWithInstitutionLicense . email )
result . current . resetLeaversSurveyExpiration ( professionalUserData )
expect ( localStorage . getItem ( 'showInstitutionalLeaversSurveyUntil' ) ) . to
. be . null
} )
} )
} )
} )