fix(image-pipeline): close cleared in-flight source bitmaps

This commit is contained in:
Matthias
2026-04-04 11:40:32 +02:00
parent c0534e04e0
commit 4fa517066f
2 changed files with 53 additions and 0 deletions

View File

@@ -253,4 +253,49 @@ describe("loadSourceBitmap", () => {
expect(bitmap.close).toHaveBeenCalledTimes(1);
}
});
it("closes a decoded bitmap that resolves after its cache entry was cleared in flight", async () => {
const subject = (await importSubject()) as typeof import("@/lib/image-pipeline/source-loader") & {
clearSourceBitmapCache?: () => void;
};
const { clearSourceBitmapCache, loadSourceBitmap } = subject;
expect(clearSourceBitmapCache).toBeTypeOf("function");
const firstBitmap = { close: vi.fn() } as unknown as ImageBitmap;
const secondBitmap = { close: vi.fn() } as unknown as ImageBitmap;
const decodeDeferred = createDeferred<ImageBitmap>();
vi.stubGlobal(
"createImageBitmap",
vi
.fn()
.mockImplementationOnce(async () => await decodeDeferred.promise)
.mockResolvedValueOnce(secondBitmap),
);
vi.stubGlobal(
"fetch",
vi.fn().mockImplementation(async (input: string | URL | Request) => ({
ok: true,
status: 200,
blob: vi.fn().mockResolvedValue(new Blob([String(input)])),
})),
);
const sourceUrl = "https://cdn.example.com/source-in-flight.png";
const pendingLoad = loadSourceBitmap(sourceUrl);
await vi.waitFor(() => {
expect(createImageBitmap).toHaveBeenCalledTimes(1);
});
clearSourceBitmapCache!();
decodeDeferred.resolve(firstBitmap);
await expect(pendingLoad).resolves.toBe(firstBitmap);
expect(firstBitmap.close).toHaveBeenCalledTimes(1);
await expect(loadSourceBitmap(sourceUrl)).resolves.toBe(secondBitmap);
expect(fetch).toHaveBeenCalledTimes(2);
});
});