Просмотр исходного кода

Merge pull request #33202 from overleaf/jel-domain-captured-by-group-settings-page

[Domain capture] Check `domainCapturedByGroup` for existing emails on user settings

GitOrigin-RevId: 5ac86b89969b186cce0cac410c2957e5aa1b9703
Jessica Lawshe 5 месяцев назад
Родитель
Сommit
ba13ccdb11

+ 5 - 1
services/web/app/src/Features/User/UserPagesController.mjs

@@ -117,7 +117,11 @@ async function settingsPage(req, res) {
   }
   }
 
 
   await SplitTestHandler.promises.getAssignment(req, res, 'email-notifications')
   await SplitTestHandler.promises.getAssignment(req, res, 'email-notifications')
-
+  await SplitTestHandler.promises.getAssignment(
+    req,
+    res,
+    'domain-captured-by-group'
+  )
   res.render('user/settings', {
   res.render('user/settings', {
     title: 'account_settings',
     title: 'account_settings',
     user: {
     user: {

+ 9 - 1
services/web/frontend/js/features/settings/components/emails/row.tsx

@@ -8,6 +8,7 @@ import Actions from './actions'
 import { institutionAlreadyLinked } from '../../utils/selectors'
 import { institutionAlreadyLinked } from '../../utils/selectors'
 import { useUserEmailsContext } from '../../context/user-email-context'
 import { useUserEmailsContext } from '../../context/user-email-context'
 import getMeta from '../../../../utils/meta'
 import getMeta from '../../../../utils/meta'
+import { useFeatureFlag } from '@/shared/context/split-test-context'
 import { ssoAvailableForInstitution } from '../../utils/sso'
 import { ssoAvailableForInstitution } from '../../utils/sso'
 import ReconfirmationInfo from './reconfirmation-info'
 import ReconfirmationInfo from './reconfirmation-info'
 import { useLocation } from '../../../../shared/hooks/use-location'
 import { useLocation } from '../../../../shared/hooks/use-location'
@@ -71,6 +72,10 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
   const [linkAccountsButtonDisabled, setLinkAccountsButtonDisabled] =
   const [linkAccountsButtonDisabled, setLinkAccountsButtonDisabled] =
     useState(false)
     useState(false)
 
 
+  const domainCapturedByGroupRolloutFlagEnabled = useFeatureFlag(
+    'domain-captured-by-group'
+  )
+
   function handleLinkAccountsButtonClick() {
   function handleLinkAccountsButtonClick() {
     setLinkAccountsButtonDisabled(true)
     setLinkAccountsButtonDisabled(true)
     location.assign(
     location.assign(
@@ -140,7 +145,10 @@ function SSOAffiliationInfo({ userEmailData }: SSOAffiliationInfoProps) {
   }
   }
 
 
   const domainAlsoForGroupWithDomainCapture =
   const domainAlsoForGroupWithDomainCapture =
-    userEmailData?.affiliation?.group?.domainCaptureEnabled
+    domainCapturedByGroupRolloutFlagEnabled
+      ? userEmailData?.affiliation?.domainCapturedByGroup &&
+        userEmailData?.affiliation?.group?.domainCaptureEnabled
+      : userEmailData?.affiliation?.group?.domainCaptureEnabled
 
 
   if (domainAlsoForGroupWithDomainCapture) {
   if (domainAlsoForGroupWithDomainCapture) {
     // user is not linked via Commons and should link via groups
     // user is not linked via Commons and should link via groups

+ 6 - 1
services/web/frontend/js/pages/user/settings.tsx

@@ -4,10 +4,15 @@ import '@/utils/webpack-public-path'
 import '@/infrastructure/error-reporter'
 import '@/infrastructure/error-reporter'
 import '@/i18n'
 import '@/i18n'
 import SettingsPageRoot from '@/features/settings/components/root'
 import SettingsPageRoot from '@/features/settings/components/root'
+import { SplitTestProvider } from '@/shared/context/split-test-context'
 
 
 // For react-google-recaptcha
 // For react-google-recaptcha
 window.recaptchaOptions = {
 window.recaptchaOptions = {
   enterprise: true,
   enterprise: true,
   useRecaptchaNet: true,
   useRecaptchaNet: true,
 }
 }
-renderInReactLayout('settings-page-root', () => <SettingsPageRoot />)
+renderInReactLayout('settings-page-root', () => (
+  <SplitTestProvider>
+    <SettingsPageRoot />
+  </SplitTestProvider>
+))

+ 38 - 3
services/web/test/frontend/features/settings/components/emails/emails-row.test.tsx

@@ -11,12 +11,15 @@ import { UserEmailData } from '../../../../../../types/user-email'
 import { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
 import { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
 import { Affiliation } from '../../../../../../types/affiliation'
 import { Affiliation } from '../../../../../../types/affiliation'
 import getMeta from '@/utils/meta'
 import getMeta from '@/utils/meta'
+import { SplitTestProvider } from '@/shared/context/split-test-context'
 
 
 function renderEmailsRow(data: UserEmailData) {
 function renderEmailsRow(data: UserEmailData) {
   return render(
   return render(
-    <UserEmailsProvider>
-      <EmailsRow userEmailData={data} />
-    </UserEmailsProvider>
+    <SplitTestProvider>
+      <UserEmailsProvider>
+        <EmailsRow userEmailData={data} />
+      </UserEmailsProvider>
+    </SplitTestProvider>
   )
   )
 }
 }
 
 
@@ -33,6 +36,7 @@ describe('<EmailsRow/>', function () {
       samlInitPath: '/saml',
       samlInitPath: '/saml',
       hasSamlBeta: true,
       hasSamlBeta: true,
     })
     })
+    window.metaAttributesCache.set('ol-splitTestVariants', {})
     fetchMock.get('/user/emails?ensureAffiliation=true', [])
     fetchMock.get('/user/emails?ensureAffiliation=true', [])
   })
   })
 
 
@@ -165,6 +169,37 @@ describe('<EmailsRow/>', function () {
         expect(screen.queryByRole('button', { name: 'Link accounts' })).to.be
         expect(screen.queryByRole('button', { name: 'Link accounts' })).to.be
           .null
           .null
       })
       })
+
+      it('uses `domainCapturedByGroup` when the feature flag is enabled', function () {
+        affiliatedEmailWithDomainCaptureAndCommons.affiliation.group = {
+          _id: 'grou123',
+          domainCaptureEnabled: true,
+          managedUsersEnabled: true,
+        }
+        affiliatedEmailWithDomainCaptureAndCommons.affiliation.domainCapturedByGroup = true
+        window.metaAttributesCache.set('ol-splitTestVariants', {
+          'domain-captured-by-group': 'enabled',
+        })
+
+        renderEmailsRow(affiliatedEmailWithDomainCaptureAndCommons)
+
+        expect(screen.queryByRole('button', { name: 'Link accounts' })).to.be
+          .null
+      })
+
+      it('ignores group domain capture when the feature flag is enabled and the domain is not captured', function () {
+        affiliatedEmailWithDomainCaptureAndCommons.affiliation.domainCapturedByGroup = false
+        window.metaAttributesCache.set('ol-splitTestVariants', {
+          'domain-captured-by-group': 'enabled',
+        })
+
+        renderEmailsRow(affiliatedEmailWithDomainCaptureAndCommons)
+
+        getByTextContent(
+          'You can now link your Overleaf account to your Overleaf institutional account.'
+        )
+        screen.getByRole('button', { name: 'Link accounts' })
+      })
     })
     })
   })
   })
 })
 })

+ 20 - 10
services/web/test/frontend/features/settings/components/emails/emails-section-actions.test.tsx

@@ -13,6 +13,7 @@ import EmailsSection from '../../../../../../frontend/js/features/settings/compo
 import { Institution } from '../../../../../../types/institution'
 import { Institution } from '../../../../../../types/institution'
 import { Affiliation } from '../../../../../../types/affiliation'
 import { Affiliation } from '../../../../../../types/affiliation'
 import getMeta from '@/utils/meta'
 import getMeta from '@/utils/meta'
+import { SplitTestProvider } from '@/shared/context/split-test-context'
 
 
 const userEmailData: UserEmailData = {
 const userEmailData: UserEmailData = {
   confirmedAt: '2022-03-10T10:59:44.139Z',
   confirmedAt: '2022-03-10T10:59:44.139Z',
@@ -32,6 +33,14 @@ const userEmailData2: UserEmailData & { affiliation: Affiliation } = {
   default: false,
   default: false,
 }
 }
 
 
+function renderEmailsSection() {
+  return render(<EmailsSection />, {
+    wrapper: ({ children }) => (
+      <SplitTestProvider>{children}</SplitTestProvider>
+    ),
+  })
+}
+
 describe('email actions - make primary', function () {
 describe('email actions - make primary', function () {
   beforeEach(function () {
   beforeEach(function () {
     Object.assign(getMeta('ol-ExposedSettings'), {
     Object.assign(getMeta('ol-ExposedSettings'), {
@@ -49,7 +58,7 @@ describe('email actions - make primary', function () {
       const userEmailDataCopy = { ...userEmailData2 }
       const userEmailDataCopy = { ...userEmailData2 }
       const { confirmedAt: _, ...userEmailData } = userEmailDataCopy
       const { confirmedAt: _, ...userEmailData } = userEmailDataCopy
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailData])
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailData])
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       const button = (await screen.findByRole('button', {
       const button = (await screen.findByRole('button', {
         name: /make primary/i,
         name: /make primary/i,
@@ -66,7 +75,7 @@ describe('email actions - make primary', function () {
         },
         },
       }
       }
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       const button = (await screen.findByRole('button', {
       const button = (await screen.findByRole('button', {
         name: /make primary/i,
         name: /make primary/i,
@@ -86,7 +95,7 @@ describe('email actions - make primary', function () {
       }
       }
 
 
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       const button = (await screen.findByRole('button', {
       const button = (await screen.findByRole('button', {
         name: /make primary/i,
         name: /make primary/i,
@@ -103,7 +112,7 @@ describe('email actions - make primary', function () {
       const userEmailDataCopy = { ...userEmailData2 }
       const userEmailDataCopy = { ...userEmailData2 }
 
 
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailDataCopy])
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       const button = (await screen.findByRole('button', {
       const button = (await screen.findByRole('button', {
         name: /make primary/i,
         name: /make primary/i,
@@ -144,7 +153,7 @@ describe('email actions - make primary', function () {
         userEmailDataCopy1,
         userEmailDataCopy1,
         userEmailDataCopy2,
         userEmailDataCopy2,
       ])
       ])
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       const buttons = (await screen.findAllByRole('button', {
       const buttons = (await screen.findAllByRole('button', {
         name: /make primary/i,
         name: /make primary/i,
@@ -177,7 +186,7 @@ describe('email actions - make primary', function () {
 
 
     it('shows confirmation modal and closes it', async function () {
     it('shows confirmation modal and closes it', async function () {
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailData])
       fetchMock.get('/user/emails?ensureAffiliation=true', [userEmailData])
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       const button = await screen.findByRole('button', {
       const button = await screen.findByRole('button', {
         name: /make primary/i,
         name: /make primary/i,
@@ -205,7 +214,7 @@ describe('email actions - make primary', function () {
       fetchMock
       fetchMock
         .get('/user/emails?ensureAffiliation=true', [userEmailData])
         .get('/user/emails?ensureAffiliation=true', [userEmailData])
         .post('/user/emails/default?delete-unconfirmed-primary', 200)
         .post('/user/emails/default?delete-unconfirmed-primary', 200)
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       await confirmPrimaryEmail()
       await confirmPrimaryEmail()
 
 
@@ -221,7 +230,7 @@ describe('email actions - make primary', function () {
       fetchMock
       fetchMock
         .get('/user/emails?ensureAffiliation=true', [userEmailData])
         .get('/user/emails?ensureAffiliation=true', [userEmailData])
         .post('/user/emails/default?delete-unconfirmed-primary', 503)
         .post('/user/emails/default?delete-unconfirmed-primary', 503)
-      render(<EmailsSection />)
+      renderEmailsSection()
 
 
       await confirmPrimaryEmail()
       await confirmPrimaryEmail()
 
 
@@ -241,13 +250,14 @@ describe('email actions - delete', function () {
 
 
   afterEach(function () {
   afterEach(function () {
     fetchMock.removeRoutes().clearHistory()
     fetchMock.removeRoutes().clearHistory()
+    window.metaAttributesCache.set('ol-splitTestVariants', {})
   })
   })
 
 
   it('shows loader when deleting and removes the row', async function () {
   it('shows loader when deleting and removes the row', async function () {
     fetchMock
     fetchMock
       .get('/user/emails?ensureAffiliation=true', [userEmailData])
       .get('/user/emails?ensureAffiliation=true', [userEmailData])
       .post('/user/emails/delete', 200)
       .post('/user/emails/delete', 200)
-    render(<EmailsSection />)
+    renderEmailsSection()
 
 
     const button = await screen.findByRole('button', { name: /remove/i })
     const button = await screen.findByRole('button', { name: /remove/i })
     fireEvent.click(button)
     fireEvent.click(button)
@@ -261,7 +271,7 @@ describe('email actions - delete', function () {
     fetchMock
     fetchMock
       .get('/user/emails?ensureAffiliation=true', [userEmailData])
       .get('/user/emails?ensureAffiliation=true', [userEmailData])
       .post('/user/emails/delete', 503)
       .post('/user/emails/delete', 503)
-    render(<EmailsSection />)
+    renderEmailsSection()
 
 
     const button = await screen.findByRole('button', { name: /remove/i })
     const button = await screen.findByRole('button', { name: /remove/i })
     fireEvent.click(button)
     fireEvent.click(button)