Files
PriceUpdaterAppv2/tests/tui/utils/PerformanceOptimizer.test.js

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);
});
});
});