mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2025-11-28 10:18:47 +08:00
chore: add test
This commit is contained in:
211
src/components/app/hooks/__tests__/useViewNavigation.test.ts
Normal file
211
src/components/app/hooks/__tests__/useViewNavigation.test.ts
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
import { renderHook,act } from '@testing-library/react';
|
||||||
|
import { expect } from '@jest/globals';
|
||||||
|
import { useDatabaseViewNavigation } from '../useViewNavigation';
|
||||||
|
import { SCROLL_DELAY, SCROLL_FALLBACK_DELAY } from '../constants';
|
||||||
|
|
||||||
|
describe('useDatabaseViewNavigation', () => {
|
||||||
|
let mockScrollIntoView: jest.Mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
// Mock scrollIntoView
|
||||||
|
mockScrollIntoView = jest.fn();
|
||||||
|
Element.prototype.scrollIntoView = mockScrollIntoView;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('navigateToView with element present', () => {
|
||||||
|
it('should call scrollIntoView when element exists', async () => {
|
||||||
|
const mockElement = document.createElement('div');
|
||||||
|
mockElement.scrollIntoView = mockScrollIntoView;
|
||||||
|
|
||||||
|
const tabRefs = { current: new Map([['view-123', mockElement]]) };
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(tabRefs as React.MutableRefObject<Map<string, HTMLElement>>)
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigatePromise = result.current.navigateToView('view-123');
|
||||||
|
|
||||||
|
// Run all pending timers (SCROLL_DELAY)
|
||||||
|
await act(async () => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
await navigatePromise;
|
||||||
|
|
||||||
|
expect(mockScrollIntoView).toHaveBeenCalledWith({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'nearest',
|
||||||
|
inline: 'center',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call setSelectedViewId before scrolling', async () => {
|
||||||
|
const mockElement = document.createElement('div');
|
||||||
|
mockElement.scrollIntoView = mockScrollIntoView;
|
||||||
|
|
||||||
|
const tabRefs = { current: new Map([['view-123', mockElement]]) };
|
||||||
|
const setSelectedViewId = jest.fn();
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(
|
||||||
|
tabRefs as React.MutableRefObject<Map<string, HTMLElement>>,
|
||||||
|
setSelectedViewId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigatePromise = result.current.navigateToView('view-123');
|
||||||
|
|
||||||
|
// setSelectedViewId should be called immediately
|
||||||
|
expect(setSelectedViewId).toHaveBeenCalledWith('view-123');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
await navigatePromise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not throw when setSelectedViewId is undefined', async () => {
|
||||||
|
const mockElement = document.createElement('div');
|
||||||
|
mockElement.scrollIntoView = mockScrollIntoView;
|
||||||
|
|
||||||
|
const tabRefs = { current: new Map([['view-123', mockElement]]) };
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(tabRefs as React.MutableRefObject<Map<string, HTMLElement>>)
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigatePromise = result.current.navigateToView('view-123');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(navigatePromise).resolves.not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should wait SCROLL_DELAY before scrolling', async () => {
|
||||||
|
const mockElement = document.createElement('div');
|
||||||
|
mockElement.scrollIntoView = mockScrollIntoView;
|
||||||
|
|
||||||
|
const tabRefs = { current: new Map([['view-123', mockElement]]) };
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(tabRefs as React.MutableRefObject<Map<string, HTMLElement>>)
|
||||||
|
);
|
||||||
|
|
||||||
|
result.current.navigateToView('view-123');
|
||||||
|
|
||||||
|
// Should not have scrolled yet
|
||||||
|
expect(mockScrollIntoView).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Advance to just before SCROLL_DELAY
|
||||||
|
await act(async () => {
|
||||||
|
jest.advanceTimersByTime(SCROLL_DELAY - 1);
|
||||||
|
});
|
||||||
|
expect(mockScrollIntoView).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Advance past SCROLL_DELAY
|
||||||
|
await act(async () => {
|
||||||
|
jest.advanceTimersByTime(1);
|
||||||
|
});
|
||||||
|
expect(mockScrollIntoView).toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Clean up remaining timers
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('navigateToView without element', () => {
|
||||||
|
it('should not scroll when element does not exist', async () => {
|
||||||
|
const tabRefs = { current: new Map() };
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(tabRefs as React.MutableRefObject<Map<string, HTMLElement>>)
|
||||||
|
);
|
||||||
|
|
||||||
|
const navigatePromise = result.current.navigateToView('nonexistent-view');
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
await navigatePromise;
|
||||||
|
|
||||||
|
// scrollIntoView should have been called twice (initial try + fallback try)
|
||||||
|
// but since element doesn't exist, it won't actually be called
|
||||||
|
expect(mockScrollIntoView).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger fallback scroll after SCROLL_FALLBACK_DELAY', async () => {
|
||||||
|
const tabRefs = { current: new Map() };
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(tabRefs as React.MutableRefObject<Map<string, HTMLElement>>)
|
||||||
|
);
|
||||||
|
|
||||||
|
result.current.navigateToView('view-123');
|
||||||
|
|
||||||
|
// Fast-forward initial delay
|
||||||
|
await act(async () => {
|
||||||
|
jest.advanceTimersByTime(SCROLL_DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Element is not found, so fallback timer should be set
|
||||||
|
expect(mockScrollIntoView).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
// Add element before fallback fires
|
||||||
|
const mockElement = document.createElement('div');
|
||||||
|
mockElement.scrollIntoView = mockScrollIntoView;
|
||||||
|
tabRefs.current.set('view-123', mockElement);
|
||||||
|
|
||||||
|
// Fast-forward to fallback delay
|
||||||
|
await act(async () => {
|
||||||
|
jest.advanceTimersByTime(SCROLL_FALLBACK_DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now scrollIntoView should have been called by fallback
|
||||||
|
expect(mockScrollIntoView).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ref map updates', () => {
|
||||||
|
it('should handle ref map updates between calls', async () => {
|
||||||
|
const mockElement1 = document.createElement('div');
|
||||||
|
const mockElement2 = document.createElement('div');
|
||||||
|
mockElement1.scrollIntoView = mockScrollIntoView;
|
||||||
|
mockElement2.scrollIntoView = mockScrollIntoView;
|
||||||
|
|
||||||
|
const tabRefs = { current: new Map([['view-1', mockElement1]]) };
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseViewNavigation(tabRefs as React.MutableRefObject<Map<string, HTMLElement>>)
|
||||||
|
);
|
||||||
|
|
||||||
|
// First navigation
|
||||||
|
const nav1Promise = result.current.navigateToView('view-1');
|
||||||
|
await act(async () => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
await nav1Promise;
|
||||||
|
|
||||||
|
expect(mockScrollIntoView).toHaveBeenCalledTimes(1);
|
||||||
|
mockScrollIntoView.mockClear();
|
||||||
|
|
||||||
|
// Update ref map
|
||||||
|
tabRefs.current = new Map([['view-2', mockElement2]]);
|
||||||
|
|
||||||
|
// Second navigation
|
||||||
|
const nav2Promise = result.current.navigateToView('view-2');
|
||||||
|
await act(async () => {
|
||||||
|
jest.runAllTimers();
|
||||||
|
});
|
||||||
|
await nav2Promise;
|
||||||
|
|
||||||
|
expect(mockScrollIntoView).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
142
src/components/app/hooks/__tests__/useViewSync.test.ts
Normal file
142
src/components/app/hooks/__tests__/useViewSync.test.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { expect } from '@jest/globals';
|
||||||
|
import * as Y from 'yjs';
|
||||||
|
import { useDatabaseViewSync } from '../useViewSync';
|
||||||
|
import { SYNC_MAX_ATTEMPTS, SYNC_POLL_INTERVAL } from '../constants';
|
||||||
|
|
||||||
|
describe('useDatabaseViewSync', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('waitForViewData', () => {
|
||||||
|
it('should return true immediately when view exists', async () => {
|
||||||
|
const mockViewsMap = new Map();
|
||||||
|
mockViewsMap.set('view-123', { name: 'Test View' });
|
||||||
|
|
||||||
|
const mockViews = {
|
||||||
|
has: jest.fn((viewId: string) => mockViewsMap.has(viewId)),
|
||||||
|
} as unknown as Y.Map<any>;
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useDatabaseViewSync(mockViews));
|
||||||
|
|
||||||
|
const promise = result.current.waitForViewData('view-123');
|
||||||
|
|
||||||
|
// Fast-forward timers since the view exists immediately
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
const exists = await promise;
|
||||||
|
|
||||||
|
expect(exists).toBe(true);
|
||||||
|
expect(mockViews.has).toHaveBeenCalledWith('view-123');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should poll and return true when view becomes available', async () => {
|
||||||
|
const mockViewsMap = new Map();
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
const mockViews = {
|
||||||
|
has: jest.fn((viewId: string) => {
|
||||||
|
callCount++;
|
||||||
|
// View becomes available on the 3rd call
|
||||||
|
if (callCount >= 3) {
|
||||||
|
mockViewsMap.set(viewId, { name: 'Test View' });
|
||||||
|
}
|
||||||
|
return mockViewsMap.has(viewId);
|
||||||
|
}),
|
||||||
|
} as unknown as Y.Map<any>;
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useDatabaseViewSync(mockViews));
|
||||||
|
|
||||||
|
const promise = result.current.waitForViewData('view-123');
|
||||||
|
|
||||||
|
// Fast-forward through polling intervals
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
jest.advanceTimersByTime(SYNC_POLL_INTERVAL);
|
||||||
|
await Promise.resolve(); // Allow promises to resolve
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = await promise;
|
||||||
|
|
||||||
|
expect(exists).toBe(true);
|
||||||
|
expect(callCount).toBeGreaterThanOrEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false after max attempts when view never appears', async () => {
|
||||||
|
const mockViews = {
|
||||||
|
has: jest.fn(() => false),
|
||||||
|
} as unknown as Y.Map<any>;
|
||||||
|
|
||||||
|
const { result } = renderHook(() => useDatabaseViewSync(mockViews));
|
||||||
|
|
||||||
|
const promise = result.current.waitForViewData('nonexistent-view');
|
||||||
|
|
||||||
|
// Fast-forward through all polling intervals
|
||||||
|
for (let i = 0; i < SYNC_MAX_ATTEMPTS + 1; i++) {
|
||||||
|
jest.advanceTimersByTime(SYNC_POLL_INTERVAL);
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = await promise;
|
||||||
|
|
||||||
|
expect(exists).toBe(false);
|
||||||
|
expect(mockViews.has).toHaveBeenCalledTimes(SYNC_MAX_ATTEMPTS);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle undefined views map gracefully', async () => {
|
||||||
|
const { result } = renderHook(() => useDatabaseViewSync(undefined));
|
||||||
|
|
||||||
|
const promise = result.current.waitForViewData('view-123');
|
||||||
|
|
||||||
|
// Fast-forward through all polling intervals
|
||||||
|
for (let i = 0; i < SYNC_MAX_ATTEMPTS + 1; i++) {
|
||||||
|
jest.advanceTimersByTime(SYNC_POLL_INTERVAL);
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
const exists = await promise;
|
||||||
|
|
||||||
|
expect(exists).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update waitForViewData when views map changes', async () => {
|
||||||
|
const mockViewsMap1 = new Map();
|
||||||
|
const mockViews1 = {
|
||||||
|
has: jest.fn((viewId: string) => mockViewsMap1.has(viewId)),
|
||||||
|
} as unknown as Y.Map<any>;
|
||||||
|
|
||||||
|
const { result, rerender } = renderHook(
|
||||||
|
({ views }) => useDatabaseViewSync(views),
|
||||||
|
{ initialProps: { views: mockViews1 } }
|
||||||
|
);
|
||||||
|
|
||||||
|
// First call with empty map
|
||||||
|
let promise = result.current.waitForViewData('view-123');
|
||||||
|
jest.advanceTimersByTime(SYNC_POLL_INTERVAL);
|
||||||
|
await Promise.resolve();
|
||||||
|
|
||||||
|
// Update to new map with the view
|
||||||
|
const mockViewsMap2 = new Map([['view-123', { name: 'Test' }]]);
|
||||||
|
const mockViews2 = {
|
||||||
|
has: jest.fn((viewId: string) => mockViewsMap2.has(viewId)),
|
||||||
|
} as unknown as Y.Map<any>;
|
||||||
|
|
||||||
|
rerender({ views: mockViews2 });
|
||||||
|
|
||||||
|
// New call should use updated map
|
||||||
|
promise = result.current.waitForViewData('view-123');
|
||||||
|
jest.runAllTimers();
|
||||||
|
|
||||||
|
const exists = await promise;
|
||||||
|
|
||||||
|
expect(exists).toBe(true);
|
||||||
|
expect(mockViews2.has).toHaveBeenCalledWith('view-123');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
import { renderHook, waitFor } from '@testing-library/react';
|
||||||
|
import { expect } from '@jest/globals';
|
||||||
|
import { useDatabaseLoading } from '../useDatabaseLoading';
|
||||||
|
import { View, YDoc } from '@/application/types';
|
||||||
|
|
||||||
|
// Mock useRetryFunction
|
||||||
|
jest.mock('../useRetryFunction', () => ({
|
||||||
|
useRetryFunction: (fn: any, onError: any) => {
|
||||||
|
// Return a function that wraps the original and calls onError on failure
|
||||||
|
return jest.fn(async (...args: any[]) => {
|
||||||
|
try {
|
||||||
|
if (!fn) throw new Error('Function not available');
|
||||||
|
const result = await fn(...args);
|
||||||
|
if (!result) throw new Error('No result returned');
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
onError();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useDatabaseLoading', () => {
|
||||||
|
let consoleDebugSpy: jest.SpyInstance;
|
||||||
|
let consoleErrorSpy: jest.SpyInstance;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation();
|
||||||
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
const createMockView = (viewId: string, children: Array<{ view_id: string }> = []): View => ({
|
||||||
|
view_id: viewId,
|
||||||
|
name: `View ${viewId}`,
|
||||||
|
children,
|
||||||
|
layout: 0,
|
||||||
|
parent_view_id: 'parent',
|
||||||
|
icon: null,
|
||||||
|
extra: null,
|
||||||
|
is_published: false,
|
||||||
|
is_private: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createMockYDoc = (): YDoc => ({
|
||||||
|
guid: 'mock-doc',
|
||||||
|
} as YDoc);
|
||||||
|
|
||||||
|
describe('initial view loading', () => {
|
||||||
|
it('should load view doc on mount', async () => {
|
||||||
|
const mockDoc = createMockYDoc();
|
||||||
|
const loadView = jest.fn().mockResolvedValue(mockDoc);
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(createMockView('view-1'));
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.doc).toBe(mockDoc);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.notFound).toBe(false);
|
||||||
|
expect(loadView).toHaveBeenCalledWith('view-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set doc to null initially', () => {
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(createMockView('view-1'));
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initially doc should be null
|
||||||
|
expect(result.current.doc).toBeNull();
|
||||||
|
expect(result.current.notFound).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('view meta loading', () => {
|
||||||
|
it('should load view meta and update visible view IDs', async () => {
|
||||||
|
const mockView = createMockView('view-1', [
|
||||||
|
{ view_id: 'child-1' },
|
||||||
|
{ view_id: 'child-2' },
|
||||||
|
]);
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(mockView);
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.visibleViewIds).toEqual(['view-1', 'child-1', 'child-2']);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.iidName).toBe('View view-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select first child view when viewId not in visible views', async () => {
|
||||||
|
const mockView = createMockView('view-1', [
|
||||||
|
{ view_id: 'child-1' },
|
||||||
|
{ view_id: 'child-2' },
|
||||||
|
]);
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(mockView);
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.visibleViewIds.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should select view-1 since it's in the visible views list (first item)
|
||||||
|
expect(result.current.selectedViewId).toBe('view-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select requested viewId when it is in visible views', async () => {
|
||||||
|
const mockView = createMockView('view-1', [
|
||||||
|
{ view_id: 'child-1' },
|
||||||
|
]);
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(mockView);
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.selectedViewId).toBe('view-1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set notFound when view meta fails to load', async () => {
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
const loadViewMeta = jest.fn().mockRejectedValue(new Error('Meta not found'));
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.notFound).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(consoleErrorSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadViewMeta function', () => {
|
||||||
|
it('should load meta for the same viewId with callback', async () => {
|
||||||
|
const mockView = createMockView('view-1');
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(mockView);
|
||||||
|
const callback = jest.fn();
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.doc).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
const meta = await result.current.loadViewMeta('view-1', callback);
|
||||||
|
|
||||||
|
expect(meta).toEqual(mockView);
|
||||||
|
expect(result.current.notFound).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('error handling', () => {
|
||||||
|
it('should handle missing loadView function', async () => {
|
||||||
|
const loadViewMeta = jest.fn().mockResolvedValue(createMockView('view-1'));
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView: undefined,
|
||||||
|
loadViewMeta,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.notFound).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle missing loadViewMeta function', async () => {
|
||||||
|
const loadView = jest.fn().mockResolvedValue(createMockYDoc());
|
||||||
|
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useDatabaseLoading({
|
||||||
|
viewId: 'view-1',
|
||||||
|
loadView,
|
||||||
|
loadViewMeta: undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(result.current.notFound).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
Reference in New Issue
Block a user