argent-grid 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/pages.yml +68 -0
- package/AGENTS.md +179 -0
- package/README.md +222 -0
- package/demo-app/README.md +70 -0
- package/demo-app/angular.json +78 -0
- package/demo-app/e2e/benchmark.spec.ts +53 -0
- package/demo-app/e2e/demo-page.spec.ts +77 -0
- package/demo-app/e2e/grid-features.spec.ts +269 -0
- package/demo-app/package-lock.json +14023 -0
- package/demo-app/package.json +36 -0
- package/demo-app/playwright-test-menu.js +19 -0
- package/demo-app/playwright.config.ts +23 -0
- package/demo-app/src/app/app.component.ts +10 -0
- package/demo-app/src/app/app.config.ts +13 -0
- package/demo-app/src/app/app.routes.ts +7 -0
- package/demo-app/src/app/demo-page/demo-page.component.css +313 -0
- package/demo-app/src/app/demo-page/demo-page.component.html +124 -0
- package/demo-app/src/app/demo-page/demo-page.component.ts +366 -0
- package/demo-app/src/index.html +19 -0
- package/demo-app/src/main.ts +6 -0
- package/demo-app/tsconfig.json +31 -0
- package/ng-package.json +8 -0
- package/package.json +60 -0
- package/plan.md +131 -0
- package/setup-vitest.ts +18 -0
- package/src/lib/argent-grid.module.ts +21 -0
- package/src/lib/components/argent-grid.component.css +483 -0
- package/src/lib/components/argent-grid.component.html +320 -0
- package/src/lib/components/argent-grid.component.spec.ts +189 -0
- package/src/lib/components/argent-grid.component.ts +1188 -0
- package/src/lib/directives/ag-grid-compatibility.directive.ts +92 -0
- package/src/lib/rendering/canvas-renderer.ts +962 -0
- package/src/lib/rendering/render/blit.spec.ts +453 -0
- package/src/lib/rendering/render/blit.ts +393 -0
- package/src/lib/rendering/render/cells.ts +369 -0
- package/src/lib/rendering/render/index.ts +105 -0
- package/src/lib/rendering/render/lines.ts +363 -0
- package/src/lib/rendering/render/theme.spec.ts +282 -0
- package/src/lib/rendering/render/theme.ts +201 -0
- package/src/lib/rendering/render/types.ts +279 -0
- package/src/lib/rendering/render/walk.spec.ts +360 -0
- package/src/lib/rendering/render/walk.ts +360 -0
- package/src/lib/rendering/utils/damage-tracker.spec.ts +444 -0
- package/src/lib/rendering/utils/damage-tracker.ts +423 -0
- package/src/lib/rendering/utils/index.ts +7 -0
- package/src/lib/services/grid.service.spec.ts +1039 -0
- package/src/lib/services/grid.service.ts +1284 -0
- package/src/lib/types/ag-grid-types.ts +970 -0
- package/src/public-api.ts +22 -0
- package/tsconfig.json +32 -0
- package/tsconfig.lib.json +11 -0
- package/tsconfig.spec.json +8 -0
- package/vitest.config.ts +55 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for Blitting Optimization
|
|
3
|
+
*
|
|
4
|
+
* Tests for blit calculations, execution, and state management.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
shouldBlit,
|
|
9
|
+
calculateBlit,
|
|
10
|
+
blitLastFrame,
|
|
11
|
+
createBufferPair,
|
|
12
|
+
swapBuffers,
|
|
13
|
+
displayBuffer,
|
|
14
|
+
resizeBufferPair,
|
|
15
|
+
BlitState,
|
|
16
|
+
MIN_BLIT_DELTA,
|
|
17
|
+
MAX_BLIT_DELTA_RATIO,
|
|
18
|
+
} from './blit';
|
|
19
|
+
import { Rectangle, BufferPair } from './types';
|
|
20
|
+
|
|
21
|
+
// Mock canvas and context
|
|
22
|
+
const mockContext = {
|
|
23
|
+
canvas: { width: 800, height: 600, style: {} } as any,
|
|
24
|
+
drawImage: vi.fn(),
|
|
25
|
+
setTransform: vi.fn(),
|
|
26
|
+
getImageData: vi.fn(() => ({ data: new Uint8ClampedArray([255, 0, 0, 255]) })),
|
|
27
|
+
fillRect: vi.fn(),
|
|
28
|
+
fillStyle: '',
|
|
29
|
+
} as unknown as CanvasRenderingContext2D;
|
|
30
|
+
|
|
31
|
+
// Mock HTMLCanvasElement
|
|
32
|
+
class MockCanvas {
|
|
33
|
+
width = 800;
|
|
34
|
+
height = 600;
|
|
35
|
+
style = { width: '800px', height: '600px' };
|
|
36
|
+
|
|
37
|
+
getContext() {
|
|
38
|
+
return mockContext;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Mock document.createElement for canvas
|
|
43
|
+
const originalCreateElement = document.createElement.bind(document);
|
|
44
|
+
beforeAll(() => {
|
|
45
|
+
vi.spyOn(document, 'createElement').mockImplementation((tagName: string) => {
|
|
46
|
+
if (tagName.toLowerCase() === 'canvas') {
|
|
47
|
+
return new MockCanvas() as unknown as HTMLCanvasElement;
|
|
48
|
+
}
|
|
49
|
+
return originalCreateElement(tagName);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterAll(() => {
|
|
54
|
+
vi.restoreAllMocks();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('Blitting Optimization', () => {
|
|
58
|
+
describe('shouldBlit', () => {
|
|
59
|
+
it('should return false for no scroll', () => {
|
|
60
|
+
expect(shouldBlit(0, 0, 800, 600)).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should return false for tiny scroll deltas', () => {
|
|
64
|
+
expect(shouldBlit(1, 0, 800, 600)).toBe(false);
|
|
65
|
+
expect(shouldBlit(0, 1, 800, 600)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return true for moderate vertical scroll', () => {
|
|
69
|
+
expect(shouldBlit(0, 10, 800, 600)).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should return true for moderate horizontal scroll', () => {
|
|
73
|
+
expect(shouldBlit(10, 0, 800, 600)).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should return false for diagonal scroll', () => {
|
|
77
|
+
expect(shouldBlit(10, 10, 800, 600)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should return false for large scroll delta', () => {
|
|
81
|
+
// More than 80% of viewport
|
|
82
|
+
expect(shouldBlit(0, 500, 800, 600)).toBe(false);
|
|
83
|
+
expect(shouldBlit(650, 0, 800, 600)).toBe(false);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should use MIN_BLIT_DELTA threshold', () => {
|
|
87
|
+
expect(shouldBlit(MIN_BLIT_DELTA - 1, 0, 800, 600)).toBe(false);
|
|
88
|
+
expect(shouldBlit(MIN_BLIT_DELTA, 0, 800, 600)).toBe(true);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('calculateBlit', () => {
|
|
93
|
+
const viewportSize = { width: 800, height: 600 };
|
|
94
|
+
const pinnedWidths = { left: 100, right: 50 };
|
|
95
|
+
|
|
96
|
+
it('should return canBlit=false for no scroll change', () => {
|
|
97
|
+
const result = calculateBlit(
|
|
98
|
+
{ x: 100, y: 100 },
|
|
99
|
+
{ x: 100, y: 100 },
|
|
100
|
+
viewportSize,
|
|
101
|
+
pinnedWidths
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
expect(result.canBlit).toBe(false);
|
|
105
|
+
expect(result.dirtyRegions).toHaveLength(1);
|
|
106
|
+
expect(result.dirtyRegions[0]).toEqual({
|
|
107
|
+
x: 0,
|
|
108
|
+
y: 0,
|
|
109
|
+
width: 800,
|
|
110
|
+
height: 600,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should calculate vertical scroll blit (down)', () => {
|
|
115
|
+
const result = calculateBlit(
|
|
116
|
+
{ x: 0, y: 100 }, // Current
|
|
117
|
+
{ x: 0, y: 0 }, // Last
|
|
118
|
+
viewportSize,
|
|
119
|
+
pinnedWidths
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
expect(result.canBlit).toBe(true);
|
|
123
|
+
expect(result.deltaY).toBe(100);
|
|
124
|
+
expect(result.deltaX).toBe(0);
|
|
125
|
+
|
|
126
|
+
// Scrolling down: top strip needs redraw
|
|
127
|
+
expect(result.dirtyRegions).toHaveLength(1);
|
|
128
|
+
expect(result.dirtyRegions[0].y).toBe(0);
|
|
129
|
+
expect(result.dirtyRegions[0].height).toBe(100);
|
|
130
|
+
|
|
131
|
+
// Source should start at 0
|
|
132
|
+
expect(result.sourceRect.y).toBe(0);
|
|
133
|
+
// Dest should start at deltaY
|
|
134
|
+
expect(result.destRect.y).toBe(100);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should calculate vertical scroll blit (up)', () => {
|
|
138
|
+
const result = calculateBlit(
|
|
139
|
+
{ x: 0, y: 0 }, // Current
|
|
140
|
+
{ x: 0, y: 100 }, // Last
|
|
141
|
+
viewportSize,
|
|
142
|
+
pinnedWidths
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
expect(result.canBlit).toBe(true);
|
|
146
|
+
expect(result.deltaY).toBe(-100);
|
|
147
|
+
|
|
148
|
+
// Scrolling up: bottom strip needs redraw
|
|
149
|
+
expect(result.dirtyRegions).toHaveLength(1);
|
|
150
|
+
expect(result.dirtyRegions[0].y).toBe(500); // 600 - 100
|
|
151
|
+
expect(result.dirtyRegions[0].height).toBe(100);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should calculate horizontal scroll blit (right)', () => {
|
|
155
|
+
const result = calculateBlit(
|
|
156
|
+
{ x: 100, y: 0 }, // Current
|
|
157
|
+
{ x: 0, y: 0 }, // Last
|
|
158
|
+
viewportSize,
|
|
159
|
+
pinnedWidths
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(result.canBlit).toBe(true);
|
|
163
|
+
expect(result.deltaX).toBe(100);
|
|
164
|
+
expect(result.deltaY).toBe(0);
|
|
165
|
+
|
|
166
|
+
// Scrolling right: left strip needs redraw
|
|
167
|
+
expect(result.dirtyRegions).toHaveLength(1);
|
|
168
|
+
expect(result.dirtyRegions[0].x).toBe(100); // left pinned width
|
|
169
|
+
expect(result.dirtyRegions[0].width).toBe(100);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('should calculate horizontal scroll blit (left)', () => {
|
|
173
|
+
const result = calculateBlit(
|
|
174
|
+
{ x: 0, y: 0 }, // Current
|
|
175
|
+
{ x: 100, y: 0 }, // Last
|
|
176
|
+
viewportSize,
|
|
177
|
+
pinnedWidths
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
expect(result.canBlit).toBe(true);
|
|
181
|
+
expect(result.deltaX).toBe(-100);
|
|
182
|
+
|
|
183
|
+
// Scrolling left: right strip needs redraw
|
|
184
|
+
expect(result.dirtyRegions).toHaveLength(1);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should return full redraw for diagonal scroll', () => {
|
|
188
|
+
const result = calculateBlit(
|
|
189
|
+
{ x: 50, y: 50 },
|
|
190
|
+
{ x: 0, y: 0 },
|
|
191
|
+
viewportSize,
|
|
192
|
+
pinnedWidths
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
expect(result.canBlit).toBe(false);
|
|
196
|
+
expect(result.dirtyRegions[0].width).toBe(800);
|
|
197
|
+
expect(result.dirtyRegions[0].height).toBe(600);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should account for pinned widths', () => {
|
|
201
|
+
const result = calculateBlit(
|
|
202
|
+
{ x: 100, y: 0 },
|
|
203
|
+
{ x: 0, y: 0 },
|
|
204
|
+
viewportSize,
|
|
205
|
+
pinnedWidths
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
// Center region is between pinned columns
|
|
209
|
+
expect(result.sourceRect.x).toBe(100); // left pinned width
|
|
210
|
+
// Center width is 800 - 100 - 50 = 650
|
|
211
|
+
expect(result.sourceRect.width).toBe(550); // 650 - 100 (delta)
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
describe('blitLastFrame', () => {
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
(mockContext.drawImage as ReturnType<typeof vi.fn>).mockClear();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should return blitted=false when blitting not possible', () => {
|
|
221
|
+
const lastCanvas = new MockCanvas() as unknown as HTMLCanvasElement;
|
|
222
|
+
|
|
223
|
+
const result = blitLastFrame(
|
|
224
|
+
mockContext,
|
|
225
|
+
lastCanvas,
|
|
226
|
+
{ x: 0, y: 0 },
|
|
227
|
+
{ x: 0, y: 0 },
|
|
228
|
+
{ width: 800, height: 600 },
|
|
229
|
+
{ left: 0, right: 0 }
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
expect(result.blitted).toBe(false);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should perform blit when possible', () => {
|
|
236
|
+
const lastCanvas = new MockCanvas() as unknown as HTMLCanvasElement;
|
|
237
|
+
|
|
238
|
+
const result = blitLastFrame(
|
|
239
|
+
mockContext,
|
|
240
|
+
lastCanvas,
|
|
241
|
+
{ x: 0, y: 100 },
|
|
242
|
+
{ x: 0, y: 0 },
|
|
243
|
+
{ width: 800, height: 600 },
|
|
244
|
+
{ left: 0, right: 0 }
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
expect(result.blitted).toBe(true);
|
|
248
|
+
expect(result.regionsToDraw.length).toBeGreaterThan(0);
|
|
249
|
+
expect(mockContext.drawImage).toHaveBeenCalled();
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe('createBufferPair', () => {
|
|
254
|
+
it('should create front and back buffers', () => {
|
|
255
|
+
const buffers = createBufferPair(800, 600);
|
|
256
|
+
|
|
257
|
+
expect(buffers.front).toBeDefined();
|
|
258
|
+
expect(buffers.back).toBeDefined();
|
|
259
|
+
expect(buffers.frontCtx).toBeDefined();
|
|
260
|
+
expect(buffers.backCtx).toBeDefined();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should set correct dimensions', () => {
|
|
264
|
+
const buffers = createBufferPair(800, 600);
|
|
265
|
+
|
|
266
|
+
expect(buffers.front.width).toBe(800);
|
|
267
|
+
expect(buffers.front.height).toBe(600);
|
|
268
|
+
expect(buffers.back.width).toBe(800);
|
|
269
|
+
expect(buffers.back.height).toBe(600);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('should apply DPR scaling', () => {
|
|
273
|
+
const buffers = createBufferPair(800, 600, 2);
|
|
274
|
+
|
|
275
|
+
expect(buffers.front.width).toBe(1600);
|
|
276
|
+
expect(buffers.front.height).toBe(1200);
|
|
277
|
+
expect(buffers.front.style.width).toBe('800px');
|
|
278
|
+
expect(buffers.front.style.height).toBe('600px');
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe('swapBuffers', () => {
|
|
283
|
+
it('should swap front and back buffers', () => {
|
|
284
|
+
const buffers = createBufferPair(800, 600);
|
|
285
|
+
const originalFront = buffers.front;
|
|
286
|
+
const originalBack = buffers.back;
|
|
287
|
+
|
|
288
|
+
swapBuffers(buffers);
|
|
289
|
+
|
|
290
|
+
expect(buffers.front).toBe(originalBack);
|
|
291
|
+
expect(buffers.back).toBe(originalFront);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('should swap contexts too', () => {
|
|
295
|
+
const buffers = createBufferPair(800, 600);
|
|
296
|
+
const originalFrontCtx = buffers.frontCtx;
|
|
297
|
+
|
|
298
|
+
swapBuffers(buffers);
|
|
299
|
+
|
|
300
|
+
expect(buffers.frontCtx).toBe(buffers.backCtx);
|
|
301
|
+
});
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
describe('displayBuffer', () => {
|
|
305
|
+
beforeEach(() => {
|
|
306
|
+
(mockContext.drawImage as ReturnType<typeof vi.fn>).mockClear();
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should copy buffer to display context', () => {
|
|
310
|
+
const buffer = new MockCanvas() as unknown as HTMLCanvasElement;
|
|
311
|
+
|
|
312
|
+
displayBuffer(mockContext, buffer);
|
|
313
|
+
|
|
314
|
+
expect(mockContext.drawImage).toHaveBeenCalled();
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('resizeBufferPair', () => {
|
|
319
|
+
it('should resize both buffers', () => {
|
|
320
|
+
const buffers = createBufferPair(800, 600);
|
|
321
|
+
|
|
322
|
+
resizeBufferPair(buffers, 1024, 768);
|
|
323
|
+
|
|
324
|
+
expect(buffers.front.width).toBe(1024);
|
|
325
|
+
expect(buffers.front.height).toBe(768);
|
|
326
|
+
expect(buffers.back.width).toBe(1024);
|
|
327
|
+
expect(buffers.back.height).toBe(768);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should apply DPR on resize', () => {
|
|
331
|
+
const buffers = createBufferPair(800, 600);
|
|
332
|
+
|
|
333
|
+
resizeBufferPair(buffers, 1024, 768, 2);
|
|
334
|
+
|
|
335
|
+
expect(buffers.front.width).toBe(2048);
|
|
336
|
+
expect(buffers.front.height).toBe(1536);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('BlitState', () => {
|
|
341
|
+
let state: BlitState;
|
|
342
|
+
|
|
343
|
+
beforeEach(() => {
|
|
344
|
+
state = new BlitState();
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('updateScroll', () => {
|
|
348
|
+
it('should update and return last scroll position', () => {
|
|
349
|
+
const last = state.updateScroll(100, 200);
|
|
350
|
+
|
|
351
|
+
expect(last.x).toBe(0);
|
|
352
|
+
expect(last.y).toBe(0);
|
|
353
|
+
|
|
354
|
+
const next = state.updateScroll(150, 250);
|
|
355
|
+
expect(next.x).toBe(100);
|
|
356
|
+
expect(next.y).toBe(200);
|
|
357
|
+
});
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
describe('getScroll', () => {
|
|
361
|
+
it('should return current scroll position', () => {
|
|
362
|
+
state.updateScroll(100, 200);
|
|
363
|
+
|
|
364
|
+
const scroll = state.getScroll();
|
|
365
|
+
expect(scroll.x).toBe(100);
|
|
366
|
+
expect(scroll.y).toBe(200);
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
describe('lastCanvas', () => {
|
|
371
|
+
it('should store and retrieve last canvas', () => {
|
|
372
|
+
const canvas = new MockCanvas() as unknown as HTMLCanvasElement;
|
|
373
|
+
canvas.width = 800;
|
|
374
|
+
canvas.height = 600;
|
|
375
|
+
|
|
376
|
+
state.setLastCanvas(canvas);
|
|
377
|
+
|
|
378
|
+
const lastCanvas = state.getLastCanvas();
|
|
379
|
+
expect(lastCanvas).not.toBeNull();
|
|
380
|
+
expect(lastCanvas!.width).toBe(800);
|
|
381
|
+
expect(lastCanvas!.height).toBe(600);
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('should report hasLastFrame correctly', () => {
|
|
385
|
+
expect(state.hasLastFrame()).toBe(false);
|
|
386
|
+
|
|
387
|
+
state.setLastCanvas(new MockCanvas() as unknown as HTMLCanvasElement);
|
|
388
|
+
expect(state.hasLastFrame()).toBe(true);
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
describe('reset', () => {
|
|
393
|
+
it('should reset all state', () => {
|
|
394
|
+
state.updateScroll(100, 200);
|
|
395
|
+
state.setLastCanvas(new MockCanvas() as unknown as HTMLCanvasElement);
|
|
396
|
+
|
|
397
|
+
state.reset();
|
|
398
|
+
|
|
399
|
+
expect(state.getScroll()).toEqual({ x: 0, y: 0 });
|
|
400
|
+
expect(state.hasLastFrame()).toBe(false);
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe('Constants', () => {
|
|
406
|
+
it('MIN_BLIT_DELTA should be reasonable', () => {
|
|
407
|
+
expect(MIN_BLIT_DELTA).toBeGreaterThan(0);
|
|
408
|
+
expect(MIN_BLIT_DELTA).toBeLessThan(10);
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it('MAX_BLIT_DELTA_RATIO should be reasonable', () => {
|
|
412
|
+
expect(MAX_BLIT_DELTA_RATIO).toBeGreaterThan(0.5);
|
|
413
|
+
expect(MAX_BLIT_DELTA_RATIO).toBeLessThan(1);
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
describe('Edge cases', () => {
|
|
418
|
+
it('should handle zero viewport size', () => {
|
|
419
|
+
const result = calculateBlit(
|
|
420
|
+
{ x: 0, y: 0 },
|
|
421
|
+
{ x: 0, y: 0 },
|
|
422
|
+
{ width: 0, height: 0 },
|
|
423
|
+
{ left: 0, right: 0 }
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
expect(result.canBlit).toBe(false);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('should handle scroll delta equal to viewport', () => {
|
|
430
|
+
const result = calculateBlit(
|
|
431
|
+
{ x: 0, y: 600 },
|
|
432
|
+
{ x: 0, y: 0 },
|
|
433
|
+
{ width: 800, height: 600 },
|
|
434
|
+
{ left: 0, right: 0 }
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// Should not blit when delta equals viewport
|
|
438
|
+
expect(result.canBlit).toBe(false);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('should handle large pinned widths', () => {
|
|
442
|
+
const result = calculateBlit(
|
|
443
|
+
{ x: 0, y: 100 },
|
|
444
|
+
{ x: 0, y: 0 },
|
|
445
|
+
{ width: 800, height: 600 },
|
|
446
|
+
{ left: 400, right: 300 }
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
// Very small center area (100px)
|
|
450
|
+
expect(result.canBlit).toBe(true);
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
});
|