Jelajahi Sumber

Merge pull request #8226 from overleaf/ae-remaining-types

[web] Fix remaining strict type checks

GitOrigin-RevId: 69881c37938f88c7ea4a630f362712a804085bc8
ilkin-overleaf 4 tahun lalu
induk
melakukan
e41315364d
19 mengubah file dengan 91 tambahan dan 71 penghapusan
  1. 1 1
      services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx
  2. 1 1
      services/web/frontend/js/features/editor-navigation-toolbar/components/shortcut.tsx
  3. 0 1
      services/web/frontend/js/features/settings/components/emails/add-email/input.tsx
  4. 4 2
      services/web/frontend/js/features/settings/utils/sso.ts
  5. 6 6
      services/web/frontend/stories/settings/sso-alert.stories.tsx
  6. 25 17
      services/web/test/frontend/features/settings/components/account-info-section.test.tsx
  7. 3 3
      services/web/test/frontend/features/settings/components/emails/add-email-input.test.tsx
  8. 3 2
      services/web/test/frontend/features/settings/components/emails/emails-row.test.tsx
  9. 4 4
      services/web/test/frontend/features/settings/components/emails/emails-section-actions.test.tsx
  10. 14 11
      services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx
  11. 5 4
      services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx
  12. 6 6
      services/web/test/frontend/features/settings/components/leave/modal-form.test.tsx
  13. 1 1
      services/web/test/frontend/features/settings/components/leavers-survey-alert.test.tsx
  14. 2 2
      services/web/test/frontend/features/settings/components/linking/sso-widget.test.tsx
  15. 8 6
      services/web/test/frontend/features/settings/components/password-section.test.tsx
  16. 1 1
      services/web/test/frontend/features/settings/components/root.test.tsx
  17. 2 2
      services/web/test/frontend/features/settings/context/user-email-context.test.tsx
  18. 4 1
      services/web/test/frontend/features/settings/fixtures/test-user-email-data.ts
  19. 1 0
      services/web/types/window.ts

+ 1 - 1
services/web/frontend/js/features/dictionary/components/dictionary-modal-content.tsx

@@ -12,7 +12,7 @@ type DictionaryModalContentProps = {
   handleHide: () => void
   handleHide: () => void
 }
 }
 
 
-const wordsSortFunction = (a, b) => a.localeCompare(b)
+const wordsSortFunction = (a: string, b: string) => a.localeCompare(b)
 
 
 export default function DictionaryModalContent({
 export default function DictionaryModalContent({
   handleHide,
   handleHide,

+ 1 - 1
services/web/frontend/js/features/editor-navigation-toolbar/components/shortcut.tsx

@@ -2,7 +2,7 @@ import { Fragment, memo } from 'react'
 
 
 const isMac = /Mac/.test(window.navigator.platform)
 const isMac = /Mac/.test(window.navigator.platform)
 
 
-const symbols = isMac
+const symbols: Record<string, string> = isMac
   ? {
   ? {
       CommandOrControl: '⌘',
       CommandOrControl: '⌘',
       Option: '⌥',
       Option: '⌥',

+ 0 - 1
services/web/frontend/js/features/settings/components/emails/add-email/input.tsx

@@ -125,7 +125,6 @@ function Input({ onChange, handleAddNewEmail }: InputProps) {
 
 
         if (suggestion) {
         if (suggestion) {
           setInputValueAndResetSuggestion()
           setInputValueAndResetSuggestion()
-
           return
           return
         }
         }
 
 

+ 4 - 2
services/web/frontend/js/features/settings/utils/sso.ts

@@ -3,7 +3,9 @@ import { DomainInfo } from '../components/emails/add-email/input'
 import { ExposedSettings } from '../../../../../types/exposed-settings'
 import { ExposedSettings } from '../../../../../types/exposed-settings'
 import { Institution } from '../../../../../types/institution'
 import { Institution } from '../../../../../types/institution'
 
 
-export const ssoAvailableForDomain = (domain: DomainInfo | null) => {
+export const ssoAvailableForDomain = (
+  domain: DomainInfo | null
+): domain is DomainInfo => {
   const { hasSamlBeta, hasSamlFeature } = getMeta(
   const { hasSamlBeta, hasSamlFeature } = getMeta(
     'ol-ExposedSettings'
     'ol-ExposedSettings'
   ) as ExposedSettings
   ) as ExposedSettings
@@ -13,7 +15,7 @@ export const ssoAvailableForDomain = (domain: DomainInfo | null) => {
   if (domain.university.ssoEnabled) {
   if (domain.university.ssoEnabled) {
     return true
     return true
   }
   }
-  return hasSamlBeta && domain.university.ssoBeta
+  return Boolean(hasSamlBeta && domain.university.ssoBeta)
 }
 }
 
 
 export const ssoAvailableForInstitution = (institution: Institution | null) => {
 export const ssoAvailableForInstitution = (institution: Institution | null) => {

+ 6 - 6
services/web/frontend/stories/settings/sso-alert.stories.tsx

@@ -1,7 +1,7 @@
 import EmailsSection from '../../js/features/settings/components/emails-section'
 import EmailsSection from '../../js/features/settings/components/emails-section'
 import { SSOAlert } from '../../js/features/settings/components/emails/sso-alert'
 import { SSOAlert } from '../../js/features/settings/components/emails/sso-alert'
 
 
-export const Info = (args: any) => {
+export const Info = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-institutionLinked', {
   window.metaAttributesCache.set('ol-institutionLinked', {
     universityName: 'Overleaf University',
     universityName: 'Overleaf University',
@@ -9,7 +9,7 @@ export const Info = (args: any) => {
   return <SSOAlert />
   return <SSOAlert />
 }
 }
 
 
-export const InfoWithEntitlement = (args: any) => {
+export const InfoWithEntitlement = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-institutionLinked', {
   window.metaAttributesCache.set('ol-institutionLinked', {
     universityName: 'Overleaf University',
     universityName: 'Overleaf University',
@@ -18,7 +18,7 @@ export const InfoWithEntitlement = (args: any) => {
   return <SSOAlert />
   return <SSOAlert />
 }
 }
 
 
-export const NonCanonicalEmail = (args: any) => {
+export const NonCanonicalEmail = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-institutionLinked', {
   window.metaAttributesCache.set('ol-institutionLinked', {
     universityName: 'Overleaf University',
     universityName: 'Overleaf University',
@@ -30,7 +30,7 @@ export const NonCanonicalEmail = (args: any) => {
   return <SSOAlert />
   return <SSOAlert />
 }
 }
 
 
-export const Error = (args: any) => {
+export const Error = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-samlError', {
   window.metaAttributesCache.set('ol-samlError', {
     translatedMessage: 'There was an Error',
     translatedMessage: 'There was an Error',
@@ -38,7 +38,7 @@ export const Error = (args: any) => {
   return <SSOAlert />
   return <SSOAlert />
 }
 }
 
 
-export const ErrorTranslated = (args: any) => {
+export const ErrorTranslated = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-samlError', {
   window.metaAttributesCache.set('ol-samlError', {
     translatedMessage: 'Translated Error Message',
     translatedMessage: 'Translated Error Message',
@@ -47,7 +47,7 @@ export const ErrorTranslated = (args: any) => {
   return <SSOAlert />
   return <SSOAlert />
 }
 }
 
 
-export const ErrorWithTryAgain = (args: any) => {
+export const ErrorWithTryAgain = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-samlError', {
   window.metaAttributesCache.set('ol-samlError', {
     message: 'There was an Error',
     message: 'There was an Error',

+ 25 - 17
services/web/test/frontend/features/settings/components/account-info-section.test.tsx

@@ -52,11 +52,13 @@ describe('<AccountInfoSection />', function () {
       })
       })
     )
     )
     expect(updateMock.called()).to.be.true
     expect(updateMock.called()).to.be.true
-    expect(JSON.parse(updateMock.lastCall()[1].body as string)).to.deep.equal({
-      email: 'john@watson.co.uk',
-      first_name: 'John',
-      last_name: 'Watson',
-    })
+    expect(JSON.parse(updateMock.lastCall()![1]!.body as string)).to.deep.equal(
+      {
+        email: 'john@watson.co.uk',
+        first_name: 'John',
+        last_name: 'Watson',
+      }
+    )
   })
   })
 
 
   it('disables button on invalid email', async function () {
   it('disables button on invalid email', async function () {
@@ -77,7 +79,7 @@ describe('<AccountInfoSection />', function () {
   })
   })
 
 
   it('shows inflight state and success message', async function () {
   it('shows inflight state and success message', async function () {
-    let finishUpdateCall
+    let finishUpdateCall: (value: any) => void = () => {}
     fetchMock.post(
     fetchMock.post(
       '/user/settings',
       '/user/settings',
       new Promise(resolve => (finishUpdateCall = resolve))
       new Promise(resolve => (finishUpdateCall = resolve))
@@ -155,10 +157,12 @@ describe('<AccountInfoSection />', function () {
         name: 'Update',
         name: 'Update',
       })
       })
     )
     )
-    expect(JSON.parse(updateMock.lastCall()[1].body as string)).to.deep.equal({
-      first_name: 'Sherlock',
-      last_name: 'Holmes',
-    })
+    expect(JSON.parse(updateMock.lastCall()![1]!.body as string)).to.deep.equal(
+      {
+        first_name: 'Sherlock',
+        last_name: 'Holmes',
+      }
+    )
   })
   })
 
 
   it('disables email input', async function () {
   it('disables email input', async function () {
@@ -184,10 +188,12 @@ describe('<AccountInfoSection />', function () {
         name: 'Update',
         name: 'Update',
       })
       })
     )
     )
-    expect(JSON.parse(updateMock.lastCall()[1].body as string)).to.deep.equal({
-      first_name: 'Sherlock',
-      last_name: 'Holmes',
-    })
+    expect(JSON.parse(updateMock.lastCall()![1]!.body as string)).to.deep.equal(
+      {
+        first_name: 'Sherlock',
+        last_name: 'Holmes',
+      }
+    )
   })
   })
 
 
   it('disables names input', async function () {
   it('disables names input', async function () {
@@ -210,8 +216,10 @@ describe('<AccountInfoSection />', function () {
         name: 'Update',
         name: 'Update',
       })
       })
     )
     )
-    expect(JSON.parse(updateMock.lastCall()[1].body as string)).to.deep.equal({
-      email: 'sherlock@holmes.co.uk',
-    })
+    expect(JSON.parse(updateMock.lastCall()![1]!.body as string)).to.deep.equal(
+      {
+        email: 'sherlock@holmes.co.uk',
+      }
+    )
   })
   })
 })
 })

+ 3 - 3
services/web/test/frontend/features/settings/components/emails/add-email-input.test.tsx

@@ -40,8 +40,8 @@ describe('<AddEmailInput/>', function () {
   })
   })
 
 
   describe('when typing text that does not contain any potential domain match', function () {
   describe('when typing text that does not contain any potential domain match', function () {
-    let onChangeStub
-    let handleAddNewEmailStub
+    let onChangeStub: sinon.SinonStub
+    let handleAddNewEmailStub: sinon.SinonStub
 
 
     beforeEach(function () {
     beforeEach(function () {
       fetchMock.get('express:/institutions/domains', 200)
       fetchMock.get('express:/institutions/domains', 200)
@@ -94,7 +94,7 @@ describe('<AddEmailInput/>', function () {
   })
   })
 
 
   describe('when typing text that contains a potential domain match', function () {
   describe('when typing text that contains a potential domain match', function () {
-    let onChangeStub
+    let onChangeStub: sinon.SinonStub
 
 
     beforeEach(function () {
     beforeEach(function () {
       onChangeStub = sinon.stub()
       onChangeStub = sinon.stub()

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

@@ -9,6 +9,7 @@ import {
 } from '../../fixtures/test-user-email-data'
 } from '../../fixtures/test-user-email-data'
 import { UserEmailData } from '../../../../../../types/user-email'
 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'
 
 
 function renderEmailsRow(data: UserEmailData) {
 function renderEmailsRow(data: UserEmailData) {
   return render(
   return render(
@@ -21,7 +22,7 @@ function renderEmailsRow(data: UserEmailData) {
 function getByTextContent(text: string) {
 function getByTextContent(text: string) {
   return screen.getAllByText(
   return screen.getAllByText(
     (content, node) =>
     (content, node) =>
-      content === text || node.children[0]?.textContent === text
+      content === text || node?.children[0]?.textContent === text
   )
   )
 }
 }
 
 
@@ -65,7 +66,7 @@ describe('<EmailsRow/>', function () {
   })
   })
 
 
   describe('with email data affiliated to an institution with SSO available', function () {
   describe('with email data affiliated to an institution with SSO available', function () {
-    let affiliatedEmail: UserEmailData
+    let affiliatedEmail: UserEmailData & { affiliation: Affiliation }
 
 
     beforeEach(function () {
     beforeEach(function () {
       window.metaAttributesCache.get('ol-ExposedSettings').hasSamlFeature = true
       window.metaAttributesCache.get('ol-ExposedSettings').hasSamlFeature = true

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

@@ -18,7 +18,7 @@ const userEmailData: UserEmailData = {
   default: false,
   default: false,
 }
 }
 
 
-const userEmailData2: UserEmailData = {
+const userEmailData2: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
   affiliation: {
     inReconfirmNotificationPeriod: false,
     inReconfirmNotificationPeriod: false,
     institution: {
     institution: {
@@ -91,7 +91,7 @@ describe('email actions - make primary', function () {
         name: /make primary/i,
         name: /make primary/i,
       })) as HTMLButtonElement
       })) as HTMLButtonElement
 
 
-      userEvent.hover(button.parentElement)
+      userEvent.hover(button.parentElement!)
 
 
       screen.getByText(
       screen.getByText(
         /Please confirm your affiliation before making this the primary/i
         /Please confirm your affiliation before making this the primary/i
@@ -108,7 +108,7 @@ describe('email actions - make primary', function () {
         name: /make primary/i,
         name: /make primary/i,
       })) as HTMLButtonElement
       })) as HTMLButtonElement
 
 
-      userEvent.hover(button.parentElement)
+      userEvent.hover(button.parentElement!)
 
 
       screen.getByText('Make this the primary email, used to log in', {
       screen.getByText('Make this the primary email, used to log in', {
         exact: false,
         exact: false,
@@ -149,7 +149,7 @@ describe('email actions - make primary', function () {
         name: /make primary/i,
         name: /make primary/i,
       })) as HTMLButtonElement[]
       })) as HTMLButtonElement[]
 
 
-      userEvent.hover(buttons[1].parentElement)
+      userEvent.hover(buttons[1].parentElement!)
 
 
       screen.getByText(
       screen.getByText(
         'Please confirm your email by linking to your institutional account before making it the primary email',
         'Please confirm your email by linking to your institutional account before making it the primary email',

+ 14 - 11
services/web/test/frontend/features/settings/components/emails/emails-section-add-new-email.test.tsx

@@ -9,8 +9,9 @@ import EmailsSection from '../../../../../../frontend/js/features/settings/compo
 import { expect } from 'chai'
 import { expect } from 'chai'
 import fetchMock from 'fetch-mock'
 import fetchMock from 'fetch-mock'
 import { UserEmailData } from '../../../../../../types/user-email'
 import { UserEmailData } from '../../../../../../types/user-email'
+import { Affiliation } from '../../../../../../types/affiliation'
 
 
-const userEmailData: UserEmailData = {
+const userEmailData: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
   affiliation: {
     cachedConfirmedAt: null,
     cachedConfirmedAt: null,
     cachedEntitlement: null,
     cachedEntitlement: null,
@@ -307,7 +308,7 @@ describe('<EmailsSection />', function () {
     )
     )
 
 
     const roleInput = screen.getByRole('textbox', { name: /role/i })
     const roleInput = screen.getByRole('textbox', { name: /role/i })
-    await userEvent.type(roleInput, userEmailData.affiliation.role)
+    await userEvent.type(roleInput, userEmailData.affiliation.role!)
     const departmentInput = screen.getByRole('textbox', { name: /department/i })
     const departmentInput = screen.getByRole('textbox', { name: /department/i })
     await userEvent.click(departmentInput)
     await userEvent.click(departmentInput)
     await userEvent.click(screen.getByText(customDepartment))
     await userEvent.click(screen.getByText(customDepartment))
@@ -343,7 +344,7 @@ describe('<EmailsSection />', function () {
 
 
     screen.getByText(userEmailData.email)
     screen.getByText(userEmailData.email)
     screen.getByText(userEmailData.affiliation.institution.name)
     screen.getByText(userEmailData.affiliation.institution.name)
-    screen.getByText(userEmailData.affiliation.role, { exact: false })
+    screen.getByText(userEmailData.affiliation.role!, { exact: false })
     screen.getByText(customDepartment, { exact: false })
     screen.getByText(customDepartment, { exact: false })
   })
   })
 
 
@@ -450,9 +451,9 @@ describe('<EmailsSection />', function () {
     await userEvent.type(universityInput, newUniversity)
     await userEvent.type(universityInput, newUniversity)
 
 
     const roleInput = screen.getByRole('textbox', { name: /role/i })
     const roleInput = screen.getByRole('textbox', { name: /role/i })
-    await userEvent.type(roleInput, userEmailData.affiliation.role)
+    await userEvent.type(roleInput, userEmailData.affiliation.role!)
     const departmentInput = screen.getByRole('textbox', { name: /department/i })
     const departmentInput = screen.getByRole('textbox', { name: /department/i })
-    await userEvent.type(departmentInput, userEmailData.affiliation.department)
+    await userEvent.type(departmentInput, userEmailData.affiliation.department!)
 
 
     const userEmailDataCopy = {
     const userEmailDataCopy = {
       ...userEmailData,
       ...userEmailData,
@@ -489,8 +490,8 @@ describe('<EmailsSection />', function () {
 
 
     screen.getByText(userEmailData.email)
     screen.getByText(userEmailData.email)
     screen.getByText(newUniversity)
     screen.getByText(newUniversity)
-    screen.getByText(userEmailData.affiliation.role, { exact: false })
-    screen.getByText(userEmailData.affiliation.department, { exact: false })
+    screen.getByText(userEmailData.affiliation.role!, { exact: false })
+    screen.getByText(userEmailData.affiliation.department!, { exact: false })
   })
   })
 
 
   it('shows country, university, role and department fields based on whether `change` was clicked or not', async function () {
   it('shows country, university, role and department fields based on whether `change` was clicked or not', async function () {
@@ -621,11 +622,11 @@ describe('<EmailsSection />', function () {
 
 
     await userEvent.type(
     await userEvent.type(
       screen.getByRole('textbox', { name: /role/i }),
       screen.getByRole('textbox', { name: /role/i }),
-      userEmailData.affiliation.role
+      userEmailData.affiliation.role!
     )
     )
     await userEvent.type(
     await userEvent.type(
       screen.getByRole('textbox', { name: /department/i }),
       screen.getByRole('textbox', { name: /department/i }),
-      userEmailData.affiliation.department
+      userEmailData.affiliation.department!
     )
     )
     await userEvent.click(
     await userEvent.click(
       screen.getByRole('button', {
       screen.getByRole('button', {
@@ -639,7 +640,9 @@ describe('<EmailsSection />', function () {
     screen.getByText(userEmailDataCopy.affiliation.institution.name, {
     screen.getByText(userEmailDataCopy.affiliation.institution.name, {
       exact: false,
       exact: false,
     })
     })
-    screen.getByText(userEmailDataCopy.affiliation.role, { exact: false })
-    screen.getByText(userEmailDataCopy.affiliation.department, { exact: false })
+    screen.getByText(userEmailDataCopy.affiliation.role!, { exact: false })
+    screen.getByText(userEmailDataCopy.affiliation.department!, {
+      exact: false,
+    })
   })
   })
 })
 })

+ 5 - 4
services/web/test/frontend/features/settings/components/emails/emails-section-institution-and-role.test.tsx

@@ -10,8 +10,9 @@ import fetchMock from 'fetch-mock'
 import InstitutionAndRole from '../../../../../../frontend/js/features/settings/components/emails/institution-and-role'
 import InstitutionAndRole from '../../../../../../frontend/js/features/settings/components/emails/institution-and-role'
 import { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
 import { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
 import EmailsSection from '../../../../../../frontend/js/features/settings/components/emails-section'
 import EmailsSection from '../../../../../../frontend/js/features/settings/components/emails-section'
+import { Affiliation } from '../../../../../../types/affiliation'
 
 
-const userData1: UserEmailData = {
+const userData1: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
   affiliation: {
     cachedConfirmedAt: null,
     cachedConfirmedAt: null,
     cachedEntitlement: null,
     cachedEntitlement: null,
@@ -41,7 +42,7 @@ const userData1: UserEmailData = {
   default: true,
   default: true,
 }
 }
 
 
-const userData2: UserEmailData = {
+const userData2: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
   affiliation: {
     cachedConfirmedAt: null,
     cachedConfirmedAt: null,
     cachedEntitlement: null,
     cachedEntitlement: null,
@@ -110,8 +111,8 @@ describe('user role and institution', function () {
     screen.getByText(userEmailData.affiliation.institution.name, {
     screen.getByText(userEmailData.affiliation.institution.name, {
       exact: false,
       exact: false,
     })
     })
-    screen.getByText(userEmailData.affiliation.department, { exact: false })
-    screen.getByText(userEmailData.affiliation.role, { exact: false })
+    screen.getByText(userEmailData.affiliation.department!, { exact: false })
+    screen.getByText(userEmailData.affiliation.role!, { exact: false })
     screen.getByRole('button', { name: /change/i })
     screen.getByRole('button', { name: /change/i })
     expect(screen.queryByRole('button', { name: /add role and department/i }))
     expect(screen.queryByRole('button', { name: /add role and department/i }))
       .to.not.exist
       .to.not.exist

+ 6 - 6
services/web/test/frontend/features/settings/components/leave/modal-form.test.tsx

@@ -1,7 +1,7 @@
 import { expect } from 'chai'
 import { expect } from 'chai'
 import sinon from 'sinon'
 import sinon from 'sinon'
 import { fireEvent, screen, render, waitFor } from '@testing-library/react'
 import { fireEvent, screen, render, waitFor } from '@testing-library/react'
-import fetchMock from 'fetch-mock'
+import fetchMock, { FetchMockStatic } from 'fetch-mock'
 
 
 import LeaveModalForm from '../../../../../../frontend/js/features/settings/components/leave/modal-form'
 import LeaveModalForm from '../../../../../../frontend/js/features/settings/components/leave/modal-form'
 
 
@@ -40,7 +40,7 @@ describe('<LeaveModalForm />', function () {
 
 
     const setIsFormValidCalls = setIsFormValid.getCalls()
     const setIsFormValidCalls = setIsFormValid.getCalls()
     const lastSetIsFormValidCall = setIsFormValidCalls.pop()
     const lastSetIsFormValidCall = setIsFormValidCalls.pop()
-    expect(lastSetIsFormValidCall.args[0]).to.be.true
+    expect(lastSetIsFormValidCall!.args[0]).to.be.true
 
 
     for (const setIsFormValidCall of setIsFormValidCalls) {
     for (const setIsFormValidCall of setIsFormValidCalls) {
       expect(setIsFormValidCall.args[0]).to.be.false
       expect(setIsFormValidCall.args[0]).to.be.false
@@ -48,10 +48,10 @@ describe('<LeaveModalForm />', function () {
   })
   })
 
 
   describe('submits', async function () {
   describe('submits', async function () {
-    let setInFlight
-    let setIsFormValid
-    let deleteMock
-    let locationStub
+    let setInFlight: sinon.SinonStub
+    let setIsFormValid: sinon.SinonStub
+    let deleteMock: FetchMockStatic
+    let locationStub: sinon.SinonStub
     const originalLocation = window.location
     const originalLocation = window.location
 
 
     beforeEach(function () {
     beforeEach(function () {

+ 1 - 1
services/web/test/frontend/features/settings/components/leavers-survey-alert.test.tsx

@@ -56,7 +56,7 @@ describe('<LeaversSurveyAlert/>', function () {
   })
   })
 
 
   describe('event tracking', function () {
   describe('event tracking', function () {
-    let sendMBSpy
+    let sendMBSpy: sinon.SinonSpy
 
 
     beforeEach(function () {
     beforeEach(function () {
       sendMBSpy = sinon.spy(eventTracking, 'sendMB')
       sendMBSpy = sinon.spy(eventTracking, 'sendMB')

+ 2 - 2
services/web/test/frontend/features/settings/components/linking/sso-widget.test.tsx

@@ -33,7 +33,7 @@ describe('<SSOLinkingWidget />', function () {
   })
   })
 
 
   describe('when linked', function () {
   describe('when linked', function () {
-    let unlinkFunction
+    let unlinkFunction: sinon.SinonStub
 
 
     beforeEach(function () {
     beforeEach(function () {
       unlinkFunction = sinon.stub()
       unlinkFunction = sinon.stub()
@@ -69,7 +69,7 @@ describe('<SSOLinkingWidget />', function () {
   })
   })
 
 
   describe('unlinking an account', function () {
   describe('unlinking an account', function () {
-    let confirmBtn, unlinkFunction
+    let confirmBtn: HTMLElement, unlinkFunction: sinon.SinonStub
 
 
     beforeEach(function () {
     beforeEach(function () {
       unlinkFunction = sinon.stub()
       unlinkFunction = sinon.stub()

+ 8 - 6
services/web/test/frontend/features/settings/components/password-section.test.tsx

@@ -47,11 +47,13 @@ describe('<PasswordSection />', function () {
     submitValidForm()
     submitValidForm()
 
 
     expect(updateMock.called()).to.be.true
     expect(updateMock.called()).to.be.true
-    expect(JSON.parse(updateMock.lastCall()[1].body as string)).to.deep.equal({
-      currentPassword: 'foobar',
-      newPassword1: 'barbaz',
-      newPassword2: 'barbaz',
-    })
+    expect(JSON.parse(updateMock.lastCall()![1]!.body as string)).to.deep.equal(
+      {
+        currentPassword: 'foobar',
+        newPassword1: 'barbaz',
+        newPassword2: 'barbaz',
+      }
+    )
   })
   })
 
 
   it('disables button on invalid form', async function () {
   it('disables button on invalid form', async function () {
@@ -136,7 +138,7 @@ describe('<PasswordSection />', function () {
   })
   })
 
 
   it('shows inflight state and success message', async function () {
   it('shows inflight state and success message', async function () {
-    let finishUpdateCall
+    let finishUpdateCall: (value: any) => void = () => {}
     fetchMock.post(
     fetchMock.post(
       '/user/password/update',
       '/user/password/update',
       new Promise(resolve => (finishUpdateCall = resolve))
       new Promise(resolve => (finishUpdateCall = resolve))

+ 1 - 1
services/web/test/frontend/features/settings/components/root.test.tsx

@@ -5,7 +5,7 @@ import * as eventTracking from '../../../../../frontend/js/infrastructure/event-
 import SettingsPageRoot from '../../../../../frontend/js/features/settings/components/root'
 import SettingsPageRoot from '../../../../../frontend/js/features/settings/components/root'
 
 
 describe('<SettingsPageRoot />', function () {
 describe('<SettingsPageRoot />', function () {
-  let sendMBSpy
+  let sendMBSpy: sinon.SinonSpy
   beforeEach(function () {
   beforeEach(function () {
     window.metaAttributesCache = new Map()
     window.metaAttributesCache = new Map()
     window.metaAttributesCache.set('ol-usersEmail', 'foo@bar.com')
     window.metaAttributesCache.set('ol-usersEmail', 'foo@bar.com')

+ 2 - 2
services/web/test/frontend/features/settings/context/user-email-context.test.tsx

@@ -202,10 +202,10 @@ describe('UserEmailContext', function () {
           'new department'
           'new department'
         )
         )
         expect(
         expect(
-          result.current.state.data.byId['foo@overleaf.com'].affiliation.role
+          result.current.state.data.byId['foo@overleaf.com'].affiliation!.role
         ).to.equal('new role')
         ).to.equal('new role')
         expect(
         expect(
-          result.current.state.data.byId['foo@overleaf.com'].affiliation
+          result.current.state.data.byId['foo@overleaf.com'].affiliation!
             .department
             .department
         ).to.equal('new department')
         ).to.equal('new department')
       })
       })

+ 4 - 1
services/web/test/frontend/features/settings/fixtures/test-user-email-data.ts

@@ -1,4 +1,5 @@
 import { UserEmailData } from '../../../../../types/user-email'
 import { UserEmailData } from '../../../../../types/user-email'
+import { Affiliation } from '../../../../../types/affiliation'
 
 
 export const confirmedUserData: UserEmailData = {
 export const confirmedUserData: UserEmailData = {
   confirmedAt: '2022-03-10T10:59:44.139Z',
   confirmedAt: '2022-03-10T10:59:44.139Z',
@@ -11,7 +12,9 @@ export const unconfirmedUserData: UserEmailData = {
   default: false,
   default: false,
 }
 }
 
 
-export const professionalUserData: UserEmailData = {
+export const professionalUserData: UserEmailData & {
+  affiliation: Affiliation
+} = {
   affiliation: {
   affiliation: {
     cachedConfirmedAt: null,
     cachedConfirmedAt: null,
     cachedEntitlement: null,
     cachedEntitlement: null,

+ 1 - 0
services/web/types/window.ts

@@ -19,5 +19,6 @@ declare global {
     project_id: string
     project_id: string
     gitBridgePublicBaseUrl: string
     gitBridgePublicBaseUrl: string
     _ide: Record<string, unknown>
     _ide: Record<string, unknown>
+    isRestrictedTokenMember: boolean
   }
   }
 }
 }