Skip to content

Commit e71e0b6

Browse files
chargomeclaude
andcommitted
test(e2e): Move collectSpanNamesUntilSegment into @sentry-internal/test-utils
Add collectStreamedSpansUntilSegment and collectSpanNamesUntilSegment to the shared test-utils and replace the inlined "collect until the segment span arrived" check across the e2e suites. The names helper was copied into five Next.js apps and the same check was inlined in ~70 more places. Calls that also wait for specific child spans keep using collectStreamedSpans. No behavior change. Fixes #23944 Refs JS-3537 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 1e91b97 commit e71e0b6

57 files changed

Lines changed: 296 additions & 335 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎dev-packages/e2e-tests/test-applications/cloudflare-agent/tests/callable.test.ts‎

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils';
2+
import {
3+
collectStreamedSpans,
4+
collectStreamedSpansUntilSegment,
5+
getSpanOp,
6+
waitForStreamedSpan,
7+
} from '@sentry-internal/test-utils';
38

49
// The agent request segment is the Durable Object's `http.server` span. It has a parent because
510
// the worker propagates its trace over the RPC binding; the worker's own segment for the same URL
@@ -124,14 +129,12 @@ test('does not emit db.query spans for the agents runtime `cf_`-prefixed interna
124129
page,
125130
baseURL,
126131
}) => {
127-
const spansPromise = collectStreamedSpans('cloudflare-agent', spans =>
128-
spans.some(
129-
span =>
130-
getSpanOp(span) === 'http.server' &&
131-
span.is_segment &&
132-
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
133-
span.parent_span_id !== undefined,
134-
),
132+
const spansPromise = collectStreamedSpansUntilSegment(
133+
'cloudflare-agent',
134+
span =>
135+
getSpanOp(span) === 'http.server' &&
136+
span.attributes['url.path']?.value === '/agents/my-agent/user-123' &&
137+
span.parent_span_id !== undefined,
135138
);
136139

137140
await page.goto(baseURL!);

‎dev-packages/e2e-tests/test-applications/cloudflare-autoinstrument/tests/autoinstrument.test.ts‎

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils';
2+
import {
3+
collectStreamedSpans,
4+
collectStreamedSpansUntilSegment,
5+
getSpanOp,
6+
waitForStreamedSpan,
7+
} from '@sentry-internal/test-utils';
38
import { callRpc } from './agent-socket';
49

510
// The worker entry (`src/index.ts`) contains no Sentry calls at all — every
@@ -90,11 +95,9 @@ for (const { title, binding, agentClass } of [
9095
}
9196

9297
test('applies plain Durable Object instrumentation to a non-Agent class', async ({ baseURL }) => {
93-
const spansPromise = collectStreamedSpans('cloudflare-autoinstrument', spans =>
94-
spans.some(
95-
span =>
96-
getSpanOp(span) === 'http.server' && span.is_segment && span.attributes['url.path']?.value === '/plain-do',
97-
),
98+
const spansPromise = collectStreamedSpansUntilSegment(
99+
'cloudflare-autoinstrument',
100+
span => getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/plain-do',
98101
);
99102

100103
const res = await fetch(`${baseURL}/plain-do`);

‎dev-packages/e2e-tests/test-applications/elysia-bun/tests/transactions.test.ts‎

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment, getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils';
33

44
test('Sends a segment span for a successful route', async ({ baseURL, request }) => {
55
const spanPromise = waitForStreamedSpan('elysia-bun', span => {
@@ -70,9 +70,7 @@ test('Sends a segment span for an errored route', async ({ baseURL, request }) =
7070
});
7171

7272
test('Includes manually started spans with parent-child relationship', async ({ baseURL, request }) => {
73-
const spansPromise = collectStreamedSpans('elysia-bun', spans =>
74-
spans.some(span => span.name === 'GET /test-transaction' && span.is_segment),
75-
);
73+
const spansPromise = collectStreamedSpansUntilSegment('elysia-bun', 'GET /test-transaction');
7674

7775
await request.get(`${baseURL}/test-transaction`);
7876

@@ -102,9 +100,7 @@ test('Includes manually started spans with parent-child relationship', async ({
102100
});
103101

104102
test('Creates lifecycle spans for Elysia hooks', async ({ baseURL, request }) => {
105-
const spansPromise = collectStreamedSpans('elysia-bun', spans =>
106-
spans.some(span => span.name === 'GET /test-success' && span.is_segment),
107-
);
103+
const spansPromise = collectStreamedSpansUntilSegment('elysia-bun', 'GET /test-success');
108104

109105
await request.get(`${baseURL}/test-success`);
110106

@@ -128,9 +124,7 @@ test('Creates lifecycle spans for Elysia hooks', async ({ baseURL, request }) =>
128124
});
129125

130126
test('Names handler spans after the route instead of "<unknown>"', async ({ baseURL, request }) => {
131-
const spansPromise = collectStreamedSpans('elysia-bun', spans =>
132-
spans.some(span => span.name === 'GET /with-middleware/test' && span.is_segment),
133-
);
127+
const spansPromise = collectStreamedSpansUntilSegment('elysia-bun', 'GET /with-middleware/test');
134128

135129
// Use a route with middleware so there are child handler spans
136130
await request.get(`${baseURL}/with-middleware/test`);
@@ -153,9 +147,7 @@ test('Names handler spans after the route instead of "<unknown>"', async ({ base
153147
});
154148

155149
test('Creates lifecycle spans for route-specific middleware', async ({ baseURL, request }) => {
156-
const spansPromise = collectStreamedSpans('elysia-bun', spans =>
157-
spans.some(span => span.name === 'GET /with-middleware/test' && span.is_segment),
158-
);
150+
const spansPromise = collectStreamedSpansUntilSegment('elysia-bun', 'GET /with-middleware/test');
159151

160152
await request.get(`${baseURL}/with-middleware/test`);
161153

‎dev-packages/e2e-tests/test-applications/nextjs-14/tests/generation-functions.test.ts‎

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans, waitForError, waitForStreamedSpan } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment, waitForError, waitForStreamedSpan } from '@sentry-internal/test-utils';
33

4-
// The generation-function spans are children of the segment span, which ends last, so accumulate
5-
// spans until the segment for this request arrives.
64
function collectSpansForTarget(httpTarget: string) {
7-
return collectStreamedSpans('nextjs-14', spans =>
8-
spans.some(span => span.is_segment && span.attributes['http.target']?.value === httpTarget),
9-
);
5+
return collectStreamedSpansUntilSegment('nextjs-14', span => span.attributes['http.target']?.value === httpTarget);
106
}
117

128
test('Should emit a span for a generateMetadata() function invocation', async ({ page }) => {

‎dev-packages/e2e-tests/test-applications/nextjs-14/tests/request-instrumentation.test.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils';
33

44
test('Should send a fetch span', async ({ page }) => {
55
// The fetch spans are children of the segment span, which ends last.
6-
const spansPromise = collectStreamedSpans('nextjs-14', spans =>
7-
spans.some(span => span.name === 'GET /request-instrumentation' && span.is_segment),
8-
);
6+
const spansPromise = collectStreamedSpansUntilSegment('nextjs-14', 'GET /request-instrumentation');
97

108
await page.goto(`/request-instrumentation`);
119

‎dev-packages/e2e-tests/test-applications/nextjs-15/tests/prefetch-spans.test.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils';
33

44
test('Prefetch client spans should have a http.request.prefetch attribute', async ({ page }) => {
55
test.skip(
@@ -8,9 +8,7 @@ test('Prefetch client spans should have a http.request.prefetch attribute', asyn
88
);
99

1010
// The prefetch span is a child of the pageload segment span, which ends last.
11-
const spansPromise = collectStreamedSpans('nextjs-15', spans =>
12-
spans.some(span => span.name === '/prefetching' && span.is_segment),
13-
);
11+
const spansPromise = collectStreamedSpansUntilSegment('nextjs-15', '/prefetching');
1412

1513
await page.goto(`/prefetching`);
1614

‎dev-packages/e2e-tests/test-applications/nextjs-15/tests/server-components.test.ts‎

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils';
33

44
test('Sends a span for a request to app router with URL', async ({ page }) => {
5-
const spansPromise = collectStreamedSpans('nextjs-15', spans =>
6-
spans.some(
7-
span =>
8-
span.name === 'GET /parameterized/[one]/beep/[two]' &&
9-
span.is_segment &&
10-
String(span.attributes['http.target']?.value).startsWith('/parameterized/1337/beep/42'),
11-
),
5+
const spansPromise = collectStreamedSpansUntilSegment(
6+
'nextjs-15',
7+
span =>
8+
span.name === 'GET /parameterized/[one]/beep/[two]' &&
9+
String(span.attributes['http.target']?.value).startsWith('/parameterized/1337/beep/42'),
1210
);
1311

1412
await page.goto('/parameterized/1337/beep/42');

‎dev-packages/e2e-tests/test-applications/nextjs-16-bun/tests/server-components.test.ts‎

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,12 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans } from '@sentry-internal/test-utils';
3-
4-
// Streamed spans are flushed across multiple envelopes as they end, so the server-component child spans
5-
// can arrive in a different (earlier) envelope than the `is_segment` root span. Accumulate spans across
6-
// envelopes until the root span (which ends last) is seen.
7-
function collectSpanNamesUntilSegment(segmentName: string): Promise<string[]> {
8-
return collectStreamedSpans('nextjs-16-bun', spans =>
9-
spans.some(span => span.name === segmentName && span.is_segment),
10-
).then(spans => spans.map(span => span.name));
11-
}
2+
import { collectSpanNamesUntilSegment, collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils';
123

134
test('Sends a span for a request to app router with URL', async ({ page }) => {
14-
const spansPromise = collectStreamedSpans('nextjs-16-bun', spans =>
15-
spans.some(
16-
span =>
17-
span.name === 'GET /parameterized/[one]/beep/[two]' &&
18-
span.is_segment &&
19-
String(span.attributes['http.target']?.value).startsWith('/parameterized/1337/beep/42'),
20-
),
5+
const spansPromise = collectStreamedSpansUntilSegment(
6+
'nextjs-16-bun',
7+
span =>
8+
span.name === 'GET /parameterized/[one]/beep/[two]' &&
9+
String(span.attributes['http.target']?.value).startsWith('/parameterized/1337/beep/42'),
2110
);
2211

2312
await page.goto('/parameterized/1337/beep/42');
@@ -54,7 +43,7 @@ test('Sends a span for a request to app router with URL', async ({ page }) => {
5443
test('Will create spans for every server component and metadata generation functions when visiting a page', async ({
5544
page,
5645
}) => {
57-
const spanNamesPromise = collectSpanNamesUntilSegment('GET /nested-layout');
46+
const spanNamesPromise = collectSpanNamesUntilSegment('nextjs-16-bun', 'GET /nested-layout');
5847

5948
await page.goto('/nested-layout');
6049

@@ -73,7 +62,7 @@ test('Will create spans for every server component and metadata generation funct
7362
test('Will create spans for every server component and metadata generation functions when visiting a dynamic page', async ({
7463
page,
7564
}) => {
76-
const spanNamesPromise = collectSpanNamesUntilSegment('GET /nested-layout/[dynamic]');
65+
const spanNamesPromise = collectSpanNamesUntilSegment('nextjs-16-bun', 'GET /nested-layout/[dynamic]');
7766

7867
await page.goto('/nested-layout/123');
7968

‎dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/prefetch-spans.test.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans } from '@sentry-internal/test-utils';
2+
import { collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils';
33
import { isDevMode } from './isDevMode';
44

55
test('Prefetch client spans should have a http.request.prefetch attribute', async ({ page }) => {
66
test.skip(isDevMode, "Prefetch requests don't have the prefetch header in dev mode");
77

88
// The prefetch span is a child of the pageload segment span, which ends last.
9-
const spansPromise = collectStreamedSpans('nextjs-16-cf-workers', spans =>
10-
spans.some(span => span.name === '/prefetching' && span.is_segment),
11-
);
9+
const spansPromise = collectStreamedSpansUntilSegment('nextjs-16-cf-workers', '/prefetching');
1210

1311
await page.goto(`/prefetching`);
1412

‎dev-packages/e2e-tests/test-applications/nextjs-16-cf-workers/tests/server-components.test.ts‎

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,13 @@
11
import { expect, test } from '@playwright/test';
2-
import { collectStreamedSpans } from '@sentry-internal/test-utils';
3-
4-
// Streamed spans are flushed across multiple envelopes as they end, so the server-component child spans
5-
// can arrive in a different (earlier) envelope than the `is_segment` root span. Accumulate spans across
6-
// envelopes until the root span (which ends last) is seen.
7-
function collectSpanNamesUntilSegment(segmentName: string): Promise<string[]> {
8-
return collectStreamedSpans('nextjs-16-cf-workers', spans =>
9-
spans.some(span => span.name === segmentName && span.is_segment),
10-
).then(spans => spans.map(span => span.name));
11-
}
2+
import { collectSpanNamesUntilSegment, collectStreamedSpansUntilSegment } from '@sentry-internal/test-utils';
123

134
// TODO: Server component tests need SDK adjustments for Cloudflare Workers
145
test.skip('Sends a span for a request to app router with URL', async ({ page }) => {
15-
const spansPromise = collectStreamedSpans('nextjs-16-cf-workers', spans =>
16-
spans.some(
17-
span =>
18-
span.name === 'GET /parameterized/[one]/beep/[two]' &&
19-
span.is_segment &&
20-
String(span.attributes['http.target']?.value).startsWith('/parameterized/1337/beep/42'),
21-
),
6+
const spansPromise = collectStreamedSpansUntilSegment(
7+
'nextjs-16-cf-workers',
8+
span =>
9+
span.name === 'GET /parameterized/[one]/beep/[two]' &&
10+
String(span.attributes['http.target']?.value).startsWith('/parameterized/1337/beep/42'),
2211
);
2312

2413
await page.goto('/parameterized/1337/beep/42');
@@ -56,7 +45,7 @@ test.skip('Sends a span for a request to app router with URL', async ({ page })
5645
test.skip('Will create spans for every server component and metadata generation functions when visiting a page', async ({
5746
page,
5847
}) => {
59-
const spanNamesPromise = collectSpanNamesUntilSegment('GET /nested-layout');
48+
const spanNamesPromise = collectSpanNamesUntilSegment('nextjs-16-cf-workers', 'GET /nested-layout');
6049

6150
await page.goto('/nested-layout');
6251

@@ -76,7 +65,7 @@ test.skip('Will create spans for every server component and metadata generation
7665
test.skip('Will create spans for every server component and metadata generation functions when visiting a dynamic page', async ({
7766
page,
7867
}) => {
79-
const spanNamesPromise = collectSpanNamesUntilSegment('GET /nested-layout/[dynamic]');
68+
const spanNamesPromise = collectSpanNamesUntilSegment('nextjs-16-cf-workers', 'GET /nested-layout/[dynamic]');
8069

8170
await page.goto('/nested-layout/123');
8271

0 commit comments

Comments
 (0)