@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
JavaScript
;
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