240 lines
5.9 KiB
JavaScript
240 lines
5.9 KiB
JavaScript
const PerformanceOptimizer = require("../../../src/tui/utils/PerformanceOptimizer.js");
|
|
|
|
describe("PerformanceOptimizer", () => {
|
|
let optimizer;
|
|
|
|
beforeEach(() => {
|
|
optimizer = new PerformanceOptimizer();
|
|
});
|
|
|
|
afterEach(() => {
|
|
optimizer.destroy();
|
|
});
|
|
|
|
describe("debounce", () => {
|
|
it("should debounce function calls", (done) => {
|
|
let callCount = 0;
|
|
const testFunction = () => {
|
|
callCount++;
|
|
};
|
|
|
|
const debouncedFunction = optimizer.debounce(testFunction, 100, "test");
|
|
|
|
// Call multiple times rapidly
|
|
debouncedFunction();
|
|
debouncedFunction();
|
|
debouncedFunction();
|
|
|
|
// Should not have been called yet
|
|
expect(callCount).toBe(0);
|
|
|
|
// Wait for debounce delay
|
|
setTimeout(() => {
|
|
expect(callCount).toBe(1);
|
|
done();
|
|
}, 150);
|
|
});
|
|
});
|
|
|
|
describe("throttle", () => {
|
|
it("should throttle function calls", (done) => {
|
|
let callCount = 0;
|
|
const testFunction = () => {
|
|
callCount++;
|
|
};
|
|
|
|
const throttledFunction = optimizer.throttle(testFunction, 100, "test");
|
|
|
|
// Call multiple times rapidly
|
|
throttledFunction();
|
|
throttledFunction();
|
|
throttledFunction();
|
|
|
|
// Should have been called once immediately
|
|
expect(callCount).toBe(1);
|
|
|
|
// Wait and call again
|
|
setTimeout(() => {
|
|
throttledFunction();
|
|
expect(callCount).toBe(2);
|
|
done();
|
|
}, 150);
|
|
});
|
|
});
|
|
|
|
describe("memoize", () => {
|
|
it("should memoize function results", () => {
|
|
let callCount = 0;
|
|
const expensiveFunction = (x, y) => {
|
|
callCount++;
|
|
return x + y;
|
|
};
|
|
|
|
const memoizedFunction = optimizer.memoize(expensiveFunction);
|
|
|
|
// First call
|
|
const result1 = memoizedFunction(1, 2);
|
|
expect(result1).toBe(3);
|
|
expect(callCount).toBe(1);
|
|
|
|
// Second call with same arguments
|
|
const result2 = memoizedFunction(1, 2);
|
|
expect(result2).toBe(3);
|
|
expect(callCount).toBe(1); // Should not have called function again
|
|
|
|
// Third call with different arguments
|
|
const result3 = memoizedFunction(2, 3);
|
|
expect(result3).toBe(5);
|
|
expect(callCount).toBe(2);
|
|
});
|
|
|
|
it("should limit cache size", () => {
|
|
const testFunction = (x) => x * 2;
|
|
const memoizedFunction = optimizer.memoize(testFunction, undefined, 2);
|
|
|
|
// Fill cache beyond limit
|
|
memoizedFunction(1);
|
|
memoizedFunction(2);
|
|
memoizedFunction(3); // Should evict first entry
|
|
|
|
// Verify first entry was evicted
|
|
let callCount = 0;
|
|
const countingFunction = (x) => {
|
|
callCount++;
|
|
return x * 2;
|
|
};
|
|
const countingMemoized = optimizer.memoize(
|
|
countingFunction,
|
|
undefined,
|
|
2
|
|
);
|
|
|
|
countingMemoized(1);
|
|
countingMemoized(2);
|
|
expect(callCount).toBe(2);
|
|
|
|
countingMemoized(3); // Should evict entry for 1
|
|
expect(callCount).toBe(3);
|
|
|
|
countingMemoized(1); // Should call function again since it was evicted
|
|
expect(callCount).toBe(4);
|
|
});
|
|
});
|
|
|
|
describe("createVirtualScrolling", () => {
|
|
it("should calculate virtual scrolling data correctly", () => {
|
|
const items = Array.from({ length: 100 }, (_, i) => ({
|
|
id: i,
|
|
name: `Item ${i}`,
|
|
}));
|
|
const result = optimizer.createVirtualScrolling(items, 300, 30, 150);
|
|
|
|
expect(result.totalHeight).toBe(3000); // 100 items * 30px each
|
|
expect(result.startIndex).toBe(5); // 150px / 30px per item
|
|
expect(result.visibleItems.length).toBeGreaterThan(0);
|
|
expect(result.visibleItems.length).toBeLessThanOrEqual(12); // Visible count + buffer
|
|
});
|
|
});
|
|
|
|
describe("createLazyLoading", () => {
|
|
it("should create lazy loading data correctly", () => {
|
|
const items = Array.from({ length: 100 }, (_, i) => ({
|
|
id: i,
|
|
name: `Item ${i}`,
|
|
}));
|
|
const result = optimizer.createLazyLoading(items, 10, 5);
|
|
|
|
expect(result.startIndex).toBe(5); // 10 - 5
|
|
expect(result.endIndex).toBe(20); // 10 + 5*2
|
|
expect(result.loadedItems.length).toBe(15); // 20 - 5
|
|
expect(result.hasMore).toBe(true);
|
|
expect(result.hasPrevious).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("memory management", () => {
|
|
it("should track memory usage", () => {
|
|
const stats = optimizer.getMemoryUsage();
|
|
expect(stats).toHaveProperty("estimatedSizeBytes");
|
|
expect(stats).toHaveProperty("estimatedSizeMB");
|
|
expect(stats).toHaveProperty("cacheEntries");
|
|
expect(stats).toHaveProperty("eventListeners");
|
|
expect(stats).toHaveProperty("activeTimers");
|
|
expect(stats).toHaveProperty("memoryPressure");
|
|
});
|
|
|
|
it("should clean up expired cache entries", () => {
|
|
// Add some cache entries
|
|
optimizer.componentCache.set("test1", {
|
|
data: "test",
|
|
timestamp: Date.now() - 10000,
|
|
});
|
|
optimizer.componentCache.set("test2", {
|
|
data: "test",
|
|
timestamp: Date.now(),
|
|
});
|
|
|
|
optimizer.cleanupExpiredCache(5000); // 5 second max age
|
|
|
|
expect(optimizer.componentCache.has("test1")).toBe(false);
|
|
expect(optimizer.componentCache.has("test2")).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("event listener management", () => {
|
|
it("should register and cleanup event listeners", () => {
|
|
const mockTarget = {
|
|
addEventListener: jest.fn(),
|
|
removeEventListener: jest.fn(),
|
|
};
|
|
|
|
const handler = () => {};
|
|
|
|
optimizer.registerEventListener(
|
|
"component1",
|
|
"click",
|
|
handler,
|
|
mockTarget
|
|
);
|
|
expect(mockTarget.addEventListener).toHaveBeenCalledWith(
|
|
"click",
|
|
handler
|
|
);
|
|
|
|
optimizer.cleanupEventListeners("component1");
|
|
expect(mockTarget.removeEventListener).toHaveBeenCalledWith(
|
|
"click",
|
|
handler
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("batched updates", () => {
|
|
it("should batch updates correctly", (done) => {
|
|
let batchedUpdates = [];
|
|
const updateFunction = async (batch) => {
|
|
batchedUpdates.push(batch);
|
|
};
|
|
|
|
const batchedUpdate = optimizer.createBatchedUpdate(
|
|
updateFunction,
|
|
3,
|
|
50
|
|
);
|
|
|
|
// Add updates
|
|
batchedUpdate("update1");
|
|
batchedUpdate("update2");
|
|
batchedUpdate("update3");
|
|
batchedUpdate("update4");
|
|
|
|
// Wait for batching
|
|
setTimeout(() => {
|
|
expect(batchedUpdates.length).toBeGreaterThan(0);
|
|
expect(batchedUpdates[0]).toEqual(["update1", "update2", "update3"]);
|
|
done();
|
|
}, 100);
|
|
});
|
|
});
|
|
});
|