Skip to content

Commit 39ccf25

Browse files
Add comprehensive test coverage for all components
1 parent 4d88b24 commit 39ccf25

File tree

4 files changed

+319
-3
lines changed

4 files changed

+319
-3
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ jobs:
2424
- run: npm ci
2525
- run: npm run build
2626
- run: npm run lint
27-
# Uncomment when tests are added
28-
# - run: npm test
27+
- run: npm test
2928

3029
publish-npm:
3130
needs: build
@@ -38,7 +37,7 @@ jobs:
3837
node-version: '18.x'
3938
registry-url: 'https://registry.npmjs.org/'
4039
- run: npm ci
41-
- run: npm run build
40+
- run: npm run build:all
4241
- run: npm publish
4342
env:
4443
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

src/__tests__/browser.test.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { BrowserLogger, BrowserPerformanceMonitor, BrowserExecutionTracker } from '../browser';
2+
3+
// Mock the performance API
4+
const originalPerformance = global.performance;
5+
beforeAll(() => {
6+
// Create a custom performance object with memory property
7+
const mockPerformance = {
8+
now: jest.fn().mockImplementation(() => Date.now()),
9+
// Add other required properties from Performance interface
10+
timing: {} as any,
11+
navigation: {} as any,
12+
timeOrigin: 0,
13+
// Custom property for Chrome's performance.memory
14+
memory: {
15+
usedJSHeapSize: 1000000,
16+
totalJSHeapSize: 2000000,
17+
jsHeapSizeLimit: 4000000
18+
}
19+
};
20+
21+
// @ts-ignore - Ignore type checking for the test mock
22+
global.performance = mockPerformance;
23+
});
24+
25+
afterAll(() => {
26+
global.performance = originalPerformance;
27+
});
28+
29+
describe('BrowserLogger', () => {
30+
let browserLogger: BrowserLogger;
31+
let consoleInfoSpy: jest.SpyInstance;
32+
let consoleWarnSpy: jest.SpyInstance;
33+
let consoleErrorSpy: jest.SpyInstance;
34+
let consoleDebugSpy: jest.SpyInstance;
35+
let consoleLogSpy: jest.SpyInstance;
36+
37+
beforeEach(() => {
38+
browserLogger = new BrowserLogger();
39+
40+
// Spy on console methods
41+
consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation();
42+
consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
43+
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
44+
consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation();
45+
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
46+
});
47+
48+
afterEach(() => {
49+
consoleInfoSpy.mockRestore();
50+
consoleWarnSpy.mockRestore();
51+
consoleErrorSpy.mockRestore();
52+
consoleDebugSpy.mockRestore();
53+
consoleLogSpy.mockRestore();
54+
});
55+
56+
test('info method should log a message', () => {
57+
browserLogger.info('Test info message');
58+
expect(consoleInfoSpy).toHaveBeenCalledWith('Test info message', []);
59+
});
60+
61+
test('warn method should log a warning message', () => {
62+
browserLogger.warn('Test warning message');
63+
expect(consoleWarnSpy).toHaveBeenCalledWith('Test warning message', []);
64+
});
65+
66+
test('error method should log an error message', () => {
67+
browserLogger.error('Test error message');
68+
expect(consoleErrorSpy).toHaveBeenCalledWith('Test error message', []);
69+
});
70+
71+
test('debug method should log a debug message', () => {
72+
browserLogger.debug('Test debug message');
73+
expect(consoleDebugSpy).toHaveBeenCalledWith('Test debug message', []);
74+
});
75+
76+
test('debug messages should be suppressed in production mode', () => {
77+
browserLogger.setMode('prod');
78+
browserLogger.debug('This should not be logged');
79+
expect(consoleDebugSpy).not.toHaveBeenCalled();
80+
});
81+
82+
test('track should return the result of the tracked function', () => {
83+
const result = browserLogger.track(() => 'test-result');
84+
expect(result).toBe('test-result');
85+
expect(consoleLogSpy).toHaveBeenCalled();
86+
});
87+
88+
test('setMode and getMode should work correctly', () => {
89+
expect(browserLogger.getMode()).toBe('dev');
90+
browserLogger.setMode('staging');
91+
expect(browserLogger.getMode()).toBe('staging');
92+
browserLogger.setMode('prod');
93+
expect(browserLogger.getMode()).toBe('prod');
94+
});
95+
});
96+
97+
describe('BrowserPerformanceMonitor', () => {
98+
let performanceMonitor: BrowserPerformanceMonitor;
99+
100+
beforeEach(() => {
101+
performanceMonitor = new BrowserPerformanceMonitor({ defaultThreshold: 100 });
102+
});
103+
104+
test('startTimer and endTimer should measure duration', () => {
105+
const id = performanceMonitor.startTimer('test-timer');
106+
const duration = performanceMonitor.endTimer(id);
107+
expect(duration).toBeGreaterThanOrEqual(0);
108+
});
109+
110+
test('isBottleneck should return true for durations above threshold', () => {
111+
expect(performanceMonitor.isBottleneck(150)).toBe(true);
112+
expect(performanceMonitor.isBottleneck(50)).toBe(false);
113+
});
114+
115+
test('getMemoryUsage should return memory usage information', () => {
116+
const memoryUsage = performanceMonitor.getMemoryUsage();
117+
expect(memoryUsage).toHaveProperty('heapUsed');
118+
expect(memoryUsage).toHaveProperty('heapTotal');
119+
expect(memoryUsage.heapUsed).toBe(1000000);
120+
expect(memoryUsage.heapTotal).toBe(2000000);
121+
});
122+
});
123+
124+
describe('BrowserExecutionTracker', () => {
125+
let executionTracker: BrowserExecutionTracker;
126+
127+
beforeEach(() => {
128+
executionTracker = new BrowserExecutionTracker({ defaultThreshold: 100 });
129+
});
130+
131+
test('track should return the result of the tracked function', () => {
132+
const result = executionTracker.track(() => 'test-result');
133+
expect(result).toBe('test-result');
134+
});
135+
136+
test('track should record execution data', () => {
137+
executionTracker.track(() => {}, { label: 'testFunction' });
138+
139+
const flowChart = executionTracker.generateFlowChart();
140+
expect(flowChart).toContain('testFunction');
141+
expect(flowChart).toContain('Execution Flow');
142+
});
143+
144+
test('getCallStack should return the current call stack', () => {
145+
executionTracker.track(() => {
146+
const callStack = executionTracker.getCallStack();
147+
expect(callStack.length).toBe(1);
148+
expect(callStack[0]).toBe('testFunction');
149+
}, { label: 'testFunction' });
150+
});
151+
152+
test('clear should reset execution data', () => {
153+
executionTracker.track(() => {}, { label: 'testFunction' });
154+
155+
let flowChart = executionTracker.generateFlowChart();
156+
expect(flowChart).toContain('testFunction');
157+
158+
executionTracker.clear();
159+
160+
flowChart = executionTracker.generateFlowChart();
161+
expect(flowChart).not.toContain('testFunction');
162+
});
163+
});

src/__tests__/execution.test.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { ExecutionTracker } from '../trackers/execution';
2+
3+
describe('ExecutionTracker', () => {
4+
let executionTracker: ExecutionTracker;
5+
6+
beforeEach(() => {
7+
executionTracker = new ExecutionTracker({ defaultThreshold: 100 });
8+
});
9+
10+
test('track should return the result of the tracked function', () => {
11+
const result = executionTracker.track(() => 'test-result');
12+
expect(result).toBe('test-result');
13+
});
14+
15+
test('track should handle errors in tracked functions', () => {
16+
const errorMessage = 'Test error';
17+
18+
expect(() => {
19+
executionTracker.track(() => {
20+
throw new Error(errorMessage);
21+
});
22+
}).toThrow(errorMessage);
23+
});
24+
25+
test('track should record execution data', () => {
26+
executionTracker.track(() => {
27+
// Simulate some work
28+
const start = Date.now();
29+
while (Date.now() - start < 10) {
30+
// Busy wait to ensure some time passes
31+
}
32+
}, { label: 'testFunction' });
33+
34+
const flowChart = executionTracker.generateFlowChart();
35+
expect(flowChart).toContain('testFunction');
36+
expect(flowChart).toContain('Execution Flow');
37+
});
38+
39+
test('track should detect slow functions', () => {
40+
executionTracker.track(() => {
41+
// Simulate slow work
42+
const start = Date.now();
43+
while (Date.now() - start < 150) {
44+
// Busy wait to ensure threshold is exceeded
45+
}
46+
}, { label: 'slowFunction', threshold: 100 });
47+
48+
const flowChart = executionTracker.generateFlowChart();
49+
expect(flowChart).toContain('slowFunction');
50+
expect(flowChart).toContain('SLOW');
51+
});
52+
53+
test('track should handle nested function calls', () => {
54+
executionTracker.track(() => {
55+
// Outer function
56+
executionTracker.track(() => {
57+
// Inner function
58+
}, { label: 'innerFunction' });
59+
}, { label: 'outerFunction' });
60+
61+
const flowChart = executionTracker.generateFlowChart();
62+
expect(flowChart).toContain('outerFunction');
63+
expect(flowChart).toContain('innerFunction');
64+
});
65+
66+
test('getCallStack should return the current call stack', () => {
67+
executionTracker.track(() => {
68+
executionTracker.track(() => {
69+
const callStack = executionTracker.getCallStack();
70+
expect(callStack.length).toBe(2);
71+
expect(callStack[0]).toBe('outerFunction');
72+
expect(callStack[1]).toBe('innerFunction');
73+
}, { label: 'innerFunction' });
74+
}, { label: 'outerFunction' });
75+
});
76+
77+
test('clear should reset execution data', () => {
78+
executionTracker.track(() => {}, { label: 'testFunction' });
79+
80+
let flowChart = executionTracker.generateFlowChart();
81+
expect(flowChart).toContain('testFunction');
82+
83+
executionTracker.clear();
84+
85+
flowChart = executionTracker.generateFlowChart();
86+
expect(flowChart).not.toContain('testFunction');
87+
});
88+
});

src/__tests__/performance.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { PerformanceMonitor } from '../trackers/performance';
2+
3+
describe('PerformanceMonitor', () => {
4+
let performanceMonitor: PerformanceMonitor;
5+
6+
beforeEach(() => {
7+
performanceMonitor = new PerformanceMonitor({ defaultThreshold: 100 });
8+
});
9+
10+
test('startTimer and endTimer should measure duration', () => {
11+
const id = performanceMonitor.startTimer('test-timer');
12+
13+
// Simulate some work
14+
const start = Date.now();
15+
while (Date.now() - start < 10) {
16+
// Busy wait to ensure some time passes
17+
}
18+
19+
const duration = performanceMonitor.endTimer(id);
20+
expect(duration).toBeGreaterThan(0);
21+
});
22+
23+
test('endTimer should throw error for non-existent timer', () => {
24+
expect(() => {
25+
performanceMonitor.endTimer('non-existent-timer');
26+
}).toThrow();
27+
});
28+
29+
test('isBottleneck should return true for durations above threshold', () => {
30+
expect(performanceMonitor.isBottleneck(150)).toBe(true);
31+
expect(performanceMonitor.isBottleneck(50)).toBe(false);
32+
});
33+
34+
test('isBottleneck should use custom threshold when provided', () => {
35+
expect(performanceMonitor.isBottleneck(75, 50)).toBe(true);
36+
expect(performanceMonitor.isBottleneck(75, 100)).toBe(false);
37+
});
38+
39+
test('getMemoryUsage should return memory usage information', () => {
40+
const memoryUsage = performanceMonitor.getMemoryUsage();
41+
expect(memoryUsage).toHaveProperty('heapUsed');
42+
expect(memoryUsage).toHaveProperty('heapTotal');
43+
expect(memoryUsage).toHaveProperty('external');
44+
expect(memoryUsage).toHaveProperty('rss');
45+
46+
expect(typeof memoryUsage.heapUsed).toBe('number');
47+
expect(typeof memoryUsage.heapTotal).toBe('number');
48+
expect(typeof memoryUsage.external).toBe('number');
49+
expect(typeof memoryUsage.rss).toBe('number');
50+
});
51+
52+
test('generateSuggestion should provide different suggestions based on duration', () => {
53+
const slowSuggestion = performanceMonitor.generateSuggestion('slowFunction', 1500);
54+
const mediumSuggestion = performanceMonitor.generateSuggestion('mediumFunction', 600);
55+
const fastSuggestion = performanceMonitor.generateSuggestion('fastFunction', 50);
56+
57+
expect(slowSuggestion).toContain('slowFunction');
58+
expect(slowSuggestion).toContain('caching');
59+
60+
expect(mediumSuggestion).toContain('mediumFunction');
61+
expect(mediumSuggestion).toContain('blocking');
62+
63+
expect(fastSuggestion).toContain('fastFunction');
64+
expect(fastSuggestion).toContain('optimizations');
65+
});
66+
});

0 commit comments

Comments
 (0)