UNPKG

@zendesk/react-measure-timing-hooks

Version:

react hooks for measuring time to interactive and time to render of components

814 lines 41.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); require("./testUtility/asciiTimelineSerializer"); const vitest_1 = require("vitest"); const constants_1 = require("./constants"); const getDynamicQuietWindowDuration_1 = require("./getDynamicQuietWindowDuration"); const matchSpan = __importStar(require("./matchSpan")); const makeTimeline_1 = require("./testUtility/makeTimeline"); const processSpans_1 = require("./testUtility/processSpans"); const TraceManager_1 = require("./TraceManager"); const GOOGLES_QUIET_WINDOW_DURATION = 2_000; const GOOGLES_CLUSTER_PADDING = 1_000; const GOOGLES_HEAVY_CLUSTER_THRESHOLD = 250; const cpuIdleProcessorOptions = { getQuietWindowDuration: () => GOOGLES_QUIET_WINDOW_DURATION, clusterPadding: GOOGLES_CLUSTER_PADDING, heavyClusterThreshold: GOOGLES_HEAVY_CLUSTER_THRESHOLD, }; (0, vitest_1.describe)('TraceManager with Capture Interactivity', () => { let reportFn; let generateId; let reportErrorFn; const DEFAULT_COLDBOOT_TIMEOUT_DURATION = 45_000; vitest_1.vitest.useFakeTimers({ now: 0, }); (0, vitest_1.beforeEach)(() => { reportFn = vitest_1.vitest.fn(); generateId = vitest_1.vitest.fn().mockReturnValue('trace-id'); reportErrorFn = vitest_1.vitest.fn(); }); (0, vitest_1.afterEach)(() => { vitest_1.vitest.clearAllMocks(); vitest_1.vitest.clearAllTimers(); }); (0, vitest_1.it)('correctly captures trace while waiting the long tasks to settle', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1' }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(50)}------<===5s===>---------${makeTimeline_1.Check} Time: ${0} ${2_000} ${2_001} ${2_001 + constants_1.DEFAULT_INTERACTIVE_TIMEOUT_DURATION} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +2000 ⋯>-| time (ms) | 0 2000 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(2_000); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(2_000); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('completes the trace with waiting-for-interactive-timeout', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const interactiveTimeout = 5_000; const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: { ...cpuIdleProcessorOptions, timeout: interactiveTimeout, }, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(interactiveTimeout)} Time: ${0} ${2_000} ${2_050} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +2000 ⋯>-| time (ms) | 0 2000 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(2_000); (0, vitest_1.expect)(report.interruptionReason).toBe('waiting-for-interactive-timeout'); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBeNull(); (0, vitest_1.expect)(report.status).toBe('ok'); }); (0, vitest_1.it)('completes the trace when interrupted during waiting for capture interactive to finish', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.interrupt-during-long-task-operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [matchSpan.withName('end')], interruptOnSpans: [matchSpan.withName('interrupt')], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}---------${(0, makeTimeline_1.Render)('end', 0)}---------${(0, makeTimeline_1.Render)('interrupt', 0)}--${(0, makeTimeline_1.LongTask)(100)}--${makeTimeline_1.Check} Time: ${0} ${200} ${201} ${300} ${5_000} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +200 ⋯>-| time (ms) | 0 200 `); (0, vitest_1.expect)(report.name).toBe('ticket.interrupt-during-long-task-operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBeNull(); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBe('matched-on-interrupt'); }); (0, vitest_1.it)('timeouts the trace while deboucing AND the last span is past the timeout duration', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.debounce-then-interrupted-operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], debounceOnSpans: [{ name: 'debounce' }], captureInteractive: cpuIdleProcessorOptions, debounceWindow: 300, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.Render)('debounce', 0)}------${makeTimeline_1.Check} Time: ${0} ${50} ${100} ${DEFAULT_COLDBOOT_TIMEOUT_DURATION + 100} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end debounce timeline | |-<⋯ +50 ⋯>-|-<⋯ +50 ⋯>-| time (ms) | 0 50 100 `); (0, vitest_1.expect)(report.name).toBe('ticket.debounce-then-interrupted-operation'); (0, vitest_1.expect)(report.status).toBe('interrupted'); (0, vitest_1.expect)(report.duration).toBeNull(); (0, vitest_1.expect)(report.interruptionReason).toBe('timeout'); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBeNull(); }); (0, vitest_1.it)('completes the trace when debouncing is done AND is waiting for capture interactive to finish', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const CUSTOM_CAPTURE_INTERACTIVE_TIMEOUT = 300; const tracer = traceManager.createTracer({ name: 'ticket.debounce-then-interrupted-operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], debounceOnSpans: [{ name: 'debounce' }], captureInteractive: { ...cpuIdleProcessorOptions, timeout: CUSTOM_CAPTURE_INTERACTIVE_TIMEOUT, }, debounceWindow: 100, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}---------------------${(0, makeTimeline_1.Render)('debounce', 100)}------------------${makeTimeline_1.Check} Time: ${0} ${DEFAULT_COLDBOOT_TIMEOUT_DURATION - 500} ${DEFAULT_COLDBOOT_TIMEOUT_DURATION - 499} ${DEFAULT_COLDBOOT_TIMEOUT_DURATION + 1} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +44500 ⋯>-| time (ms) | 0 44500 `); (0, vitest_1.expect)(report.name).toBe('ticket.debounce-then-interrupted-operation'); (0, vitest_1.expect)(report.duration).toBe(44_500); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBeNull(); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBe('timeout'); }); (0, vitest_1.it)('uses getQuietWindowDuration from capture interactive config', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const CUSTOM_QUIET_WINDOW_DURATION = 2_000; const TRACE_DURATION = 1_000; const getQuietWindowDuration = vitest_1.vitest .fn() .mockReturnValue(CUSTOM_QUIET_WINDOW_DURATION); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: { ...cpuIdleProcessorOptions, timeout: 100, getQuietWindowDuration, }, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(5)}------------${(0, makeTimeline_1.LongTask)(5)}-----${makeTimeline_1.Check} Time: ${0} ${TRACE_DURATION} ${1_001} ${1_050} ${1_200} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +1000 ⋯>-| time (ms) | 0 1000 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(TRACE_DURATION); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBeNull(); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBe('waiting-for-interactive-timeout'); (0, vitest_1.expect)(getQuietWindowDuration).toHaveBeenCalled(); (0, vitest_1.expect)(getQuietWindowDuration).toHaveBeenCalledWith(1_055, TRACE_DURATION); }); (0, vitest_1.it)('uses createQuietWindowDurationCalculator for getQuietWindowDuration in capture interactive config', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const TRACE_DURATION = 1_000; const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: { ...cpuIdleProcessorOptions, timeout: 1_000, getQuietWindowDuration: (0, getDynamicQuietWindowDuration_1.createQuietWindowDurationCalculator)(), }, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----------${(0, makeTimeline_1.LongTask)(5)}----------------${(0, makeTimeline_1.LongTask)(50)}---${makeTimeline_1.Check} Time: ${0} ${TRACE_DURATION} ${1_001} ${1_945} ${2_001} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +1000 ⋯>-| time (ms) | 0 1000 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(TRACE_DURATION); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBeNull(); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBe('waiting-for-interactive-timeout'); }); (0, vitest_1.it)('No long tasks after FMP, FirstCPUIdle immediately after FMP + quiet window', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}----------${(0, makeTimeline_1.LongTask)(400)}-----${makeTimeline_1.Check} Time: ${0} ${200} ${4_600} ${5_000} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +200 ⋯>-| time (ms) | 0 200 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(200); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('One light cluster after FMP, FirstCPUIdle at FMP', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}----------${(0, makeTimeline_1.LongTask)(50)}------${(0, makeTimeline_1.LongTask)(50)}-----${(0, makeTimeline_1.LongTask)(50)}---------${makeTimeline_1.Check} Time: ${0} ${200} ${300} ${400} ${450} ${3_050} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +200 ⋯>-| time (ms) | 0 200 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(200); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('One heavy cluster after FMP, FirstCPUIdle after the cluster', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}--${(0, makeTimeline_1.LongTask)(50)}}-${(0, makeTimeline_1.LongTask)(200)}-----${(0, makeTimeline_1.LongTask)(50)}-----------${makeTimeline_1.Check} Time: ${0} ${200} ${300} ${400} ${650} ${3_200} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end task(50) task(200) task(50) timeline | |-<⋯ +200 ⋯>-|-----------[++++]------[+++++++++++++++++++++++]------[++++]- time (ms) | 0 200 300 400 650 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(700); // TESTING TODO: are we testing enough of first idle time? (0, vitest_1.expect)(report.additionalDurations.completeTillInteractive).toBe(500); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('Multiple heavy clusters, FirstCPUIdle updated to end of last cluster', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(200)}-----${(0, makeTimeline_1.LongTask)(200)}-----${(0, makeTimeline_1.LongTask)(200)}-----${makeTimeline_1.Check} Time: ${0} ${200} ${300} ${900} ${1_500} ${3_800} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end task(200) task(200) task(200) timeline | |------------|------[+++++++++++]-<⋯ +400 ⋯>--[+++++++++++]-<⋯ +400 ⋯>-[+++++++++++] time (ms) | 0 200 300 900 1500 `); const lastLongTask = spans.at(-2); const expectedResult = lastLongTask.startTime.now + lastLongTask.duration; (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(expectedResult); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('Checking before the quiet window has passed - no long tasks processed, FirstCPUIdle not found', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${makeTimeline_1.Check} Time: ${0} ${200} ${2_400} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +200 ⋯>-| time (ms) | 0 200 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(200); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('completes the trace with one heavy cluster followed by two light clusters, value is after 1st heavy cluster', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(200)}-----${(0, makeTimeline_1.LongTask)(100)}-----${(0, makeTimeline_1.LongTask)(200)}-----${(0, makeTimeline_1.LongTask)(200)}-----${makeTimeline_1.Check} Time: ${0} ${200} ${300} ${600} ${1_700} ${2_900} ${4_650} } `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end task(200) task(100) timeline | |-<⋯ +200 ⋯>-|-----------[+++++++++++++++++++++++]------------[++++++++++]- time (ms) | 0 200 300 600 `); const lastHeavyClusterLongTask = spans.at(3); const expectedResult = lastHeavyClusterLongTask.startTime.now + lastHeavyClusterLongTask.duration; (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(expectedResult); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('completes the trace with continuous heavy clusters', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${(0, makeTimeline_1.LongTask)(300)}-----${makeTimeline_1.Check} Time: ${0} ${200} ${550} ${900} ${1_250} ${1_600} ${1_950} ${2_300} ${2_650} ${3_000} ${3_350} ${3_700} ${4_050} ${4_400} ${4_750} ${5_100} ${7_450} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | end task(300) task(300) task(300) task(300) task(300) task(300) task(300) events | start task(300) task(300) task(300) task(300) task(300) task(300) task(300) timeline | |-|-----[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]-[++]- time (ms) | 0 200 550 900 1250 1600 1950 2300 2650 3000 3350 3700 4050 4400 4750 5100 `); (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(5_400); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('completes the trace with a light cluster followed by a heavy cluster a second later, FirstCPUIdle updated', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}-----${(0, makeTimeline_1.Render)('end', 0)}-----${(0, makeTimeline_1.LongTask)(50)}----------${(0, makeTimeline_1.LongTask)(50)}----------${(0, makeTimeline_1.LongTask)(50)}--------${(0, makeTimeline_1.LongTask)(50)}---------${(0, makeTimeline_1.LongTask)(200)}-------${(0, makeTimeline_1.LongTask)(200)}--------${makeTimeline_1.Check} Time: ${0} ${200} ${300} ${350} ${1_450} ${1_550} ${1_650} ${1_900} ${4_150} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | task(50) task(50) events | start end task(50) task(50) task(200) task(200) timeline | |------------|------[+]--[+]-<⋯ +1050 ⋯>[+]--[+]---[+++++++++++]---[+++++++++++]- time (ms) | 0 200 300 350 1450 1550 1650 1900 `); const lastLongTask = spans.at(-2); const expectedResult = lastLongTask.startTime.now + lastLongTask.duration; (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(expectedResult); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); (0, vitest_1.it)('completes the trace with a long task overlapping FMP, FirstCPUIdle after the long task', () => { const traceManager = new TraceManager_1.TraceManager({ relationSchemas: { ticket: { ticketId: String } }, reportFn, generateId, reportErrorFn, }); const tracer = traceManager.createTracer({ name: 'ticket.operation', type: 'operation', relationSchemaName: 'ticket', requiredSpans: [{ name: 'end' }], captureInteractive: cpuIdleProcessorOptions, variants: { cold_boot: { timeout: DEFAULT_COLDBOOT_TIMEOUT_DURATION, }, }, }); tracer.start({ relatedTo: { ticketId: '1', }, variant: 'cold_boot', }); // prettier-ignore const { spans } = (0, makeTimeline_1.getSpansFromTimeline) ` Events: ${(0, makeTimeline_1.Render)('start', 0)}----------${(0, makeTimeline_1.LongTask)(110, { start: 150 })}----${(0, makeTimeline_1.Render)('end', 0)}-----${makeTimeline_1.Check} Time: ${0} ${150} ${200} ${2_500} `; (0, processSpans_1.processSpans)(spans, traceManager); (0, vitest_1.expect)(reportFn).toHaveBeenCalled(); const report = reportFn.mock.calls[0][0]; (0, vitest_1.expect)(report.entries.map((spanAndAnnotation) => spanAndAnnotation.span.performanceEntry)).toMatchInlineSnapshot(` events | start end timeline | |-<⋯ +200 ⋯>-| time (ms) | 0 200 `); const lastLongTask = spans.at(-2); const expectedResult = lastLongTask.startTime.now + lastLongTask.duration; (0, vitest_1.expect)(report.name).toBe('ticket.operation'); (0, vitest_1.expect)(report.duration).toBe(200); (0, vitest_1.expect)(report.additionalDurations.startTillInteractive).toBe(expectedResult); (0, vitest_1.expect)(report.status).toBe('ok'); (0, vitest_1.expect)(report.interruptionReason).toBeUndefined(); }); }); //# sourceMappingURL=TraceManagerWithCaptureInteractive.test.js.map