From 12b3dcdfdc033edba6493794f8a808b6ca41428b Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Fri, 4 Jul 2025 12:50:45 +0200 Subject: [PATCH] [web] Convert EditorProviders and some test files to Typescript (#26512) * Rename files to tsx * Update types * Remove props that aren't typed * Add `LayoutContextOwnStates` * Use `LayoutContextOwnStates` * Ignore ts errors about `SocketIOMock` * Address comments: remove `satisfies`, update `BroadcastChannel` fixture * Add types to `makeEditorOpenDocProvider`. Update `openDocId`->`currentDocumentId` * misc. * Type sockets as `SocketIOMock & Socket` * Fix remaining typing errors * Fix type of `ideReactContextValue` GitOrigin-RevId: 2734ac707517d56c452b0bf06ea3438f947a64be --- .../components/toolbar-header.tsx | 44 +++++---- .../context/editor-open-doc-context.tsx | 9 +- .../ide-react/context/react-context-root.tsx | 4 +- .../js/shared/context/layout-context.tsx | 23 +++-- .../pdf-preview/pdf-synctex-controls.spec.tsx | 3 +- ...n.test.jsx => chat-toggle-button.test.tsx} | 0 ...st.jsx => layout-dropdown-button.test.tsx} | 12 ++- ....test.jsx => online-users-widget.test.tsx} | 16 +++- ...x => project-name-editable-label.test.tsx} | 0 ...eader.test.jsx => toolbar-header.test.tsx} | 19 ++-- .../components/file-tree-root.spec.tsx | 5 +- .../file-tree/flows/create-folder.spec.tsx | 5 +- .../file-tree/flows/delete-entity.spec.tsx | 13 +-- .../file-tree/flows/rename-entity.spec.tsx | 5 +- ...tor-providers.jsx => editor-providers.tsx} | 95 ++++++++++++++----- .../frontend/helpers/render-with-context.jsx | 20 ---- .../frontend/helpers/render-with-context.tsx | 20 ++++ 17 files changed, 186 insertions(+), 107 deletions(-) rename services/web/test/frontend/features/editor-navigation-toolbar/components/{chat-toggle-button.test.jsx => chat-toggle-button.test.tsx} (100%) rename services/web/test/frontend/features/editor-navigation-toolbar/components/{layout-dropdown-button.test.jsx => layout-dropdown-button.test.tsx} (94%) rename services/web/test/frontend/features/editor-navigation-toolbar/components/{online-users-widget.test.jsx => online-users-widget.test.tsx} (89%) rename services/web/test/frontend/features/editor-navigation-toolbar/components/{project-name-editable-label.test.jsx => project-name-editable-label.test.tsx} (100%) rename services/web/test/frontend/features/editor-navigation-toolbar/components/{toolbar-header.test.jsx => toolbar-header.test.tsx} (89%) rename services/web/test/frontend/helpers/{editor-providers.jsx => editor-providers.tsx} (79%) delete mode 100644 services/web/test/frontend/helpers/render-with-context.jsx create mode 100644 services/web/test/frontend/helpers/render-with-context.tsx diff --git a/services/web/frontend/js/features/editor-navigation-toolbar/components/toolbar-header.tsx b/services/web/frontend/js/features/editor-navigation-toolbar/components/toolbar-header.tsx index 87bcbc0aac..d116c9b881 100644 --- a/services/web/frontend/js/features/editor-navigation-toolbar/components/toolbar-header.tsx +++ b/services/web/frontend/js/features/editor-navigation-toolbar/components/toolbar-header.tsx @@ -39,6 +39,28 @@ const enableROMirrorOnClient = new URLSearchParams(window.location.search).get('ro-mirror-on-client') === 'enabled' +export type ToolbarHeaderProps = { + cobranding: Cobranding | undefined + onShowLeftMenuClick: () => void + chatIsOpen: boolean + toggleChatOpen: () => void + reviewPanelOpen: boolean + toggleReviewPanelOpen: (e: React.MouseEvent) => void + historyIsOpen: boolean + toggleHistoryOpen: () => void + unreadMessageCount: number + onlineUsers: OnlineUser[] + goToUser: (user: OnlineUser) => void + isRestrictedTokenMember: boolean | undefined + hasPublishPermissions: boolean + chatVisible: boolean + projectName: string + renameProject: (name: string) => void + hasRenamePermissions: boolean + openShareModal: () => void + trackChangesVisible: boolean | undefined +} + const ToolbarHeader = React.memo(function ToolbarHeader({ cobranding, onShowLeftMenuClick, @@ -59,27 +81,7 @@ const ToolbarHeader = React.memo(function ToolbarHeader({ hasRenamePermissions, openShareModal, trackChangesVisible, -}: { - cobranding: Cobranding | undefined - onShowLeftMenuClick: () => void - chatIsOpen: boolean - toggleChatOpen: () => void - reviewPanelOpen: boolean - toggleReviewPanelOpen: (e: React.MouseEvent) => void - historyIsOpen: boolean - toggleHistoryOpen: () => void - unreadMessageCount: number - onlineUsers: OnlineUser[] - goToUser: (user: OnlineUser) => void - isRestrictedTokenMember: boolean | undefined - hasPublishPermissions: boolean - chatVisible: boolean - projectName: string - renameProject: (name: string) => void - hasRenamePermissions: boolean - openShareModal: () => void - trackChangesVisible: boolean | undefined -}) { +}: ToolbarHeaderProps) { const chatEnabled = getMeta('ol-capabilities')?.includes('chat') const { t } = useTranslation() diff --git a/services/web/frontend/js/features/ide-react/context/editor-open-doc-context.tsx b/services/web/frontend/js/features/ide-react/context/editor-open-doc-context.tsx index dd4a470578..61de80dfde 100644 --- a/services/web/frontend/js/features/ide-react/context/editor-open-doc-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/editor-open-doc-context.tsx @@ -11,12 +11,15 @@ import { DocId } from '../../../../../types/project-settings' import useExposedState from '@/shared/hooks/use-exposed-state' import { DocumentContainer } from '@/features/ide-react/editor/document-container' -type EditorOpenDocContextValue = { +export interface EditorOpenDocContextState { currentDocumentId: DocId | null - setCurrentDocumentId: Dispatch> openDocName: string | null - setOpenDocName: Dispatch> currentDocument: DocumentContainer | null +} + +interface EditorOpenDocContextValue extends EditorOpenDocContextState { + setCurrentDocumentId: Dispatch> + setOpenDocName: Dispatch> setCurrentDocument: Dispatch> } diff --git a/services/web/frontend/js/features/ide-react/context/react-context-root.tsx b/services/web/frontend/js/features/ide-react/context/react-context-root.tsx index fdac20dc0f..99442eba5e 100644 --- a/services/web/frontend/js/features/ide-react/context/react-context-root.tsx +++ b/services/web/frontend/js/features/ide-react/context/react-context-root.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import React, { FC, PropsWithChildren } from 'react' import { ChatProvider } from '@/features/chat/context/chat-context' import { ConnectionProvider } from './connection-context' import { DetachCompileProvider } from '@/shared/context/detach-compile-context' @@ -32,7 +32,7 @@ import { CommandRegistryProvider } from './command-registry-context' export const ReactContextRoot: FC< React.PropsWithChildren<{ - providers?: Record + providers?: Record> }> > = ({ children, providers = {} }) => { const Providers = { diff --git a/services/web/frontend/js/shared/context/layout-context.tsx b/services/web/frontend/js/shared/context/layout-context.tsx index c59efae270..8dcc1fd8ff 100644 --- a/services/web/frontend/js/shared/context/layout-context.tsx +++ b/services/web/frontend/js/shared/context/layout-context.tsx @@ -25,37 +25,40 @@ import usePersistedState from '@/shared/hooks/use-persisted-state' export type IdeLayout = 'sideBySide' | 'flat' export type IdeView = 'editor' | 'file' | 'pdf' | 'history' -export type LayoutContextValue = { +export type LayoutContextOwnStates = { + view: IdeView | null + chatIsOpen: boolean + reviewPanelOpen: boolean + miniReviewPanelVisible: boolean + leftMenuShown: boolean + loadingStyleSheet: boolean + pdfLayout: IdeLayout + projectSearchIsOpen: boolean + openFile: BinaryFile | null +} + +export type LayoutContextValue = LayoutContextOwnStates & { reattach: () => void detach: () => void detachIsLinked: boolean detachRole: DetachRole changeLayout: (newLayout: IdeLayout, newView?: IdeView) => void - view: IdeView | null setView: (view: IdeView | null) => void - chatIsOpen: boolean setChatIsOpen: Dispatch> - reviewPanelOpen: boolean setReviewPanelOpen: Dispatch< SetStateAction > - miniReviewPanelVisible: boolean setMiniReviewPanelVisible: Dispatch< SetStateAction > - leftMenuShown: boolean setLeftMenuShown: Dispatch< SetStateAction > - loadingStyleSheet: boolean setLoadingStyleSheet: Dispatch< SetStateAction > - pdfLayout: IdeLayout pdfPreviewOpen: boolean - projectSearchIsOpen: boolean setProjectSearchIsOpen: Dispatch> - openFile: BinaryFile | null setOpenFile: Dispatch> } diff --git a/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx b/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx index f8c379c51e..3cba86d03d 100644 --- a/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx +++ b/services/web/test/frontend/components/pdf-preview/pdf-synctex-controls.spec.tsx @@ -80,6 +80,7 @@ function mockProviders() { return { EditorOpenDocProvider: makeEditorOpenDocProvider({ openDocName: 'main.tex', + currentDocumentId: null, currentDocument: { doc_id: 'test-doc', getSnapshot: () => 'some doc content', @@ -87,7 +88,7 @@ function mockProviders() { on: () => {}, off: () => {}, leaveAndCleanUpPromise: () => Promise.resolve(), - }, + } as any, }), } } diff --git a/services/web/test/frontend/features/editor-navigation-toolbar/components/chat-toggle-button.test.jsx b/services/web/test/frontend/features/editor-navigation-toolbar/components/chat-toggle-button.test.tsx similarity index 100% rename from services/web/test/frontend/features/editor-navigation-toolbar/components/chat-toggle-button.test.jsx rename to services/web/test/frontend/features/editor-navigation-toolbar/components/chat-toggle-button.test.tsx diff --git a/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx b/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.tsx similarity index 94% rename from services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx rename to services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.tsx index f8ac817c36..c3bd08ed89 100644 --- a/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx +++ b/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.tsx @@ -5,12 +5,13 @@ import { screen, waitFor } from '@testing-library/react' import LayoutDropdownButton from '../../../../../frontend/js/features/editor-navigation-toolbar/components/layout-dropdown-button' import { renderWithEditorContext } from '../../../helpers/render-with-context' import * as eventTracking from '@/infrastructure/event-tracking' +import type { LayoutContextOwnStates } from '@/shared/context/layout-context' describe('', function () { - let openStub - let sendMBSpy + let openStub: sinon.SinonStub + let sendMBSpy: sinon.SinonSpy - const defaultLayout = { + const defaultLayout: Partial = { pdfLayout: 'flat', view: 'pdf', chatIsOpen: false, @@ -161,9 +162,10 @@ describe('', function () { }) describe('on detach', async function () { - let originalBroadcastChannel + const originalBroadcastChannel = window.BroadcastChannel beforeEach(async function () { - window.BroadcastChannel = originalBroadcastChannel || true // ensure that window.BroadcastChannel is truthy + // @ts-expect-error + window.BroadcastChannel = true // ensure that window.BroadcastChannel is truthy renderWithEditorContext(, { layoutContext: { ...defaultLayout, view: 'editor' }, diff --git a/services/web/test/frontend/features/editor-navigation-toolbar/components/online-users-widget.test.jsx b/services/web/test/frontend/features/editor-navigation-toolbar/components/online-users-widget.test.tsx similarity index 89% rename from services/web/test/frontend/features/editor-navigation-toolbar/components/online-users-widget.test.jsx rename to services/web/test/frontend/features/editor-navigation-toolbar/components/online-users-widget.test.tsx index 96a8abe0aa..42c7a04502 100644 --- a/services/web/test/frontend/features/editor-navigation-toolbar/components/online-users-widget.test.jsx +++ b/services/web/test/frontend/features/editor-navigation-toolbar/components/online-users-widget.test.tsx @@ -8,12 +8,16 @@ describe('', function () { const defaultProps = { onlineUsers: [ { + id: 'test_user', user_id: 'test_user', name: 'test_user', + email: 'test_email', }, { + id: 'another_test_user', user_id: 'another_test_user', name: 'another_test_user', + email: 'another_test_email', }, ], goToUser: () => {}, @@ -44,8 +48,10 @@ describe('', function () { fireEvent.click(icon) expect(props.goToUser).to.be.calledWith({ - name: 'test_user', + id: 'test_user', user_id: 'test_user', + name: 'test_user', + email: 'test_email', }) }) }) @@ -55,12 +61,16 @@ describe('', function () { ...defaultProps, onlineUsers: defaultProps.onlineUsers.concat([ { + id: 'user_3', user_id: 'user_3', name: 'user_3', + email: 'user_3', }, { + id: 'user_4', user_id: 'user_4', name: 'user_4', + email: 'user_4', }, ]), } @@ -96,8 +106,10 @@ describe('', function () { fireEvent.click(icon) expect(testProps.goToUser).to.be.calledWith({ - name: 'user_3', + id: 'user_3', user_id: 'user_3', + name: 'user_3', + email: 'user_3', }) }) }) diff --git a/services/web/test/frontend/features/editor-navigation-toolbar/components/project-name-editable-label.test.jsx b/services/web/test/frontend/features/editor-navigation-toolbar/components/project-name-editable-label.test.tsx similarity index 100% rename from services/web/test/frontend/features/editor-navigation-toolbar/components/project-name-editable-label.test.jsx rename to services/web/test/frontend/features/editor-navigation-toolbar/components/project-name-editable-label.test.tsx diff --git a/services/web/test/frontend/features/editor-navigation-toolbar/components/toolbar-header.test.jsx b/services/web/test/frontend/features/editor-navigation-toolbar/components/toolbar-header.test.tsx similarity index 89% rename from services/web/test/frontend/features/editor-navigation-toolbar/components/toolbar-header.test.jsx rename to services/web/test/frontend/features/editor-navigation-toolbar/components/toolbar-header.test.tsx index be7894fc73..9b2217744e 100644 --- a/services/web/test/frontend/features/editor-navigation-toolbar/components/toolbar-header.test.jsx +++ b/services/web/test/frontend/features/editor-navigation-toolbar/components/toolbar-header.test.tsx @@ -1,11 +1,13 @@ import { expect } from 'chai' import { screen } from '@testing-library/react' -import ToolbarHeader from '../../../../../frontend/js/features/editor-navigation-toolbar/components/toolbar-header' +import ToolbarHeader, { + type ToolbarHeaderProps, +} from '../../../../../frontend/js/features/editor-navigation-toolbar/components/toolbar-header' import { renderWithEditorContext } from '../../../helpers/render-with-context' describe('', function () { - const defaultProps = { + const defaultProps: ToolbarHeaderProps = { onShowLeftMenuClick: () => {}, toggleChatOpen: () => {}, toggleReviewPanelOpen: () => {}, @@ -19,11 +21,12 @@ describe('', function () { hasPublishPermissions: true, chatVisible: true, trackChangesVisible: true, - handleChangeLayout: () => {}, - pdfLayout: 'sideBySide', - view: 'editor', - reattach: () => {}, - detach: () => {}, + cobranding: undefined, + isRestrictedTokenMember: false, + hasRenamePermissions: true, + historyIsOpen: false, + chatIsOpen: false, + reviewPanelOpen: false, } beforeEach(function () { @@ -40,6 +43,8 @@ describe('', function () { const props = { ...defaultProps, cobranding: { + brandId: 12, + brandVariationId: 12, brandVariationHomeUrl: 'http://cobranding', brandVariationName: 'variation', logoImgUrl: 'http://cobranding/logo', diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx b/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx index 6e56b1b6bb..0739f5a3d5 100644 --- a/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx +++ b/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx @@ -1,6 +1,7 @@ import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root' import { EditorProviders } from '../../../helpers/editor-providers' import { SocketIOMock } from '@/ide/connection/SocketIoShim' +import type { Socket } from '@/features/ide-react/connection/types/socket' describe('', function () { beforeEach(function () { @@ -245,9 +246,9 @@ describe('', function () { }) describe('when deselecting files', function () { - let socket: SocketIOMock + let socket: SocketIOMock & Socket beforeEach(function () { - socket = new SocketIOMock() + socket = new SocketIOMock() as any const rootFolder = [ { _id: 'root-folder-id', diff --git a/services/web/test/frontend/features/file-tree/flows/create-folder.spec.tsx b/services/web/test/frontend/features/file-tree/flows/create-folder.spec.tsx index a7ccca400b..553757a1f1 100644 --- a/services/web/test/frontend/features/file-tree/flows/create-folder.spec.tsx +++ b/services/web/test/frontend/features/file-tree/flows/create-folder.spec.tsx @@ -1,11 +1,12 @@ import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root' import { EditorProviders } from '../../../helpers/editor-providers' import { SocketIOMock } from '@/ide/connection/SocketIoShim' +import type { Socket } from '@/features/ide-react/connection/types/socket' describe('FileTree Create Folder Flow', function () { - let socket: SocketIOMock + let socket: SocketIOMock & Socket beforeEach(function () { - socket = new SocketIOMock() + socket = new SocketIOMock() as any cy.window().then(win => { win.metaAttributesCache.set('ol-user', { id: 'user1' }) }) diff --git a/services/web/test/frontend/features/file-tree/flows/delete-entity.spec.tsx b/services/web/test/frontend/features/file-tree/flows/delete-entity.spec.tsx index af85aa2f46..22de945454 100644 --- a/services/web/test/frontend/features/file-tree/flows/delete-entity.spec.tsx +++ b/services/web/test/frontend/features/file-tree/flows/delete-entity.spec.tsx @@ -1,6 +1,7 @@ import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root' import { EditorProviders } from '../../../helpers/editor-providers' import { SocketIOMock } from '@/ide/connection/SocketIoShim' +import type { Socket } from '@/features/ide-react/connection/types/socket' describe('FileTree Delete Entity Flow', function () { beforeEach(function () { @@ -10,9 +11,9 @@ describe('FileTree Delete Entity Flow', function () { }) describe('single entity', function () { - let socket: SocketIOMock + let socket: SocketIOMock & Socket beforeEach(function () { - socket = new SocketIOMock() + socket = new SocketIOMock() as any const rootFolder = [ { _id: 'root-folder-id', @@ -136,9 +137,9 @@ describe('FileTree Delete Entity Flow', function () { }) describe('folders', function () { - let socket: SocketIOMock + let socket: SocketIOMock & Socket beforeEach(function () { - socket = new SocketIOMock() + socket = new SocketIOMock() as any const rootFolder = [ { _id: 'root-folder-id', @@ -207,9 +208,9 @@ describe('FileTree Delete Entity Flow', function () { }) describe('multiple entities', function () { - let socket: SocketIOMock + let socket: SocketIOMock & Socket beforeEach(function () { - socket = new SocketIOMock() + socket = new SocketIOMock() as any const rootFolder = [ { _id: 'root-folder-id', diff --git a/services/web/test/frontend/features/file-tree/flows/rename-entity.spec.tsx b/services/web/test/frontend/features/file-tree/flows/rename-entity.spec.tsx index afa849c265..dbff951dfc 100644 --- a/services/web/test/frontend/features/file-tree/flows/rename-entity.spec.tsx +++ b/services/web/test/frontend/features/file-tree/flows/rename-entity.spec.tsx @@ -1,6 +1,7 @@ import FileTreeRoot from '../../../../../frontend/js/features/file-tree/components/file-tree-root' import { EditorProviders } from '../../../helpers/editor-providers' import { SocketIOMock } from '@/ide/connection/SocketIoShim' +import type { Socket } from '@/features/ide-react/connection/types/socket' describe('FileTree Rename Entity Flow', function () { beforeEach(function () { @@ -9,9 +10,9 @@ describe('FileTree Rename Entity Flow', function () { }) }) - let socket: SocketIOMock + let socket: SocketIOMock & Socket beforeEach(function () { - socket = new SocketIOMock() + socket = new SocketIOMock() as any const rootFolder = [ { _id: 'root-folder-id', diff --git a/services/web/test/frontend/helpers/editor-providers.jsx b/services/web/test/frontend/helpers/editor-providers.tsx similarity index 79% rename from services/web/test/frontend/helpers/editor-providers.jsx rename to services/web/test/frontend/helpers/editor-providers.tsx index 5a0088120a..bad92019a2 100644 --- a/services/web/test/frontend/helpers/editor-providers.jsx +++ b/services/web/test/frontend/helpers/editor-providers.tsx @@ -3,7 +3,14 @@ import { merge } from 'lodash' import { SocketIOMock } from '@/ide/connection/SocketIoShim' import { IdeContext } from '@/shared/context/ide-context' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import React, { + useCallback, + useEffect, + useState, + useMemo, + type FC, + type PropsWithChildren, +} from 'react' import { createReactScopeValueStore, IdeReactContext, @@ -12,12 +19,25 @@ import { IdeEventEmitter } from '@/features/ide-react/create-ide-event-emitter' import { ReactScopeValueStore } from '@/features/ide-react/scope-value-store/react-scope-value-store' import { ReactScopeEventEmitter } from '@/features/ide-react/scope-event-emitter/react-scope-event-emitter' import { ConnectionContext } from '@/features/ide-react/context/connection-context' -import { EditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context' +import { + EditorOpenDocContext, + type EditorOpenDocContextState, +} from '@/features/ide-react/context/editor-open-doc-context' import { ReactContextRoot } from '@/features/ide-react/context/react-context-root' import useEventListener from '@/shared/hooks/use-event-listener' import useDetachLayout from '@/shared/hooks/use-detach-layout' -import { LayoutContext } from '@/shared/context/layout-context' import useExposedState from '@/shared/hooks/use-exposed-state' +import { + type IdeLayout, + type IdeView, + LayoutContext, + type LayoutContextValue, +} from '@/shared/context/layout-context' +import type { Socket } from '@/features/ide-react/connection/types/socket' +import type { PermissionsLevel } from '@/features/ide-react/types/permissions' +import type { Folder } from '../../../types/folder' +import type { SocketDebuggingInfo } from '@/features/ide-react/connection/types/connection-state' +import type { DocumentContainer } from '@/features/ide-react/editor/document-container' // these constants can be imported in tests instead of // using magic strings @@ -41,10 +61,26 @@ const defaultUserSettings = { mathPreview: true, } -/** - * @typedef {import('@/shared/context/layout-context').LayoutContextValue} LayoutContextValue - * @type Partial - */ +export type EditorProvidersProps = { + user?: { id: string; email: string } + projectId?: string + projectOwner?: { _id: string; email: string } + rootDocId?: string + imageName?: string + compiler?: string + socket?: Socket + isRestrictedTokenMember?: boolean + scope?: Record + features?: Record + projectFeatures?: Record + permissionsLevel?: PermissionsLevel + children?: React.ReactNode + rootFolder?: Folder[] + layoutContext?: Partial + userSettings?: Record + providers?: Record>> +} + const layoutContextDefault = { view: 'editor', openFile: null, @@ -55,7 +91,7 @@ const layoutContextDefault = { projectSearchIsOpen: false, pdfLayout: 'sideBySide', loadingStyleSheet: false, -} +} satisfies Partial export function EditorProviders({ user = { id: USER_ID, email: USER_EMAIL }, @@ -67,7 +103,7 @@ export function EditorProviders({ rootDocId = '_root_doc_id', imageName = 'texlive-full:2024.1', compiler = 'pdflatex', - socket = new SocketIOMock(), + socket = new SocketIOMock() as any as Socket, isRestrictedTokenMember = false, scope: defaultScope = {}, features = { @@ -94,7 +130,7 @@ export function EditorProviders({ layoutContext = layoutContextDefault, userSettings = {}, providers = {}, -}) { +}: EditorProvidersProps) { window.metaAttributesCache.set( 'ol-gitBridgePublicBaseUrl', 'https://git.overleaf.test' @@ -121,7 +157,7 @@ export function EditorProviders({ on: () => {}, off: () => {}, leaveAndCleanUpPromise: async () => {}, - }, + } as any as DocumentContainer, openDocName: null, currentDocumentId: null, }, @@ -150,7 +186,7 @@ export function EditorProviders({ ConnectionProvider: makeConnectionProvider(socket), IdeReactProvider: makeIdeReactProvider(scope, socket), EditorOpenDocProvider: makeEditorOpenDocProvider({ - openDocId: scope.editor.currentDocumentId, + currentDocumentId: scope.editor.currentDocumentId, openDocName: scope.editor.openDocName, currentDocument: scope.editor.sharejs_doc, }), @@ -163,8 +199,8 @@ export function EditorProviders({ ) } -const makeConnectionProvider = socket => { - const ConnectionProvider = ({ children }) => { +const makeConnectionProvider = (socket: Socket) => { + const ConnectionProvider: FC = ({ children }) => { const [value] = useState(() => ({ socket, connectionState: { @@ -174,7 +210,7 @@ const makeConnectionProvider = socket => { reconnectAt: null, forcedDisconnectDelay: 0, lastConnectionAttempt: 0, - error: '', + error: '' as const, }, isConnected: true, isStillReconnecting: false, @@ -182,6 +218,8 @@ const makeConnectionProvider = socket => { tryReconnectNow: () => {}, registerUserActivity: () => {}, disconnect: () => {}, + closeConnection: () => {}, + getSocketDebuggingInfo: () => ({}) as SocketDebuggingInfo, })) return ( @@ -193,8 +231,11 @@ const makeConnectionProvider = socket => { return ConnectionProvider } -const makeIdeReactProvider = (scope, socket) => { - const IdeReactProvider = ({ children }) => { +const makeIdeReactProvider = ( + scope: Record, + socket: Socket +) => { + const IdeReactProvider: FC = ({ children }) => { const [startedFreeTrial, setStartedFreeTrial] = useState(false) const [ideReactContextValue] = useState(() => ({ @@ -204,7 +245,9 @@ const makeIdeReactProvider = (scope, socket) => { setStartedFreeTrial, reportError: () => {}, projectJoined: true, - permissionsLevel: scope.permissionsLevel, + permissionsLevel: scope.permissionsLevel as PermissionsLevel, + setPermissionsLevel: () => {}, + setOutOfSync: () => {}, })) const [ideContextValue] = useState(() => { @@ -247,13 +290,15 @@ const makeIdeReactProvider = (scope, socket) => { return IdeReactProvider } -export function makeEditorOpenDocProvider(initialValues) { +export function makeEditorOpenDocProvider( + initialValues: EditorOpenDocContextState +) { const { currentDocumentId: initialCurrentDocumentId, openDocName: initialOpenDocName, currentDocument: initialCurrentDocument, } = initialValues - const EditorOpenDocProvider = ({ children }) => { + const EditorOpenDocProvider: FC = ({ children }) => { const [currentDocumentId, setCurrentDocumentId] = useExposedState( initialCurrentDocumentId, 'editor.open_doc_id' @@ -285,13 +330,15 @@ export function makeEditorOpenDocProvider(initialValues) { return EditorOpenDocProvider } -const makeLayoutProvider = layoutContextOverrides => { +const makeLayoutProvider = ( + layoutContextOverrides?: Partial +) => { const layout = { ...layoutContextDefault, ...layoutContextOverrides, } - const LayoutProvider = ({ children }) => { - const [view, setView] = useState(layout.view) + const LayoutProvider: FC = ({ children }) => { + const [view, setView] = useState(layout.view) const [openFile, setOpenFile] = useState(layout.openFile) const [chatIsOpen, setChatIsOpen] = useState(layout.chatIsOpen) const [reviewPanelOpen, setReviewPanelOpen] = useState( @@ -316,7 +363,7 @@ const makeLayoutProvider = layoutContextOverrides => { }, [setReviewPanelOpen]) ) const changeLayout = useCallback( - (newLayout, newView = 'editor') => { + (newLayout: IdeLayout, newView: IdeView = 'editor') => { setPdfLayout(newLayout) setView(newLayout === 'sideBySide' ? 'editor' : newView) }, diff --git a/services/web/test/frontend/helpers/render-with-context.jsx b/services/web/test/frontend/helpers/render-with-context.jsx deleted file mode 100644 index 31ee64d5be..0000000000 --- a/services/web/test/frontend/helpers/render-with-context.jsx +++ /dev/null @@ -1,20 +0,0 @@ -// Disable prop type checks for test harnesses -/* eslint-disable react/prop-types */ - -import { render } from '@testing-library/react' -import { EditorProviders } from './editor-providers' - -export function renderWithEditorContext( - component, - contextProps, - renderOptions = {} -) { - const EditorProvidersWrapper = ({ children }) => ( - {children} - ) - - return render(component, { - wrapper: EditorProvidersWrapper, - ...renderOptions, - }) -} diff --git a/services/web/test/frontend/helpers/render-with-context.tsx b/services/web/test/frontend/helpers/render-with-context.tsx new file mode 100644 index 0000000000..cf7aa0b575 --- /dev/null +++ b/services/web/test/frontend/helpers/render-with-context.tsx @@ -0,0 +1,20 @@ +import React from 'react' +import { render, type RenderOptions } from '@testing-library/react' +import { EditorProviders, type EditorProvidersProps } from './editor-providers' + +export function renderWithEditorContext( + component: React.ReactElement, + contextProps: EditorProvidersProps = {}, + renderOptions: RenderOptions = {} +) { + const EditorProvidersWrapper = ({ + children, + }: { + children: React.ReactNode + }) => {children} + + return render(component, { + wrapper: EditorProvidersWrapper, + ...renderOptions, + }) +}