Skip to content

Commit e0e8e7d

Browse files
committed
Await the ErrorBoundary trace spans individually
1 parent 7f70cc4 commit e0e8e7d

1 file changed

Lines changed: 29 additions & 31 deletions

File tree

‎dev-packages/e2e-tests/test-applications/create-remix-app-express/tests/server-transactions.test.ts‎

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -75,44 +75,42 @@ test('Sends a loader span to Sentry', async ({ page }) => {
7575
});
7676

7777
test('Propagates the trace when the ErrorBoundary is triggered', async ({ page }) => {
78-
// Streamed spans are buffered before they flush, so spans from an earlier page load can still be
79-
// arriving here.
80-
const streamedSpans: SerializedStreamedSpan[] = [];
81-
void waitForStreamedSpans(APP_NAME, spans => {
82-
streamedSpans.push(...spans);
83-
return false;
84-
});
85-
8678
// The ErrorBoundary replaces the document, so there is no `sentry-trace` meta tag to read this
87-
// page load's trace off. A unique path identifies its server segment instead.
88-
const id = crypto.randomUUID();
89-
await page.goto(`/error-boundary-capture/${id}`);
79+
// page load's trace off. A unique path identifies all three of its spans instead, which lets each
80+
// one be awaited on its own rather than selected out of an accumulated trace.
81+
const path = `/error-boundary-capture/${crypto.randomUUID()}`;
82+
const hasPath = (span: SerializedStreamedSpan): boolean => span.attributes['url.path']?.value === path;
83+
84+
const serverSegmentSpanPromise = waitForStreamedSpan(
85+
APP_NAME,
86+
span => getSpanOp(span) === 'http.server' && span.is_segment && hasPath(span),
87+
);
88+
// Remix renders the document from inside the root loader, so that is the span the client
89+
// continues the trace from.
90+
const loaderSpanPromise = waitForStreamedSpan(
91+
APP_NAME,
92+
span => isDataFunction('loader', 'root')(span) && hasPath(span),
93+
);
94+
const pageloadSpanPromise = waitForStreamedSpan(
95+
APP_NAME,
96+
span => getSpanOp(span) === 'pageload' && span.is_segment && hasPath(span),
97+
);
98+
99+
await page.goto(path);
90100
await expect(page.locator('#event-id')).not.toBeEmpty();
91101

92-
const findServerSegmentSpan = () =>
93-
streamedSpans.find(
94-
span =>
95-
getSpanOp(span) === 'http.server' &&
96-
span.is_segment &&
97-
span.attributes['url.path']?.value === `/error-boundary-capture/${id}`,
98-
);
99-
await expect.poll(findServerSegmentSpan).toBeDefined();
100-
const serverSegmentSpan = findServerSegmentSpan()!;
102+
const serverSegmentSpan = await serverSegmentSpanPromise;
103+
const loaderSpan = await loaderSpanPromise;
104+
const pageloadSpan = await pageloadSpanPromise;
105+
101106
expect(serverSegmentSpan.name).toBe('GET error-boundary-capture/:id');
107+
expect(pageloadSpan.name).toBe('/error-boundary-capture/:id');
102108

103109
// The client continues the server trace, so its pageload span hangs off the root loader span.
104-
const findPageloadSpan = () =>
105-
streamedSpans.find(
106-
span => getSpanOp(span) === 'pageload' && span.is_segment && span.trace_id === serverSegmentSpan.trace_id,
107-
);
108-
await expect.poll(findPageloadSpan).toBeDefined();
109-
const pageloadSpan = findPageloadSpan()!;
110-
expect(pageloadSpan.name).toBe('/error-boundary-capture/:id');
110+
expect(loaderSpan.parent_span_id).toBe(serverSegmentSpan.span_id);
111+
expect(pageloadSpan.parent_span_id).toBe(loaderSpan.span_id);
112+
expect(pageloadSpan.trace_id).toBe(serverSegmentSpan.trace_id);
111113
expect(pageloadSpan.span_id).not.toBe(serverSegmentSpan.span_id);
112-
113-
const findLoaderSpan = () => streamedSpans.find(span => span.span_id === pageloadSpan.parent_span_id);
114-
await expect.poll(findLoaderSpan).toBeDefined();
115-
expect(findLoaderSpan()!.attributes['code.function.name']?.value).toBe('loader');
116114
});
117115

118116
test('Parameterizes a 2-level nested route on the server', async ({ page }) => {

0 commit comments

Comments
 (0)