import { afterEach, expect, test, vi } from 'vitest'; import { DOWNLOAD_URL_REVOKE_MS, downloadBlob } from './download'; afterEach(() => { vi.useRealTimers(); vi.restoreAllMocks(); document.body.replaceChildren(); }); test('keeps the blob URL alive through the click and revokes it after cleanup', () => { vi.useFakeTimers(); const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:vehicle-export'); const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined); const click = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(function (this: HTMLAnchorElement) { expect(this.isConnected).toBe(true); expect(this.href).toContain('blob:vehicle-export'); expect(this.download).toBe('车辆导出.csv'); expect(revokeObjectURL).not.toHaveBeenCalled(); }); downloadBlob(new Blob(['vehicle']), '车辆导出.csv'); expect(createObjectURL).toHaveBeenCalledTimes(1); expect(click).toHaveBeenCalledTimes(1); expect(document.querySelector('a[download]')).toBeNull(); expect(revokeObjectURL).not.toHaveBeenCalled(); vi.advanceTimersByTime(DOWNLOAD_URL_REVOKE_MS); expect(revokeObjectURL).toHaveBeenCalledWith('blob:vehicle-export'); });