diff --git a/ui/src/common/useToggleLove.test.js b/ui/src/common/useToggleLove.test.js index 4e5b64ef9..bc9636fc3 100644 --- a/ui/src/common/useToggleLove.test.js +++ b/ui/src/common/useToggleLove.test.js @@ -40,6 +40,8 @@ describe('useToggleLove', () => { await result.current[0]() }) expect(subsonic.star).toHaveBeenCalledWith('sg-1') + expect(getOne).toHaveBeenCalledTimes(2) + expect(getOne).toHaveBeenCalledWith('song', { id: 'pt-1' }) expect(getOne).toHaveBeenCalledWith('song', { id: 'sg-1' }) }) @@ -62,6 +64,15 @@ describe('useToggleLove', () => { expect(subsonic.unstar).toHaveBeenCalledWith('sg-1') }) + it('does not call notify on a successful toggle', async () => { + const record = { id: 'sg-1', starred: false } + const { result } = renderHook(() => useToggleLove('song', record)) + await act(async () => { + await result.current[0]() + }) + expect(mockNotify).not.toHaveBeenCalled() + }) + describe('playlist track scenarios', () => { it('refreshes both playlist track and song for playlist tracks', async () => { const record = { @@ -159,6 +170,18 @@ describe('useToggleLove', () => { }) expect(mockNotify).toHaveBeenCalledWith('ra.page.error', 'warning') }) + + it('does not call notify when refresh fails after a successful toggle', async () => { + getOne.mockRejectedValue(new Error('refresh failed')) + const record = { id: 'sg-1', starred: false } + const { result } = renderHook(() => useToggleLove('song', record)) + await act(async () => { + await result.current[0]() + }) + expect(subsonic.star).toHaveBeenCalledWith('sg-1') + expect(mockNotify).not.toHaveBeenCalled() + expect(result.current[1]).toBe(false) + }) }) describe('loading state', () => { @@ -185,4 +208,37 @@ describe('useToggleLove', () => { expect(result.current[1]).toBe(false) }) }) + + describe('unmount safety', () => { + it('does not warn when the promise resolves after unmount', async () => { + let resolveStar + subsonic.star.mockReturnValue( + new Promise((r) => { + resolveStar = r + }), + ) + const record = { id: 'sg-1', starred: false } + const errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) + + const { result, unmount } = renderHook(() => + useToggleLove('song', record), + ) + act(() => { + result.current[0]() + }) + unmount() + await act(async () => { + resolveStar() + }) + + const stateUpdateWarnings = errorSpy.mock.calls.filter( + ([msg]) => + typeof msg === 'string' && + msg.includes("Can't perform a React state update"), + ) + expect(stateUpdateWarnings).toHaveLength(0) + + errorSpy.mockRestore() + }) + }) })