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
 }
 
-const wordsSortFunction = (a, b) => a.localeCompare(b)
+const wordsSortFunction = (a: string, b: string) => a.localeCompare(b)
 
 export default function DictionaryModalContent({
   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 symbols = isMac
+const symbols: Record<string, string> = isMac
   ? {
       CommandOrControl: '⌘',
       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) {
           setInputValueAndResetSuggestion()
-
           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 { Institution } from '../../../../../types/institution'
 
-export const ssoAvailableForDomain = (domain: DomainInfo | null) => {
+export const ssoAvailableForDomain = (
+  domain: DomainInfo | null
+): domain is DomainInfo => {
   const { hasSamlBeta, hasSamlFeature } = getMeta(
     'ol-ExposedSettings'
   ) as ExposedSettings
@@ -13,7 +15,7 @@ export const ssoAvailableForDomain = (domain: DomainInfo | null) => {
   if (domain.university.ssoEnabled) {
     return true
   }
-  return hasSamlBeta && domain.university.ssoBeta
+  return Boolean(hasSamlBeta && domain.university.ssoBeta)
 }
 
 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 { SSOAlert } from '../../js/features/settings/components/emails/sso-alert'
 
-export const Info = (args: any) => {
+export const Info = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-institutionLinked', {
     universityName: 'Overleaf University',
@@ -9,7 +9,7 @@ export const Info = (args: any) => {
   return <SSOAlert />
 }
 
-export const InfoWithEntitlement = (args: any) => {
+export const InfoWithEntitlement = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-institutionLinked', {
     universityName: 'Overleaf University',
@@ -18,7 +18,7 @@ export const InfoWithEntitlement = (args: any) => {
   return <SSOAlert />
 }
 
-export const NonCanonicalEmail = (args: any) => {
+export const NonCanonicalEmail = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-institutionLinked', {
     universityName: 'Overleaf University',
@@ -30,7 +30,7 @@ export const NonCanonicalEmail = (args: any) => {
   return <SSOAlert />
 }
 
-export const Error = (args: any) => {
+export const Error = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-samlError', {
     translatedMessage: 'There was an Error',
@@ -38,7 +38,7 @@ export const Error = (args: any) => {
   return <SSOAlert />
 }
 
-export const ErrorTranslated = (args: any) => {
+export const ErrorTranslated = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-samlError', {
     translatedMessage: 'Translated Error Message',
@@ -47,7 +47,7 @@ export const ErrorTranslated = (args: any) => {
   return <SSOAlert />
 }
 
-export const ErrorWithTryAgain = (args: any) => {
+export const ErrorWithTryAgain = () => {
   window.metaAttributesCache = new Map()
   window.metaAttributesCache.set('ol-samlError', {
     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(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 () {
@@ -77,7 +79,7 @@ describe('<AccountInfoSection />', function () {
   })
 
   it('shows inflight state and success message', async function () {
-    let finishUpdateCall
+    let finishUpdateCall: (value: any) => void = () => {}
     fetchMock.post(
       '/user/settings',
       new Promise(resolve => (finishUpdateCall = resolve))
@@ -155,10 +157,12 @@ describe('<AccountInfoSection />', function () {
         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 () {
@@ -184,10 +188,12 @@ describe('<AccountInfoSection />', function () {
         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 () {
@@ -210,8 +216,10 @@ describe('<AccountInfoSection />', function () {
         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 () {
-    let onChangeStub
-    let handleAddNewEmailStub
+    let onChangeStub: sinon.SinonStub
+    let handleAddNewEmailStub: sinon.SinonStub
 
     beforeEach(function () {
       fetchMock.get('express:/institutions/domains', 200)
@@ -94,7 +94,7 @@ describe('<AddEmailInput/>', function () {
   })
 
   describe('when typing text that contains a potential domain match', function () {
-    let onChangeStub
+    let onChangeStub: sinon.SinonStub
 
     beforeEach(function () {
       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'
 import { UserEmailData } from '../../../../../../types/user-email'
 import { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
+import { Affiliation } from '../../../../../../types/affiliation'
 
 function renderEmailsRow(data: UserEmailData) {
   return render(
@@ -21,7 +22,7 @@ function renderEmailsRow(data: UserEmailData) {
 function getByTextContent(text: string) {
   return screen.getAllByText(
     (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 () {
-    let affiliatedEmail: UserEmailData
+    let affiliatedEmail: UserEmailData & { affiliation: Affiliation }
 
     beforeEach(function () {
       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,
 }
 
-const userEmailData2: UserEmailData = {
+const userEmailData2: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
     inReconfirmNotificationPeriod: false,
     institution: {
@@ -91,7 +91,7 @@ describe('email actions - make primary', function () {
         name: /make primary/i,
       })) as HTMLButtonElement
 
-      userEvent.hover(button.parentElement)
+      userEvent.hover(button.parentElement!)
 
       screen.getByText(
         /Please confirm your affiliation before making this the primary/i
@@ -108,7 +108,7 @@ describe('email actions - make primary', function () {
         name: /make primary/i,
       })) as HTMLButtonElement
 
-      userEvent.hover(button.parentElement)
+      userEvent.hover(button.parentElement!)
 
       screen.getByText('Make this the primary email, used to log in', {
         exact: false,
@@ -149,7 +149,7 @@ describe('email actions - make primary', function () {
         name: /make primary/i,
       })) as HTMLButtonElement[]
 
-      userEvent.hover(buttons[1].parentElement)
+      userEvent.hover(buttons[1].parentElement!)
 
       screen.getByText(
         '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 fetchMock from 'fetch-mock'
 import { UserEmailData } from '../../../../../../types/user-email'
+import { Affiliation } from '../../../../../../types/affiliation'
 
-const userEmailData: UserEmailData = {
+const userEmailData: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
     cachedConfirmedAt: null,
     cachedEntitlement: null,
@@ -307,7 +308,7 @@ describe('<EmailsSection />', function () {
     )
 
     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 })
     await userEvent.click(departmentInput)
     await userEvent.click(screen.getByText(customDepartment))
@@ -343,7 +344,7 @@ describe('<EmailsSection />', function () {
 
     screen.getByText(userEmailData.email)
     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 })
   })
 
@@ -450,9 +451,9 @@ describe('<EmailsSection />', function () {
     await userEvent.type(universityInput, newUniversity)
 
     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 })
-    await userEvent.type(departmentInput, userEmailData.affiliation.department)
+    await userEvent.type(departmentInput, userEmailData.affiliation.department!)
 
     const userEmailDataCopy = {
       ...userEmailData,
@@ -489,8 +490,8 @@ describe('<EmailsSection />', function () {
 
     screen.getByText(userEmailData.email)
     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 () {
@@ -621,11 +622,11 @@ describe('<EmailsSection />', function () {
 
     await userEvent.type(
       screen.getByRole('textbox', { name: /role/i }),
-      userEmailData.affiliation.role
+      userEmailData.affiliation.role!
     )
     await userEvent.type(
       screen.getByRole('textbox', { name: /department/i }),
-      userEmailData.affiliation.department
+      userEmailData.affiliation.department!
     )
     await userEvent.click(
       screen.getByRole('button', {
@@ -639,7 +640,9 @@ describe('<EmailsSection />', function () {
     screen.getByText(userEmailDataCopy.affiliation.institution.name, {
       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 { UserEmailsProvider } from '../../../../../../frontend/js/features/settings/context/user-email-context'
 import EmailsSection from '../../../../../../frontend/js/features/settings/components/emails-section'
+import { Affiliation } from '../../../../../../types/affiliation'
 
-const userData1: UserEmailData = {
+const userData1: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
     cachedConfirmedAt: null,
     cachedEntitlement: null,
@@ -41,7 +42,7 @@ const userData1: UserEmailData = {
   default: true,
 }
 
-const userData2: UserEmailData = {
+const userData2: UserEmailData & { affiliation: Affiliation } = {
   affiliation: {
     cachedConfirmedAt: null,
     cachedEntitlement: null,
@@ -110,8 +111,8 @@ describe('user role and institution', function () {
     screen.getByText(userEmailData.affiliation.institution.name, {
       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 })
     expect(screen.queryByRole('button', { name: /add role and department/i }))
       .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 sinon from 'sinon'
 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'
 
@@ -40,7 +40,7 @@ describe('<LeaveModalForm />', function () {
 
     const setIsFormValidCalls = setIsFormValid.getCalls()
     const lastSetIsFormValidCall = setIsFormValidCalls.pop()
-    expect(lastSetIsFormValidCall.args[0]).to.be.true
+    expect(lastSetIsFormValidCall!.args[0]).to.be.true
 
     for (const setIsFormValidCall of setIsFormValidCalls) {
       expect(setIsFormValidCall.args[0]).to.be.false
@@ -48,10 +48,10 @@ describe('<LeaveModalForm />', 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
 
     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 () {
-    let sendMBSpy
+    let sendMBSpy: sinon.SinonSpy
 
     beforeEach(function () {
       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 () {
-    let unlinkFunction
+    let unlinkFunction: sinon.SinonStub
 
     beforeEach(function () {
       unlinkFunction = sinon.stub()
@@ -69,7 +69,7 @@ describe('<SSOLinkingWidget />', function () {
   })
 
   describe('unlinking an account', function () {
-    let confirmBtn, unlinkFunction
+    let confirmBtn: HTMLElement, unlinkFunction: sinon.SinonStub
 
     beforeEach(function () {
       unlinkFunction = sinon.stub()

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

@@ -47,11 +47,13 @@ describe('<PasswordSection />', function () {
     submitValidForm()
 
     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 () {
@@ -136,7 +138,7 @@ describe('<PasswordSection />', function () {
   })
 
   it('shows inflight state and success message', async function () {
-    let finishUpdateCall
+    let finishUpdateCall: (value: any) => void = () => {}
     fetchMock.post(
       '/user/password/update',
       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'
 
 describe('<SettingsPageRoot />', function () {
-  let sendMBSpy
+  let sendMBSpy: sinon.SinonSpy
   beforeEach(function () {
     window.metaAttributesCache = new Map()
     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'
         )
         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')
         expect(
-          result.current.state.data.byId['foo@overleaf.com'].affiliation
+          result.current.state.data.byId['foo@overleaf.com'].affiliation!
             .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 { Affiliation } from '../../../../../types/affiliation'
 
 export const confirmedUserData: UserEmailData = {
   confirmedAt: '2022-03-10T10:59:44.139Z',
@@ -11,7 +12,9 @@ export const unconfirmedUserData: UserEmailData = {
   default: false,
 }
 
-export const professionalUserData: UserEmailData = {
+export const professionalUserData: UserEmailData & {
+  affiliation: Affiliation
+} = {
   affiliation: {
     cachedConfirmedAt: null,
     cachedEntitlement: null,

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

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