UNPKG

@zendesk/retrace

Version:

define and capture Product Operation Traces along with computed metrics with an optional friendly React beacon API

451 lines 19.9 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 }); const vitest_1 = require("vitest"); const matchSpan = __importStar(require("./matchSpan")); const mockRelations = { ticketId: '123', }; const mockEntryBase = { type: 'element', name: 'testEntry', startTime: { now: Date.now(), epoch: Date.now(), }, relatedTo: mockRelations, attributes: { attr1: 'value1', attr2: 2, }, duration: 100, status: 'ok', }; const mockPerformanceEntry = { ...mockEntryBase, performanceEntry: { entryType: 'element', name: 'testEntry', startTime: 0, duration: 0, toJSON: () => ({}), }, }; // Mock data for ComponentRenderTraceEntry const mockComponentEntry = { ...mockEntryBase, type: 'component-render', errorInfo: undefined, isIdle: true, renderedOutput: 'content', renderCount: 1, }; const mockAnnotation = { id: '', occurrence: 1, operationRelativeEndTime: 0, operationRelativeStartTime: 0, recordedInState: 'active', labels: [], }; const mockContext = { input: { id: '123', relatedTo: mockRelations, startTime: { now: Date.now(), epoch: Date.now(), }, variant: 'origin', }, definition: { name: 'test', type: 'operation', relationSchemaName: 'ticket', relationSchema: { ticketId: String }, requiredSpans: [() => true], computedSpanDefinitions: {}, computedValueDefinitions: {}, variants: { origin: { timeout: 10_000 }, }, }, recordedItemsByLabel: {}, recordedItems: new Set(), }; // TESTING TODO: ask chatgpt to add 'or' and 'not' tests (0, vitest_1.describe)('matchSpan', () => { (0, vitest_1.describe)('name', () => { (0, vitest_1.it)('should return true for a matching entry based on name', () => { const matcher = matchSpan.withName('testEntry'); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return true for function matchers for name', () => { const matcher = matchSpan.withName((n) => n.startsWith('test')); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return true for regex matchers for name', () => { const matcher = matchSpan.withName(/^test/); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for a non-matching span based on name', () => { const matcher = matchSpan.withName('nonMatchingEntry'); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('performanceEntryName', () => { (0, vitest_1.it)('should return true for a matching span based on performanceEntryName', () => { const matcher = matchSpan.withPerformanceEntryName('testEntry'); const mockSpanAndAnnotation = { span: mockPerformanceEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for a non-matching performanceEntryName', () => { const matcher = matchSpan.withPerformanceEntryName('nonMatchingEntry'); const mockSpanAndAnnotation = { span: mockPerformanceEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('type', () => { (0, vitest_1.describe)('for Native Performance Entry', () => { (0, vitest_1.it)('should return true for matching attributes', () => { const matcher = matchSpan.withType('element'); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for non-matching attributes', () => { const matcher = matchSpan.withType('component-render'); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('for ComponentRenderTraceEntry', () => { (0, vitest_1.it)('should return true for a matching ComponentRenderTraceEntry', () => { const matcher = matchSpan.withAllConditions(matchSpan.withType('component-render'), matchSpan.withName('testEntry')); const mockSpanAndAnnotation = { span: mockComponentEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for a non-matching ComponentRenderTraceEntry', () => { const matcher = matchSpan.withAllConditions(matchSpan.withType('component-render'), matchSpan.withName('nonMatchingEntry')); const mockSpanAndAnnotation = { span: mockComponentEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); }); (0, vitest_1.describe)('status', () => { (0, vitest_1.it)('should return true when status does match', () => { const matcher = matchSpan.withStatus('ok'); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false when status does not match', () => { const matcher = matchSpan.withStatus('error'); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('occurrence', () => { (0, vitest_1.it)('should return true for occurrence matching', () => { const matcher = matchSpan.withAllConditions(matchSpan.withName('testEntry'), matchSpan.withOccurrence(1)); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for non-matching occurrence', () => { const matcher = matchSpan.withAllConditions(matchSpan.withName('testEntry'), matchSpan.withOccurrence(2)); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('attributes', () => { (0, vitest_1.it)('should return true for matching attributes', () => { const matcher = matchSpan.withAttributes({ attr1: 'value1', }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for non-matching attributes', () => { const matcher = matchSpan.withAttributes({ attr1: 'wrongValue', }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('matchingRelation', () => { (0, vitest_1.it)('should return true when relatedTo does match', () => { const matcher = matchSpan.withAllConditions(matchSpan.withName('testEntry'), matchSpan.withMatchingRelations(['ticketId'])); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false when relatedTo does not match', () => { const matcher = matchSpan.withAllConditions(matchSpan.withName('testEntry'), matchSpan.withMatchingRelations(['ticketId', 'userId'])); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, { ...mockContext, input: { ...mockContext.input, relatedTo: { ticketId: '123', userId: '123', }, }, })).toBe(false); }); }); (0, vitest_1.describe)('isIdle', () => { const mockMatcher = matchSpan.withAllConditions(matchSpan.withName('testEntry'), matchSpan.whenIdle(true)); (0, vitest_1.it)('should return true for isIdle matching', () => { const mockSpanAndAnnotation = { span: mockComponentEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(mockMatcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false for non-matching isIdle', () => { const mockEntry = { ...mockComponentEntry, isIdle: false }; const mockSpanAndAnnotation = { span: mockEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(mockMatcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('combination of conditions', () => { (0, vitest_1.it)('should return true when all conditions match', () => { const matcher = matchSpan.withAllConditions(matchSpan.withName('testEntry'), matchSpan.withPerformanceEntryName('testEntry'), matchSpan.withAttributes({ attr1: 'value1' }), matchSpan.withMatchingRelations(['ticketId']), matchSpan.withStatus('ok'), matchSpan.withType('element')); const mockSpanAndAnnotation = { span: mockPerformanceEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false when all conditions match but name', () => { const matcher = matchSpan.withAllConditions(matchSpan.withName('testEntries'), // does not match matchSpan.withPerformanceEntryName('testEntry'), matchSpan.withAttributes({ attr1: 'value1' }), matchSpan.withMatchingRelations(['ticketId']), matchSpan.withStatus('ok'), matchSpan.withType('element')); const mockSpanAndAnnotation = { span: mockPerformanceEntry, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('fn', () => { (0, vitest_1.it)('should return true when the custom function matches', () => { const matcher = matchSpan.fromDefinition({ fn: ({ span }) => span.name.startsWith('test'), }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false when the custom function does not match', () => { const matcher = matchSpan.fromDefinition({ fn: ({ span }) => span.name === 'nonMatchingEntry', }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); }); (0, vitest_1.describe)('fromDefinition', () => { (0, vitest_1.it)('should correctly handle standard definition objects', () => { const matcher = matchSpan.fromDefinition({ name: 'testEntry', type: 'element', attributes: { attr1: 'value1' }, }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.describe)('oneOf', () => { (0, vitest_1.it)('should return true when one of the conditions matches', () => { const matcher = matchSpan.fromDefinition({ oneOf: [ { name: 'nonMatchingEntry' }, { name: 'testEntry' }, { name: 'anotherNonMatchingEntry' }, ], }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should return false when none of the conditions match', () => { const matcher = matchSpan.fromDefinition({ oneOf: [ { name: 'nonMatchingEntry1' }, { name: 'nonMatchingEntry2' }, { name: 'nonMatchingEntry3' }, ], }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(false); }); (0, vitest_1.it)('should handle complex conditions within oneOf', () => { const matcher = matchSpan.fromDefinition({ oneOf: [ { name: 'nonMatchingEntry', type: 'element', }, { name: 'testEntry', type: 'component-render', // This doesn't match our entry }, { name: 'testEntry', type: 'element', attributes: { attr1: 'value1' }, }, ], }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should carry over tags from sub-matchers', () => { const matcher = matchSpan.fromDefinition({ oneOf: [ { name: 'nonMatchingEntry' }, { name: 'testEntry', isIdle: true }, ], }); (0, vitest_1.expect)('idleCheck' in matcher).toBe(true); (0, vitest_1.expect)(matcher.idleCheck).toBe(true); }); (0, vitest_1.it)('should work with nested fromDefinition calls', () => { // First, create a matcher using fromDefinition const nestedMatcher = matchSpan.fromDefinition({ type: 'element', }); // Then use that matcher along with another in withOneOfConditions const combinedMatcher = matchSpan.withOneOfConditions(nestedMatcher, matchSpan.withName('nonMatchingEntry')); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(combinedMatcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); (0, vitest_1.it)('should handle nested oneOf conditions', () => { const matcher = matchSpan.fromDefinition({ oneOf: [ { name: 'nonMatchingEntry1' }, { oneOf: [{ name: 'nonMatchingEntry2' }, { name: 'testEntry' }], }, ], }); const mockSpanAndAnnotation = { span: mockEntryBase, annotation: mockAnnotation, }; (0, vitest_1.expect)(matcher(mockSpanAndAnnotation, mockContext)).toBe(true); }); }); }); }); //# sourceMappingURL=matchSpan.test.js.map