Skip to content

Commit 045963d

Browse files
test(e2e): Port the SolidStart 2 E2E app to span streaming
Removes the static trace lifecycle pin and rewrites the specs against streamed span v2. Server action children are collected across envelopes; the http.server segment is matched on url.path because an unparameterized streamed name is the method alone. Co-Authored-By: Cursor Grok 4.6 <cursoragent@cursor.com>
1 parent cfaa457 commit 045963d

4 files changed

Lines changed: 83 additions & 126 deletions

File tree

‎dev-packages/e2e-tests/test-applications/solidstart-2/server/plugins/sentry.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { definePlugin } from 'nitro';
44
// Runs once at server startup. Build-time instrumentation means no `--import` preload is needed.
55
export default definePlugin(() => {
66
Sentry.init({
7-
traceLifecycle: 'static',
87
dsn: process.env.E2E_TEST_DSN,
98
environment: 'qa', // dynamic sampling bias to keep transactions
109
tracesSampleRate: 1.0, // Capture 100% of the transactions

‎dev-packages/e2e-tests/test-applications/solidstart-2/src/entry-client.tsx‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { solidRouterBrowserTracingIntegration } from '@sentry/solidstart/solidro
44
import { StartClient, mount } from '@solidjs/start/client';
55

66
Sentry.init({
7-
traceLifecycle: 'static',
87
// We can't use env variables here, seems like they are stripped
98
// out in production builds.
109
dsn: 'https://public@dsn.ingest.sentry.io/1337',
Lines changed: 49 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,119 +1,81 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForTransaction } from '@sentry-internal/test-utils';
2+
import { getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils';
33

4-
test('sends a pageload transaction', async ({ page }) => {
5-
const transactionPromise = waitForTransaction('solidstart-2', async transactionEvent => {
6-
return transactionEvent?.transaction === '/' && transactionEvent.contexts?.trace?.op === 'pageload';
4+
test('sends a pageload span', async ({ page }) => {
5+
const spanPromise = waitForStreamedSpan('solidstart-2', span => {
6+
return span.name === '/' && getSpanOp(span) === 'pageload' && span.is_segment;
77
});
88

99
await page.goto('/');
10-
const pageloadTransaction = await transactionPromise;
10+
const pageloadSpan = await spanPromise;
1111

12-
expect(pageloadTransaction).toMatchObject({
13-
contexts: {
14-
trace: {
15-
op: 'pageload',
16-
origin: 'auto.pageload.browser',
17-
data: {
18-
'sentry.segment.name.source': 'route',
19-
'url.template': '/',
20-
'url.path': '/',
21-
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/$/),
22-
},
23-
},
24-
},
25-
transaction: '/',
26-
transaction_info: {
27-
source: 'route',
28-
},
12+
expect(getSpanOp(pageloadSpan)).toBe('pageload');
13+
expect(pageloadSpan.attributes).toMatchObject({
14+
'sentry.origin': { value: 'auto.pageload.browser', type: 'string' },
15+
'sentry.op': { value: 'pageload', type: 'string' },
16+
'sentry.segment.name.source': { value: 'route', type: 'string' },
17+
'url.template': { value: '/', type: 'string' },
18+
'url.path': { value: '/', type: 'string' },
19+
'url.full': { value: expect.stringMatching(/^https?:\/\/localhost:\d+\/$/), type: 'string' },
2920
});
3021
});
3122

32-
test('sends a navigation transaction with parametrized route', async ({ page }) => {
33-
const transactionPromise = waitForTransaction('solidstart-2', async transactionEvent => {
34-
return transactionEvent?.transaction === '/users/:id' && transactionEvent.contexts?.trace?.op === 'navigation';
23+
test('sends a navigation span with parametrized route', async ({ page }) => {
24+
const spanPromise = waitForStreamedSpan('solidstart-2', span => {
25+
return span.name === '/users/:id' && getSpanOp(span) === 'navigation' && span.is_segment;
3526
});
3627

3728
await page.goto(`/`);
3829
await page.locator('#navLink').click();
39-
const navigationTransaction = await transactionPromise;
30+
const navigationSpan = await spanPromise;
4031

41-
expect(navigationTransaction).toMatchObject({
42-
contexts: {
43-
trace: {
44-
op: 'navigation',
45-
origin: 'auto.navigation.solidstart.solidrouter',
46-
data: {
47-
'sentry.segment.name.source': 'route',
48-
'url.template': '/users/:id',
49-
'url.path': '/users/5',
50-
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/5$/),
51-
},
52-
},
53-
},
54-
transaction: '/users/:id',
55-
transaction_info: {
56-
source: 'route',
57-
},
32+
expect(getSpanOp(navigationSpan)).toBe('navigation');
33+
expect(navigationSpan.attributes).toMatchObject({
34+
'sentry.origin': { value: 'auto.navigation.solidstart.solidrouter', type: 'string' },
35+
'sentry.op': { value: 'navigation', type: 'string' },
36+
'sentry.segment.name.source': { value: 'route', type: 'string' },
37+
'url.template': { value: '/users/:id', type: 'string' },
38+
'url.path': { value: '/users/5', type: 'string' },
39+
'url.full': { value: expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/5$/), type: 'string' },
5840
});
5941
});
6042

61-
test('updates the transaction when using the back button', async ({ page }) => {
43+
test('updates the span when using the back button', async ({ page }) => {
6244
// Solid Router sends a `-1` navigation when using the back button.
6345
// The sentry solidRouterBrowserTracingIntegration tries to update such
64-
// transactions with the proper name once the `useLocation` hook triggers.
65-
const navigationTxnPromise = waitForTransaction('solidstart-2', async transactionEvent => {
66-
return transactionEvent?.transaction === '/users/:id' && transactionEvent.contexts?.trace?.op === 'navigation';
46+
// spans with the proper name once the `useLocation` hook triggers.
47+
const navigationSpanPromise = waitForStreamedSpan('solidstart-2', span => {
48+
return span.name === '/users/:id' && getSpanOp(span) === 'navigation' && span.is_segment;
6749
});
6850

6951
await page.goto(`/back-navigation`);
7052
await page.locator('#navLink').click();
71-
const navigationTxn = await navigationTxnPromise;
53+
const navigationSpan = await navigationSpanPromise;
7254

73-
expect(navigationTxn).toMatchObject({
74-
contexts: {
75-
trace: {
76-
op: 'navigation',
77-
origin: 'auto.navigation.solidstart.solidrouter',
78-
data: {
79-
'sentry.segment.name.source': 'route',
80-
'url.template': '/users/:id',
81-
'url.path': '/users/6',
82-
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/6$/),
83-
},
84-
},
85-
},
86-
transaction: '/users/:id',
87-
transaction_info: {
88-
source: 'route',
89-
},
55+
expect(getSpanOp(navigationSpan)).toBe('navigation');
56+
expect(navigationSpan.attributes).toMatchObject({
57+
'sentry.origin': { value: 'auto.navigation.solidstart.solidrouter', type: 'string' },
58+
'sentry.op': { value: 'navigation', type: 'string' },
59+
'sentry.segment.name.source': { value: 'route', type: 'string' },
60+
'url.template': { value: '/users/:id', type: 'string' },
61+
'url.path': { value: '/users/6', type: 'string' },
62+
'url.full': { value: expect.stringMatching(/^https?:\/\/localhost:\d+\/users\/6$/), type: 'string' },
9063
});
9164

92-
const backNavigationTxnPromise = waitForTransaction('solidstart-2', async transactionEvent => {
93-
return (
94-
transactionEvent?.transaction === '/back-navigation' && transactionEvent.contexts?.trace?.op === 'navigation'
95-
);
65+
const backNavigationSpanPromise = waitForStreamedSpan('solidstart-2', span => {
66+
return span.name === '/back-navigation' && getSpanOp(span) === 'navigation' && span.is_segment;
9667
});
9768

9869
await page.goBack();
99-
const backNavigationTxn = await backNavigationTxnPromise;
70+
const backNavigationSpan = await backNavigationSpanPromise;
10071

101-
expect(backNavigationTxn).toMatchObject({
102-
contexts: {
103-
trace: {
104-
op: 'navigation',
105-
origin: 'auto.navigation.solidstart.solidrouter',
106-
data: {
107-
'sentry.segment.name.source': 'route',
108-
'url.template': '/back-navigation',
109-
'url.path': '/back-navigation',
110-
'url.full': expect.stringMatching(/^https?:\/\/localhost:\d+\/back-navigation$/),
111-
},
112-
},
113-
},
114-
transaction: '/back-navigation',
115-
transaction_info: {
116-
source: 'route',
117-
},
72+
expect(getSpanOp(backNavigationSpan)).toBe('navigation');
73+
expect(backNavigationSpan.attributes).toMatchObject({
74+
'sentry.origin': { value: 'auto.navigation.solidstart.solidrouter', type: 'string' },
75+
'sentry.op': { value: 'navigation', type: 'string' },
76+
'sentry.segment.name.source': { value: 'route', type: 'string' },
77+
'url.template': { value: '/back-navigation', type: 'string' },
78+
'url.path': { value: '/back-navigation', type: 'string' },
79+
'url.full': { value: expect.stringMatching(/^https?:\/\/localhost:\d+\/back-navigation$/), type: 'string' },
11880
});
11981
});
Lines changed: 34 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,46 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForTransaction } from '@sentry-internal/test-utils';
3-
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/solidstart';
4-
5-
test('sends a server action transaction on pageload', async ({ page }) => {
6-
const transactionPromise = waitForTransaction('solidstart-2', transactionEvent => {
7-
return transactionEvent?.transaction === 'GET /users/6';
8-
});
2+
import { collectStreamedSpans, getSpanOp } from '@sentry-internal/test-utils';
3+
4+
test('sends a server action span on pageload', async ({ page }) => {
5+
const spansPromise = collectStreamedSpans(
6+
'solidstart-2',
7+
spans =>
8+
spans.some(
9+
span =>
10+
span.is_segment && getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/users/6',
11+
) && spans.some(span => span.name === 'getPrefecture'),
12+
);
913

1014
await page.goto('/users/6');
1115

12-
const transaction = await transactionPromise;
13-
14-
expect(transaction.spans).toEqual(
15-
expect.arrayContaining([
16-
expect.objectContaining({
17-
description: 'getPrefecture',
18-
data: {
19-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function',
20-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart',
21-
},
22-
}),
23-
]),
24-
);
25-
});
16+
const spans = await spansPromise;
17+
const functionSpan = spans.find(span => span.name === 'getPrefecture');
2618

27-
test('sends a server action transaction on client navigation', async ({ page }) => {
28-
const transactionPromise = waitForTransaction('solidstart-2', transactionEvent => {
29-
return transactionEvent?.transaction === 'POST getPrefecture';
19+
expect(functionSpan).toBeDefined();
20+
expect(functionSpan?.attributes).toMatchObject({
21+
'sentry.op': { value: 'function', type: 'string' },
22+
'sentry.origin': { value: 'auto.function.solidstart', type: 'string' },
3023
});
24+
});
25+
26+
test('sends a server action span on client navigation', async ({ page }) => {
27+
const spansPromise = collectStreamedSpans(
28+
'solidstart-2',
29+
spans =>
30+
spans.some(span => span.is_segment && span.name === 'POST getPrefecture') &&
31+
spans.some(span => span.name === 'getPrefecture' && !span.is_segment),
32+
);
3133

3234
await page.goto('/');
3335
await page.locator('#navLink').click();
3436
await page.waitForURL('/users/5');
3537

36-
const transaction = await transactionPromise;
37-
38-
expect(transaction.spans).toEqual(
39-
expect.arrayContaining([
40-
expect.objectContaining({
41-
description: 'getPrefecture',
42-
data: {
43-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function',
44-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.solidstart',
45-
},
46-
}),
47-
]),
48-
);
38+
const spans = await spansPromise;
39+
const functionSpan = spans.find(span => span.name === 'getPrefecture' && !span.is_segment);
40+
41+
expect(functionSpan).toBeDefined();
42+
expect(functionSpan?.attributes).toMatchObject({
43+
'sentry.op': { value: 'function', type: 'string' },
44+
'sentry.origin': { value: 'auto.function.solidstart', type: 'string' },
45+
});
4946
});

0 commit comments

Comments
 (0)