Merge branch 'main' into main
This commit is contained in:
@@ -138,10 +138,51 @@ describe('useVaultWatcher', () => {
|
||||
|
||||
unmount()
|
||||
|
||||
await settleWatcherSubscription()
|
||||
expect(mocks.unlisten).toHaveBeenCalledOnce()
|
||||
expect(mocks.invoke).toHaveBeenCalledWith('stop_vault_watcher')
|
||||
})
|
||||
|
||||
it('swallows stale native watcher unlisten failures and still stops the watcher', async () => {
|
||||
mocks.unlisten.mockImplementationOnce(() => {
|
||||
throw new TypeError("undefined is not an object (evaluating 'listeners[eventId].handlerId')")
|
||||
})
|
||||
|
||||
const { unmount } = renderHook(() => useVaultWatcher({
|
||||
vaultPath: '/vault',
|
||||
onVaultChanged: vi.fn(),
|
||||
}))
|
||||
|
||||
await settleWatcherSubscription()
|
||||
|
||||
expect(() => unmount()).not.toThrow()
|
||||
await settleWatcherSubscription()
|
||||
expect(mocks.unlisten).toHaveBeenCalledOnce()
|
||||
expect(mocks.invoke).toHaveBeenCalledWith('stop_vault_watcher')
|
||||
})
|
||||
|
||||
it('keeps listener replacement stable when vault paths churn', async () => {
|
||||
mocks.unlisten.mockImplementationOnce(() => {
|
||||
throw new TypeError("undefined is not an object (evaluating 'listeners[eventId].handlerId')")
|
||||
})
|
||||
|
||||
const { rerender } = renderHook(
|
||||
({ vaultPath }) => useVaultWatcher({ vaultPath, onVaultChanged: vi.fn() }),
|
||||
{ initialProps: { vaultPath: '/vault-a' } },
|
||||
)
|
||||
|
||||
await settleWatcherSubscription()
|
||||
|
||||
expect(() => rerender({ vaultPath: '/vault-b' })).not.toThrow()
|
||||
await settleWatcherSubscription()
|
||||
|
||||
expect(mocks.listen).toHaveBeenCalledTimes(2)
|
||||
expect(mocks.unlisten).toHaveBeenCalledOnce()
|
||||
expect(mocks.invoke).toHaveBeenCalledWith('start_vault_watcher', { path: '/vault-a' })
|
||||
expect(mocks.invoke).toHaveBeenCalledWith('start_vault_watcher', { path: '/vault-b' })
|
||||
expect(mocks.invoke).toHaveBeenCalledWith('stop_vault_watcher')
|
||||
})
|
||||
|
||||
it('batches changed paths for the active vault', async () => {
|
||||
const onVaultChanged = vi.fn()
|
||||
renderHook(() => useVaultWatcher({ vaultPath: '/vault', onVaultChanged }))
|
||||
|
||||
@@ -200,6 +200,12 @@ function handleWatcherEvent({
|
||||
}
|
||||
}
|
||||
|
||||
function cleanupNativeWatcherListener(unlisten: UnlistenFn): void {
|
||||
void Promise.resolve()
|
||||
.then(unlisten)
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
function usePendingVaultRefresh({
|
||||
vaultPathRef,
|
||||
onVaultChanged,
|
||||
@@ -263,7 +269,6 @@ function useNativeVaultWatcher({
|
||||
enqueueChangedPaths: (paths: WatchPath[]) => void
|
||||
clearPendingRefresh: () => void
|
||||
}) {
|
||||
|
||||
useEffect(() => {
|
||||
const root = normalizeWatchPath(vaultPath)
|
||||
if (!root || !isTauri()) return
|
||||
@@ -275,7 +280,7 @@ function useNativeVaultWatcher({
|
||||
handleWatcherEvent({ event, root, enqueueChangedPaths })
|
||||
}).then((nextUnlisten) => {
|
||||
if (cancelled) {
|
||||
nextUnlisten()
|
||||
cleanupNativeWatcherListener(nextUnlisten)
|
||||
} else {
|
||||
unlisten = nextUnlisten
|
||||
}
|
||||
@@ -290,7 +295,7 @@ function useNativeVaultWatcher({
|
||||
return () => {
|
||||
cancelled = true
|
||||
clearPendingRefresh()
|
||||
unlisten?.()
|
||||
if (unlisten) cleanupNativeWatcherListener(unlisten)
|
||||
void invoke('stop_vault_watcher').catch(() => {})
|
||||
}
|
||||
}, [vaultPath, enqueueChangedPaths, clearPendingRefresh])
|
||||
|
||||
Reference in New Issue
Block a user