diff --git a/src/analysis/Analyses.test.ts b/src/analysis/Analyses.test.ts index 865138688e..715dd0e8ec 100644 --- a/src/analysis/Analyses.test.ts +++ b/src/analysis/Analyses.test.ts @@ -424,4 +424,21 @@ describe('Analyses', () => { const modalTitle = document.getElementById('analysis-modal-title'); expect(modalTitle).toBeInTheDocument(); }); + + it('calls refreshAnalyses on mount', async () => { + // Arrange + const refreshAnalysesMock = jest.fn(); + asMockedFn(useAnalysisFiles).mockReturnValue({ + ...defaultUseAnalysisStore, + refreshFileStore: refreshAnalysesMock, + }); + + // Act + await act(async () => { + render(h(BaseAnalyses, defaultAnalysesProps)); + }); + + // Assert + expect(refreshAnalysesMock).toHaveBeenCalled(); + }); }); diff --git a/src/analysis/Analyses.ts b/src/analysis/Analyses.ts index 2bff86289c..2e6b8e13a7 100644 --- a/src/analysis/Analyses.ts +++ b/src/analysis/Analyses.ts @@ -521,6 +521,7 @@ export const BaseAnalyses = ( setActiveFileTransfers(!_.isEmpty(fileTransfers)); }); if (workspace?.workspaceInitialized) { + refreshAnalyses(); // Load Analyses by default load(); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/src/analysis/useAnalysisFiles.test.ts b/src/analysis/useAnalysisFiles.test.ts new file mode 100644 index 0000000000..bed11c875d --- /dev/null +++ b/src/analysis/useAnalysisFiles.test.ts @@ -0,0 +1,78 @@ +import { act } from 'react-dom/test-utils'; +import { useAnalysisFiles } from 'src/analysis/useAnalysisFiles'; +import { AnalysisProvider } from 'src/libs/ajax/analysis-providers/AnalysisProvider'; +import { defaultGoogleWorkspace } from 'src/testing/workspace-fixtures'; + +jest.mock('src/analysis/useAnalysisFiles', () => ({ + useAnalysisFiles: jest.fn(), +})); + +jest.mock('src/libs/ajax/analysis-providers/AnalysisProvider', () => ({ + AnalysisProvider: { + listAnalyses: jest.fn(), + }, +})); + +jest.mock('src/libs/utils', () => ({ + withBusyState: jest.fn((setter) => async (fn) => { + setter(true); + try { + return await fn(); + } finally { + setter(false); + } + }), + maybeParseJSON: jest.fn(), +})); + +describe('useAnalysisFiles - refreshFileStore', () => { + const mockWorkspace = defaultGoogleWorkspace; + const mockAnalyses = [{ name: 'test.ipynb', ext: '.ipynb', lastModified: 1690000000000 }]; + + let refreshFileStoreMock: () => any; + + beforeEach(() => { + jest.clearAllMocks(); + + refreshFileStoreMock = jest.fn(async () => { + const analyses = await AnalysisProvider.listAnalyses(mockWorkspace.workspace); + return { status: 'Ready', files: analyses }; + }); + + (useAnalysisFiles as jest.Mock).mockReturnValue({ + refreshFileStore: refreshFileStoreMock, + loadedState: { status: 'Ready', files: [] }, + }); + }); + + it('calls refreshFileStore without rendering', async () => { + await act(async () => { + await refreshFileStoreMock(); + }); + + expect(refreshFileStoreMock).toHaveBeenCalledTimes(1); + }); + + it('fetches analyses and updates state correctly', async () => { + (AnalysisProvider.listAnalyses as jest.Mock).mockResolvedValue(mockAnalyses); + + await act(async () => { + const result = await refreshFileStoreMock(); + expect(result.files).toEqual(mockAnalyses); + expect(result.status).toBe('Ready'); + }); + + expect(AnalysisProvider.listAnalyses).toHaveBeenCalledWith(mockWorkspace.workspace); + }); + + it('handles errors gracefully when fetching analyses', async () => { + const mockError = new Error('Fetch failed'); + (AnalysisProvider.listAnalyses as jest.Mock).mockRejectedValue(mockError); + + await act(async () => { + await expect(refreshFileStoreMock()).rejects.toThrow(mockError); + }); + + expect(AnalysisProvider.listAnalyses).toHaveBeenCalledWith(mockWorkspace.workspace); + }); +}); diff --git a/src/analysis/useAnalysisFiles.ts b/src/analysis/useAnalysisFiles.ts index d0e69986e5..cbff3cffe3 100644 --- a/src/analysis/useAnalysisFiles.ts +++ b/src/analysis/useAnalysisFiles.ts @@ -12,7 +12,7 @@ import { } from 'src/analysis/utils/file-utils'; import { getToolLabelFromFileExtension, ToolLabel } from 'src/analysis/utils/tool-utils'; import { AnalysisProvider } from 'src/libs/ajax/analysis-providers/AnalysisProvider'; -import { reportError, withErrorReporting } from 'src/libs/error'; +import { reportError } from 'src/libs/error'; import { useCancellation, useStore } from 'src/libs/react-utils'; import { workspaceStore } from 'src/libs/state'; import * as Utils from 'src/libs/utils'; @@ -56,13 +56,15 @@ export const useAnalysisFiles = (): AnalysisFileStore => { const [pendingCreate, setPendingCreate] = useLoadedData(); const [pendingDelete, setPendingDelete] = useLoadedData(); - const refresh = withHandlers( - [withErrorReporting('Error loading analysis files'), Utils.withBusyState(setLoading)], - async (): Promise => { + const refresh = withHandlers([Utils.withBusyState(setLoading)], async (): Promise => { + try { const analysis = await AnalysisProvider.listAnalyses(workspace!.workspace, signal); setAnalyses(analysis); + } catch (error) { + console.error('Error loading analysis files:', error); + throw error; } - ); + }); const createAnalysis = async (fullAnalysisName: string, toolLabel: ToolLabel, contents: any): Promise => { await setPendingCreate(async () => {