Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/utils/src/lib/profiler/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const PROFILER_ENABLED = 'CP_PROFILING';
Comment thread
BioPhoton marked this conversation as resolved.
Outdated
299 changes: 299 additions & 0 deletions packages/utils/src/lib/profiler/profiler.int.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
import { performance } from 'node:perf_hooks';
import { beforeEach, describe, expect, it } from 'vitest';
import type { ActionTrackEntryPayload } from '../user-timing-extensibility-api.type.js';
import { Profiler } from './profiler.js';

describe('Profiler Integration', () => {
let profiler: Profiler<Record<string, ActionTrackEntryPayload>>;

beforeEach(() => {
performance.clearMarks();
performance.clearMeasures();

profiler = new Profiler({
prefix: 'cp',
track: 'CLI',
trackGroup: 'Code Pushup',
color: 'primary-dark',
tracks: {
utils: { track: 'Utils', color: 'primary' },
core: { track: 'Core', color: 'primary-light' },
},
enabled: true,
});
});

it('should create complete performance timeline for sync operation', () => {
const result = profiler.measure('sync-test', () =>
Array.from({ length: 1000 }, (_, i) => i).reduce(
(sum, num) => sum + num,
0,
),
);

expect(result).toBe(499_500);

const marks = performance.getEntriesByType('mark');
const measures = performance.getEntriesByType('measure');

expect(marks).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'cp:sync-test:start',
detail: expect.objectContaining({
devtools: expect.objectContaining({ dataType: 'track-entry' }),
}),
}),
expect.objectContaining({
name: 'cp:sync-test:end',
detail: expect.objectContaining({
devtools: expect.objectContaining({ dataType: 'track-entry' }),
}),
}),
]),
);

expect(measures).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'cp:sync-test',
duration: expect.any(Number),
detail: expect.objectContaining({
devtools: expect.objectContaining({ dataType: 'track-entry' }),
}),
}),
]),
);
});

it('should create complete performance timeline for async operation', async () => {
const result = await profiler.measureAsync('async-test', async () => {
await new Promise(resolve => setTimeout(resolve, 10));
return 'async-result';
});

expect(result).toBe('async-result');

const marks = performance.getEntriesByType('mark');
const measures = performance.getEntriesByType('measure');

expect(marks).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'cp:async-test:start',
detail: expect.objectContaining({
devtools: expect.objectContaining({ dataType: 'track-entry' }),
}),
}),
expect.objectContaining({
name: 'cp:async-test:end',
detail: expect.objectContaining({
devtools: expect.objectContaining({ dataType: 'track-entry' }),
}),
}),
]),
);

expect(measures).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'cp:async-test',
duration: expect.any(Number),
detail: expect.objectContaining({
devtools: expect.objectContaining({ dataType: 'track-entry' }),
}),
}),
]),
);
});

it('should handle nested measurements correctly', () => {
profiler.measure('outer', () => {
profiler.measure('inner', () => 'inner-result');
return 'outer-result';
});

const marks = performance.getEntriesByType('mark');
const measures = performance.getEntriesByType('measure');

expect(marks).toHaveLength(4);
expect(measures).toHaveLength(2);

const markNames = marks.map(m => m.name);
expect(markNames).toStrictEqual(
expect.arrayContaining([
'cp:outer:start',
'cp:outer:end',
'cp:inner:start',
'cp:inner:end',
]),
);

const measureNames = measures.map(m => m.name);
expect(measureNames).toStrictEqual(
expect.arrayContaining(['cp:outer', 'cp:inner']),
);
});

it('should create markers with proper metadata', () => {
profiler.marker('test-marker', {
color: 'warning',
tooltipText: 'Test marker tooltip',
properties: [
['event', 'test-event'],
['timestamp', Date.now()],
],
});

const marks = performance.getEntriesByType('mark');
expect(marks).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'test-marker',
detail: {
devtools: expect.objectContaining({
dataType: 'marker',
color: 'warning',
tooltipText: 'Test marker tooltip',
properties: [
['event', 'test-event'],
['timestamp', expect.any(Number)],
],
}),
},
}),
]),
);
});

it('should create proper DevTools payloads for tracks', () => {
profiler.measure('track-test', (): string => 'result', {
success: result => ({
properties: [['result', result]],
tooltipText: 'Track test completed',
}),
});

const measures = performance.getEntriesByType('measure');
expect(measures).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'cp:track-test',
detail: {
devtools: expect.objectContaining({
dataType: 'track-entry',
track: 'CLI',
trackGroup: 'Code Pushup',
color: 'primary-dark',
properties: [['result', 'result']],
tooltipText: 'Track test completed',
}),
},
}),
]),
);
});

it('should merge track defaults with measurement options', () => {
profiler.measure('sync-op', () => 'sync-result', {
success: result => ({
properties: [
['operation', 'sync'],
['result', result],
],
}),
});

const measures = performance.getEntriesByType('measure');
expect(measures).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'cp:sync-op',
detail: {
devtools: expect.objectContaining({
dataType: 'track-entry',
track: 'CLI',
trackGroup: 'Code Pushup',
color: 'primary-dark',
properties: [
['operation', 'sync'],
['result', 'sync-result'],
],
}),
},
}),
]),
);
});

it('should mark errors with red color in DevTools', () => {
const error = new Error('Test error');

expect(() => {
profiler.measure('error-test', () => {
throw error;
});
}).toThrow(error);

const measures = performance.getEntriesByType('measure');
expect(measures).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
detail: {
devtools: expect.objectContaining({
color: 'error',
properties: expect.arrayContaining([
['Error Type', 'Error'],
['Error Message', 'Test error'],
]),
}),
},
}),
]),
);
});

it('should include error metadata in DevTools properties', () => {
const customError = new TypeError('Custom type error');

expect(() => {
profiler.measure('custom-error-test', () => {
throw customError;
});
}).toThrow(customError);

const measures = performance.getEntriesByType('measure');
expect(measures).toStrictEqual(
expect.arrayContaining([
expect.objectContaining({
detail: {
devtools: expect.objectContaining({
properties: expect.arrayContaining([
['Error Type', 'TypeError'],
['Error Message', 'Custom type error'],
]),
}),
},
}),
]),
);
});

it('should not create performance entries when disabled', async () => {
profiler.setEnabled(false);

const syncResult = profiler.measure('disabled-sync', () => 'sync');
expect(syncResult).toBe('sync');

const asyncResult = profiler.measureAsync(
'disabled-async',
async () => 'async',
);
await expect(asyncResult).resolves.toBe('async');

profiler.marker('disabled-marker');

expect(performance.getEntriesByType('mark')).toHaveLength(0);
expect(performance.getEntriesByType('measure')).toHaveLength(0);
});
});
Loading