Skip to content

Commit e4cc238

Browse files
committed
Add a collectStreamedTrace test helper
Spans are buffered per trace before they flush, so an earlier page load's spans can still be arriving when a test starts collecting. This resolves with only the spans sharing the matched segment's trace, so tests asserting on one request's children cannot pick up leftovers.
1 parent 6d1c1e9 commit e4cc238

2 files changed

Lines changed: 53 additions & 0 deletions

File tree

dev-packages/test-utils/src/event-proxy-server.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,58 @@ export function collectStreamedSpans(
637637
}).then(() => collected);
638638
}
639639

640+
/**
641+
* Accumulate streamed spans until a segment span matches `isSegment`, then resolve with only the
642+
* spans that share that segment's trace.
643+
*
644+
* Spans are buffered per trace before they flush, so spans from an earlier page load can still be
645+
* arriving when a test starts collecting. Scoping to a single trace is what keeps those leftovers
646+
* from ending the wait early or from failing an assertion about the trace under test. Prefer this
647+
* over {@link collectStreamedSpans} whenever a test asserts on the children of one specific request.
648+
*
649+
* `isDone` receives the spans collected so far for that trace and defaults to "the segment span has
650+
* arrived". Pass it when a test needs particular children, since a child can flush before or after
651+
* its segment.
652+
*
653+
* @example
654+
* ```ts
655+
* const spans = await collectStreamedTrace(PROXY_SERVER_NAME, span => span.name === 'GET /redis');
656+
* expect(spans.map(span => span.name)).toContainEqual('redis-set');
657+
* ```
658+
*
659+
* @example
660+
* ```ts
661+
* // Wait for the segment and at least two of its db spans
662+
* const spans = await collectStreamedTrace(
663+
* PROXY_SERVER_NAME,
664+
* span => span.name === 'GET /redis',
665+
* spansOfTrace => spansOfTrace.filter(span => getSpanOp(span) === 'db.query').length >= 2,
666+
* );
667+
* ```
668+
*/
669+
export function collectStreamedTrace(
670+
proxyServerName: string,
671+
isSegment: (span: SerializedStreamedSpan) => boolean,
672+
isDone?: (spansOfTrace: SerializedStreamedSpan[]) => boolean,
673+
): Promise<SerializedStreamedSpan[]> {
674+
const collected: SerializedStreamedSpan[] = [];
675+
let traceId: string | undefined;
676+
677+
return waitForStreamedSpans(proxyServerName, spans => {
678+
collected.push(...spans);
679+
680+
// Only the first matching segment counts, so a later page load on the same route cannot move
681+
// the collection to a different trace halfway through.
682+
traceId ??= collected.find(span => span.is_segment && isSegment(span))?.trace_id;
683+
684+
if (traceId === undefined) {
685+
return false;
686+
}
687+
688+
return isDone ? isDone(collected.filter(span => span.trace_id === traceId)) : true;
689+
}).then(() => collected.filter(span => span.trace_id === traceId));
690+
}
691+
640692
/**
641693
* Helper to get the span operation from a Span V2 JSON object.
642694
*

dev-packages/test-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ export {
1212
waitForStreamedSpans,
1313
waitForStreamedSpanEnvelope,
1414
collectStreamedSpans,
15+
collectStreamedTrace,
1516
getSpanOp,
1617
} from './event-proxy-server';
1718
export type { SerializedStreamedSpan } from '@sentry/core';

0 commit comments

Comments
 (0)