task(B4): cache background image (fixes BUG-4)
This commit is contained in:
@@ -3,10 +3,14 @@
|
||||
* Diese Tests sind VOR dem jeweiligen Fix ROT (dokumentiertes Fehlverhalten)
|
||||
* und müssen danach GRÜN sein.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { SeatingService } from '../src/services/seatingService';
|
||||
import { trimElement, offsetElement } from '../src/tools/modification/geometry';
|
||||
import type { CADElement } from '../src/types/cad.types';
|
||||
import { RenderEngine } from '../src/canvas/RenderEngine';
|
||||
import { ZoomPanController } from '../src/canvas/ZoomPanController';
|
||||
import { SpatialIndex } from '../src/canvas/SpatialIndex';
|
||||
import { LayerManager } from '../src/canvas/LayerManager';
|
||||
|
||||
// ── Helfer (Muster aus tests/geometry.test.ts) ─────────────
|
||||
function makeLine(id: string, x1: number, y1: number, x2: number, y2: number): CADElement {
|
||||
@@ -100,3 +104,70 @@ describe('BUG-3: offset respects click side', () => {
|
||||
expect(offsetElement(circle, 10, 1).properties.radius).toBeCloseTo(60);
|
||||
});
|
||||
});
|
||||
|
||||
// ── BUG-4: Hintergrundbild wurde pro Frame neu geladen ──────
|
||||
describe('BUG-4: background image cached', () => {
|
||||
let imageCount: number;
|
||||
let OriginalImage: typeof Image;
|
||||
|
||||
beforeEach(() => {
|
||||
imageCount = 0;
|
||||
OriginalImage = global.Image;
|
||||
// Bild-Stub: zaehlt Konstruktur-Aufrufe; simuliert Browser-Cache-Bild
|
||||
// (complete=true sofort verfuegbar, onload als Mikrotask wie async decode)
|
||||
class FakeImage {
|
||||
complete = true;
|
||||
naturalWidth = 800;
|
||||
width = 800;
|
||||
height = 600;
|
||||
onload: (() => void) | null = null;
|
||||
private internalSrc = '';
|
||||
get src(): string {
|
||||
return this.internalSrc;
|
||||
}
|
||||
set src(value: string) {
|
||||
this.internalSrc = value;
|
||||
queueMicrotask(() => this.onload?.());
|
||||
}
|
||||
constructor() {
|
||||
imageCount += 1;
|
||||
}
|
||||
}
|
||||
(global as { Image: unknown }).Image = FakeImage as unknown as typeof Image;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
(global as { Image: unknown }).Image = OriginalImage;
|
||||
});
|
||||
|
||||
function createEngine(): RenderEngine {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 800;
|
||||
canvas.height = 600;
|
||||
const zoomPan = new ZoomPanController(canvas);
|
||||
const spatialIndex = new SpatialIndex();
|
||||
const layerManager = new LayerManager();
|
||||
return new RenderEngine(canvas, zoomPan, spatialIndex, layerManager);
|
||||
}
|
||||
|
||||
it('lädt das Bild pro Quellwechsel nur EINMAL, weitere Frames nutzen den Cache', async () => {
|
||||
const engine = createEngine();
|
||||
const DATA_URL = 'data:image/png;base64,iVBORw0KGgo=';
|
||||
engine.setOptions({ backgroundSrc: DATA_URL, backgroundOpacity: 0.5 });
|
||||
|
||||
// Frame 1: legt genau ein Image an und cachet es (complete=true)
|
||||
engine.render();
|
||||
expect(imageCount).toBe(1);
|
||||
|
||||
// Weitere Frames mit identischer src: dürfen KEIN neues Image erzeugen
|
||||
engine.render();
|
||||
engine.render();
|
||||
engine.render();
|
||||
expect(imageCount).toBe(1);
|
||||
|
||||
// Ausstehende Mikrotasks (simuliertes onload) abwarten, dann erneut prüfen:
|
||||
await Promise.resolve();
|
||||
engine.render();
|
||||
expect(imageCount).toBe(1); // vorher (Bug): jedes render() erzeugte ein neues Image
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user