import { render, screen, fireEvent, waitForElementToBeRemoved, within, } from '@testing-library/react' import userEvent from '@testing-library/user-event' import EmailsSection from '../../../../../../frontend/js/features/settings/components/emails-section' import { expect } from 'chai' import fetchMock from 'fetch-mock' import { UserEmailData } from '../../../../../../types/user-email' import { Affiliation } from '../../../../../../types/affiliation' import withMarkup from '../../../../helpers/with-markup' import getMeta from '@/utils/meta' import { clearDomainCache } from '../../../../../../frontend/js/features/settings/components/emails/add-email/input' import { SplitTestProvider } from '@/shared/context/split-test-context' const userEmailData: UserEmailData & { affiliation: Affiliation } = { affiliation: { cachedConfirmedAt: null, cachedEntitlement: null, cachedLastDayToReconfirm: null, cachedPastReconfirmDate: false, cachedReconfirmedAt: null, department: 'Art History', institution: { commonsAccount: false, writefullCommonsAccount: false, confirmed: true, id: 1, isUniversity: true, maxConfirmationMonths: null, name: 'Overleaf', ssoEnabled: false, ssoBeta: false, }, inReconfirmNotificationPeriod: false, inferred: false, licence: 'pro_plus', pastReconfirmDate: false, portal: { slug: '', templates_count: 1 }, role: 'Reader', }, email: 'baz@overleaf.com', default: false, } const institutionDomainData = [ { university: { id: 1234, ssoEnabled: true, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, }, ] as const function renderEmailsSection() { return render(, { wrapper: ({ children }) => ( {children} ), }) } function resetFetchMock() { fetchMock.removeRoutes().clearHistory() fetchMock.get('express:/institutions/domains', []) } async function confirmCodeForEmail(email: string) { const inputCode = await screen.findByRole('textbox', { name: /Enter the 6-digit confirmation code sent to /, }) screen.getByText(email, { exact: false }) fireEvent.change(inputCode, { target: { value: '123456' } }) const submitCodeBtn = screen.getByRole('button', { name: 'Confirm', }) fireEvent.click(submitCodeBtn) await waitForElementToBeRemoved(() => screen.getByRole('button', { name: 'Confirming' }) ) } describe('', function () { beforeEach(function () { Object.assign(getMeta('ol-ExposedSettings'), { hasAffiliationsFeature: true, hasSamlFeature: true, samlInitPath: 'saml/init', }) fetchMock.removeRoutes().clearHistory() }) afterEach(function () { resetFetchMock() clearDomainCache() }) it('renders "add another email" button', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() await screen.findByRole('button', { name: 'Add another email' }) }) it('renders input', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() await fetchMock.callHistory.flush(true) const button = await screen.findByRole('button', { name: 'Add another email', }) fireEvent.click(button) await screen.findByRole('textbox', { name: 'Email' }) }) it('renders "Start adding your address" until a valid email is typed', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) fetchMock.get(`/institutions/domains?hostname=email.com&limit=1`, 200) fetchMock.get(`/institutions/domains?hostname=email&limit=1`, 200) renderEmailsSection() await fetchMock.callHistory.flush(true) const button = await screen.findByRole('button', { name: 'Add another email', }) fireEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) // initially the text is displayed and the "add email" button disabled screen.getByText('Start by adding your email address.') expect( screen.getByRole('button', { name: 'Add new email', }).disabled ).to.be.true // no changes while writing the email address fireEvent.change(input, { target: { value: 'partial@email' }, }) screen.getByText('Start by adding your email address.') expect( screen.getByRole('button', { name: 'Add new email', }).disabled ).to.be.true // the text is removed when the complete email address is typed, and the "add button" is reenabled fireEvent.change(input, { target: { value: 'valid@email.com' }, }) expect(screen.queryByText('Start by adding your email address.')).to.be.null expect( screen.getByRole('button', { name: 'Add new email', }).disabled ).to.be.false }) it('renders "add new email" button', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) fireEvent.click(button) screen.getByRole('button', { name: 'Add new email' }) }) it('prevent users from adding new emails when the limit is reached', async function () { const emails = [] for (let i = 0; i < 10; i++) { emails.push({ email: `bar${i}@overleaf.com` }) } fetchMock.get('/user/emails?ensureAffiliation=true', emails) renderEmailsSection() const findByTextWithMarkup = withMarkup(screen.findByText) await findByTextWithMarkup( 'You can have a maximum of 10 email addresses on this account. To add another email address, please delete an existing one.' ) expect(screen.queryByRole('button', { name: 'Add another email' })).to.not .exist }) it('adds new email address', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const addAnotherEmailBtn = await screen.findByRole( 'button', { name: 'Add another email' } ) await fetchMock.callHistory.flush(true) resetFetchMock() fetchMock .get('/user/emails?ensureAffiliation=true', [userEmailData]) .post('/user/emails/secondary', 200) .post('/user/emails/confirm-secondary', 200) fireEvent.click(addAnotherEmailBtn) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: userEmailData.email }, }) const submitBtn = screen.getByRole('button', { name: 'Add new email', }) expect(submitBtn.disabled).to.be.false fireEvent.click(submitBtn) expect(submitBtn.disabled).to.be.true await waitForElementToBeRemoved(() => screen.getByRole('button', { name: /adding/i, }) ) await confirmCodeForEmail(userEmailData.email) const elements = await screen.findAllByText(userEmailData.email) expect(elements.length).to.equal(2) }) it('fails to add add new email address', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const addAnotherEmailBtn = await screen.findByRole( 'button', { name: 'Add another email' } ) await fetchMock.callHistory.flush(true) resetFetchMock() fetchMock .get('/user/emails?ensureAffiliation=true', []) .post('/user/emails/secondary', 400) fireEvent.click(addAnotherEmailBtn) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: userEmailData.email }, }) const submitBtn = screen.getByRole('button', { name: 'Add new email', }) expect(submitBtn.disabled).to.be.false fireEvent.click(submitBtn) expect(submitBtn.disabled).to.be.true await screen.findByText( 'Invalid Request. Please correct the data and try again.' ) expect(submitBtn).to.not.be.null expect(submitBtn.disabled).to.be.false }) it('can link email address to an existing SSO institution', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() fetchMock.get('express:/institutions/domains', institutionDomainData) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) await screen.findByRole('button', { name: 'Link accounts and add email' }) }) it('prevents user from linking to same SSO institution twice', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', [ { email: 'bar@autocomplete.edu', samlProviderId: '1234' }, ]) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() fetchMock.get('express:/institutions/domains', institutionDomainData) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'baz@autocomplete.edu' }, }) await screen.findByText( 'This institution is already linked with your account via another email address.' ) }) it('adds new email address with existing institution and custom departments', async function () { const country = 'Germany' const customDepartment = 'Custom department' fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) resetFetchMock() await userEvent.click(button) await userEvent.type( screen.getByRole('textbox', { name: 'Email' }), userEmailData.email ) await userEvent.click(screen.getByRole('button', { name: 'Let us know' })) const universityInput = screen.getByRole('combobox', { name: 'University', }) expect(universityInput.disabled).to.be.true fetchMock.get('/institutions/list?country_code=de', [ { id: userEmailData.affiliation.institution.id, name: userEmailData.affiliation.institution.name, country_code: 'de', departments: [customDepartment], }, ]) // Select the country from dropdown await userEvent.type( screen.getByRole('combobox', { name: 'Country', }), country ) await userEvent.click(screen.getByText(country)) expect(universityInput.disabled).to.be.false await fetchMock.callHistory.flush(true) resetFetchMock() // Select the university from dropdown await userEvent.click(universityInput) await userEvent.click( await screen.findByText(userEmailData.affiliation.institution.name) ) const roleInput = screen.getByRole('combobox', { name: 'Role' }) await userEvent.type(roleInput, userEmailData.affiliation.role!) const departmentInput = screen.getByRole('combobox', { name: 'Department', }) await userEvent.click(departmentInput) await userEvent.click(screen.getByText(customDepartment)) const userEmailDataCopy = { ...userEmailData, affiliation: { ...userEmailData.affiliation, department: customDepartment, }, } fetchMock .get('/user/emails?ensureAffiliation=true', [userEmailDataCopy]) .post('/user/emails/secondary', 200) .post('/user/emails/confirm-secondary', 200) await userEvent.click( screen.getByRole('button', { name: 'Add new email', }) ) const request = fetchMock.callHistory.calls(/\/user\/emails/).at(0) expect( JSON.parse(request?.options.body?.toString() || '{}') ).to.deep.include({ email: userEmailData.email, university: { id: userEmailData.affiliation?.institution.id, }, role: userEmailData.affiliation?.role, department: customDepartment, }) await screen.findByText(/Enter the 6-digit confirmation code sent to /) await confirmCodeForEmail(userEmailData.email) await screen.findByText(userEmailData.affiliation.role!, { exact: false }) await screen.findByText(customDepartment, { exact: false }) }) it('autocompletes institution name', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) resetFetchMock() fetchMock.get('/institutions/list?country_code=de', [ { id: 1, name: 'University of Bonn', }, { id: 2, name: 'Bochum institute of Science', }, ]) // open "add new email" section and click "let us know" to open the Country/University form await userEvent.click(button) await userEvent.type( screen.getByRole('textbox', { name: 'Email' }), userEmailData.email ) await userEvent.click(screen.getByRole('button', { name: 'Let us know' })) // select a country const countryInput = screen.getByRole('combobox', { name: 'Country', }) await userEvent.click(countryInput) await userEvent.type(countryInput, 'Germ') await userEvent.click(await screen.findByText('Germany')) // match several universities on initial typing const universityInput = screen.getByRole('combobox', { name: 'University', }) await userEvent.click(universityInput) await userEvent.type(universityInput, 'bo') await screen.findByText('University of Bonn') await screen.findByText('Bochum institute of Science') // match a single university when typing to refine the search await userEvent.type(universityInput, 'nn') await screen.findByText('University of Bonn') expect(screen.queryByText('Bochum institute of Science')).to.be.null }) it('adds new email address without existing institution', async function () { const country = 'Germany' const countryCode = 'de' const newUniversity = 'Abcdef' fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) resetFetchMock() await userEvent.click(button) await userEvent.type( screen.getByRole('textbox', { name: 'Email' }), userEmailData.email ) await userEvent.click(screen.getByRole('button', { name: 'Let us know' })) const universityInput = screen.getByRole('combobox', { name: 'University', }) expect(universityInput.disabled).to.be.true fetchMock.get(/\/institutions\/list/, [ { id: userEmailData.affiliation.institution.id, name: userEmailData.affiliation.institution.name, country_code: 'de', departments: [], }, ]) // Select the country from dropdown await userEvent.type( screen.getByRole('combobox', { name: 'Country', }), country ) await userEvent.click(screen.getByText(country)) expect(universityInput.disabled).to.be.false await fetchMock.callHistory.flush(true) resetFetchMock() // Enter the university manually await userEvent.type(universityInput, newUniversity) const roleInput = screen.getByRole('combobox', { name: 'Role' }) await userEvent.type(roleInput, userEmailData.affiliation.role!) const departmentInput = screen.getByRole('combobox', { name: 'Department', }) await userEvent.type(departmentInput, userEmailData.affiliation.department!) const userEmailDataCopy = { ...userEmailData, affiliation: { ...userEmailData.affiliation, institution: { ...userEmailData.affiliation.institution, name: newUniversity, }, }, } fetchMock .get('/user/emails?ensureAffiliation=true', [userEmailDataCopy]) .post('/user/emails/secondary', 200) .post('/user/emails/confirm-secondary', 200) await userEvent.click( screen.getByRole('button', { name: 'Add new email', }) ) await confirmCodeForEmail(userEmailData.email) const request = fetchMock.callHistory.calls(/\/user\/emails/).at(0) expect( JSON.parse(request?.options.body?.toString() || '{}') ).to.deep.include({ email: userEmailData.email, university: { name: newUniversity, country_code: countryCode, }, role: userEmailData.affiliation?.role, department: userEmailData.affiliation?.department, }) const elements = await screen.findAllByText(userEmailData.email) expect(elements.length).to.equal(2) await screen.findByText(newUniversity) await screen.findByText(userEmailData.affiliation.role!, { exact: false }) await screen.findByText(userEmailData.affiliation.department!, { exact: false, }) }) it('shows country, university, role and department fields based on whether `change` was clicked or not', async function () { const institutionDomainDataCopy = [ { ...institutionDomainData[0], university: { ...institutionDomainData[0].university, ssoEnabled: false, }, }, ] const hostnameFirstChar = institutionDomainDataCopy[0].hostname.charAt(0) fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() fetchMock.get( `/institutions/domains?hostname=${hostnameFirstChar}&limit=1`, institutionDomainDataCopy ) await userEvent.click(button) await userEvent.type( screen.getByRole('textbox', { name: 'Email' }), `user@${hostnameFirstChar}` ) await userEvent.keyboard('{Tab}') await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() expect( screen.queryByRole('combobox', { name: 'Country', }) ).to.be.null expect( screen.queryByRole('combobox', { name: 'University', }) ).to.be.null screen.getByRole('combobox', { name: 'Role', }) screen.getByRole('combobox', { name: 'Department', }) await userEvent.click(screen.getByRole('button', { name: 'Change' })) screen.getByRole('combobox', { name: 'Country', }) screen.getByRole('combobox', { name: 'University', }) expect( screen.queryByRole('combobox', { name: 'Role', }) ).to.be.null expect( screen.queryByRole('combobox', { name: 'Department', }) ).to.be.null }) it('displays institution name with change button when autocompleted and adds new record', async function () { const institutionDomainDataCopy = [ { ...institutionDomainData[0], university: { ...institutionDomainData[0].university, ssoEnabled: false, }, }, ] const hostnameFirstChar = institutionDomainDataCopy[0].hostname.charAt(0) fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() fetchMock.get( `/institutions/domains?hostname=${hostnameFirstChar}&limit=1`, institutionDomainDataCopy ) await userEvent.click(button) await userEvent.type( screen.getByRole('textbox', { name: 'Email' }), `user@${hostnameFirstChar}` ) await userEvent.keyboard('{Tab}') await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() screen.getByText(institutionDomainDataCopy[0].university.name) const userEmailDataCopy = { ...userEmailData, affiliation: { ...userEmailData.affiliation, institution: { ...userEmailData.affiliation.institution, name: institutionDomainDataCopy[0].university.name, }, }, } fetchMock .get('/user/emails?ensureAffiliation=true', [userEmailDataCopy]) .post('/user/emails/secondary', 200) .post('/user/emails/confirm-secondary', 200) await userEvent.type( screen.getByRole('combobox', { name: 'Role' }), userEmailData.affiliation.role! ) await userEvent.type( screen.getByRole('combobox', { name: 'Department' }), userEmailData.affiliation.department! ) await userEvent.click( screen.getByRole('button', { name: 'Add new email', }) ) await confirmCodeForEmail('user@autocomplete.edu') await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() screen.getByText(userEmailDataCopy.affiliation.institution.name, { exact: false, }) screen.getByText(userEmailDataCopy.affiliation.role!, { exact: false }) screen.getByText(userEmailDataCopy.affiliation.department!, { exact: false, }) }) describe('when domain is captured by a group', function () { describe('and managed users is not enabled', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: false, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, group: { domainCaptureEnabled: true, ssoConfig: { enabled: true, }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('can add email address via SSO', async function () { // note: this UI is a WIP fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) await screen.findByText('This feature is currently unavailable.') }) }) describe('and managed users is enabled', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: false, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, group: { domainCaptureEnabled: true, managedUsersEnabled: true, ssoConfig: { enabled: true, }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('renders error', async function () { // note: this UI is a WIP fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) const notification = await screen.findByRole('alert') within(notification).getByText( 'Your company email address has been registered under a verified domain, and cannot be added as a secondary email.', { exact: false } ) }) }) describe('if Commons SSO is enabled and managed users is not enabled, Commons takes priority over group UI', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: true, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, group: { domainCaptureEnabled: true, ssoConfig: { enabled: true, }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('renders Commons UI', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) await screen.findByRole('button', { name: 'Link accounts and add email', }) }) }) describe('if Commons SSO is enabled and managed users is enabled, group domain capture takes priority', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: true, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, group: { domainCaptureEnabled: true, managedUsersEnabled: true, ssoConfig: { enabled: true, }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('renders group domain-capture error instead of Commons UI', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) const notification = await screen.findByRole('alert') within(notification).getByText( 'Your company email address has been registered under a verified domain, and cannot be added as a secondary email.', { exact: false } ) expect( screen.queryByRole('button', { name: 'Link accounts and add email', }) ).to.be.null }) }) describe('when the domain-captured-by-group feature flag is enabled', function () { beforeEach(function () { window.metaAttributesCache.set('ol-splitTestVariants', { 'domain-captured-by-group': 'enabled', }) }) afterEach(function () { window.metaAttributesCache.set('ol-splitTestVariants', {}) }) describe('and capturedByGroup is true with managedUsersEnabled', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: false, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, capturedByGroup: true, group: { domainCaptureEnabled: true, managedUsersEnabled: true, ssoConfig: { enabled: true }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('renders the domain-capture error', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) const notification = await screen.findByRole('alert') within(notification).getByText( 'Your company email address has been registered under a verified domain, and cannot be added as a secondary email.', { exact: false } ) }) }) describe('and capturedByGroup is false with managedUsersEnabled (domainCaptureEnabled alone is not enough)', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: false, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, capturedByGroup: false, group: { domainCaptureEnabled: true, managedUsersEnabled: true, ssoConfig: { enabled: true }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('does not render the domain-capture error and shows regular institution fields', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) await screen.findByRole('button', { name: 'Add new email' }) expect(screen.queryByRole('alert')).to.be.null }) }) describe('and capturedByGroup is true with ssoConfig enabled (no managedUsers)', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: false, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, capturedByGroup: true, group: { domainCaptureEnabled: true, managedUsersEnabled: false, ssoConfig: { enabled: true }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('renders the group SSO unavailable message', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) await screen.findByText('This feature is currently unavailable.') }) }) describe('and capturedByGroup is false with ssoConfig enabled', function () { beforeEach(async function () { await fetchMock.callHistory.flush(true) fetchMock.removeRoutes().clearHistory() const institution = { university: { id: 1234, ssoEnabled: false, name: 'Auto Complete University', }, hostname: 'autocomplete.edu', confirmed: true, capturedByGroup: false, group: { domainCaptureEnabled: true, managedUsersEnabled: false, ssoConfig: { enabled: true }, }, } fetchMock.get('express:/institutions/domains', [institution]) }) it('does not render SSO UI and shows regular institution fields', async function () { fetchMock.get('/user/emails?ensureAffiliation=true', []) renderEmailsSection() const button = await screen.findByRole('button', { name: 'Add another email', }) await userEvent.click(button) const input = screen.getByRole('textbox', { name: 'Email' }) fireEvent.change(input, { target: { value: 'user@autocomplete.edu' }, }) await screen.findByRole('button', { name: 'Add new email' }) expect(screen.queryByText('This feature is currently unavailable.')) .to.be.null }) }) }) }) })