Skip to content

Commit bbe8e46

Browse files
chargomeclaude
andcommitted
test(sveltekit): Enable native OTel tracing in SvelteKit 3 e2e app
Turn on SvelteKit 3's native server-side tracing (`experimental.tracing.server` and `experimental.instrumentation.server`) so the e2e app exercises the path where @sentry/sveltekit picks up Kit's OpenTelemetry spans instead of starting its own `http.server` span. `Sentry.init` moves to `src/instrumentation.server.ts` (the native instrumentation entry point); `hooks.server.ts` is reduced to `sequence(sentryHandle())` plus the error handler. Swap the default-path performance/sdk tests for the native-tracing `tracing.*` suites and add a `form-action` route to cover form-action spans, mirroring `sveltekit-2-kit-tracing`. Config is passed via the `sveltekit()` Vite plugin since SvelteKit 3 dropped `svelte.config.js`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 55bea5a commit bbe8e46

16 files changed

Lines changed: 693 additions & 541 deletions

dev-packages/e2e-tests/test-applications/sveltekit-3/README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ float the `next` dist-tag: `@sveltejs/kit@next`, `@sveltejs/adapter-node@next`,
55
`@sveltejs/adapter-auto@next`). Built on Svelte 5, Vite 8, vite-plugin-svelte 7, TypeScript 6,
66
Node 22 — the minimums SvelteKit 3 requires.
77

8+
This app enables SvelteKit's **native server-side OpenTelemetry tracing**
9+
(`experimental.tracing.server` + `experimental.instrumentation.server`), so the Sentry SDK
10+
picks up Kit's spans instead of starting its own `http.server` span. `Sentry.init` therefore
11+
lives in `src/instrumentation.server.ts`, and the `tests/tracing.*` suites assert the native span
12+
tree (`sveltekit.handle.root`, `function.sveltekit.resolve`, form-action spans, etc.).
13+
814
## Status: `sentryTest.skip = true` (draft)
915

1016
This app is **skipped in CI** because it cannot build on the current SvelteKit 3 prerelease yet.
@@ -13,7 +19,7 @@ The build fails in SvelteKit's own pipeline (reproduced with the Sentry plugin r
1319
**not** a Sentry issue):
1420

1521
```
16-
[vite]: Rolldown failed to resolve import "$env/static/private" from "src/hooks.server.ts"
22+
[vite]: Rolldown failed to resolve import "$env/static/private" from "src/instrumentation.server.ts"
1723
```
1824

1925
SvelteKit 3 ships on Vite 8 / Rolldown, and `$env/*` virtual-module resolution is broken in the
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// See https://kit.svelte.dev/docs/types#app
2+
// for information about these interfaces
3+
declare global {
4+
namespace App {
5+
// interface Error {}
6+
// interface Locals {}
7+
// interface PageData {}
8+
// interface PageState {}
9+
// interface Platform {}
10+
}
11+
}
12+
13+
export {};
Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,7 @@
1-
import { E2E_TEST_DSN } from '$env/static/private';
21
import * as Sentry from '@sentry/sveltekit';
3-
4-
Sentry.init({
5-
environment: 'qa', // dynamic sampling bias to keep transactions
6-
dsn: E2E_TEST_DSN,
7-
debug: !!process.env.DEBUG,
8-
tunnel: `http://localhost:3031/`, // proxy server
9-
tracesSampleRate: 1.0,
10-
});
2+
import { sequence } from '@sveltejs/kit/hooks';
113

124
// not logging anything to console to avoid noise in the test output
135
export const handleError = Sentry.handleErrorWithSentry(() => {});
146

15-
export const handle = Sentry.sentryHandle();
7+
export const handle = sequence(Sentry.sentryHandle());
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { E2E_TEST_DSN } from '$env/static/private';
2+
import * as Sentry from '@sentry/sveltekit';
3+
4+
// With SvelteKit 3 native instrumentation enabled (`experimental.instrumentation.server`),
5+
// `Sentry.init` runs here instead of in `hooks.server.ts`.
6+
Sentry.init({
7+
environment: 'qa', // dynamic sampling bias to keep transactions
8+
dsn: E2E_TEST_DSN,
9+
debug: !!process.env.DEBUG,
10+
tunnel: `http://localhost:3031/`, // proxy server
11+
tracesSampleRate: 1.0,
12+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export const actions = {
2+
default: async ({ request }) => {
3+
const formData = await request.formData();
4+
const name = formData.get('name');
5+
return { name };
6+
},
7+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script>
2+
import { enhance } from '$app/forms';
3+
4+
const { data, form } = $props();
5+
</script>
6+
7+
<form method="post" use:enhance>
8+
<label for="name">Name:</label>
9+
<input id="inputName" type="text" name="name" />
10+
<button id="buttonSubmit" type="submit">Submit</button>
11+
</form>
12+
13+
{#if form?.name}
14+
<p>Hello {form.name}</p>
15+
{/if}

dev-packages/e2e-tests/test-applications/sveltekit-3/tests/errors.client.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ test.describe('client-side errors', () => {
66
test('captures error thrown on click', async ({ page }) => {
77
await waitForInitialPageload(page, { route: '/client-error' });
88

9-
const errorEventPromise = waitForError('sveltekit-2', errorEvent => {
9+
const errorEventPromise = waitForError('sveltekit-3', errorEvent => {
1010
return errorEvent?.exception?.values?.[0]?.value === 'Click Error';
1111
});
1212

@@ -33,7 +33,7 @@ test.describe('client-side errors', () => {
3333
await waitForInitialPageload(page);
3434
await page.reload();
3535

36-
const errorEventPromise = waitForError('sveltekit-2', errorEvent => {
36+
const errorEventPromise = waitForError('sveltekit-3', errorEvent => {
3737
return errorEvent?.exception?.values?.[0]?.value === 'Universal Load Error (browser)';
3838
});
3939

dev-packages/e2e-tests/test-applications/sveltekit-3/tests/errors.server.test.ts

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { waitForError } from '@sentry-internal/test-utils';
33

44
test.describe('server-side errors', () => {
55
test('captures universal load error', async ({ page }) => {
6-
const errorEventPromise = waitForError('sveltekit-2', errorEvent => {
6+
const errorEventPromise = waitForError('sveltekit-3', errorEvent => {
77
return errorEvent?.exception?.values?.[0]?.value === 'Universal Load Error (server)';
88
});
99

@@ -14,14 +14,25 @@ test.describe('server-side errors', () => {
1414

1515
expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
1616
expect.objectContaining({
17-
function: 'load$1',
17+
function: 'load',
1818
in_app: true,
1919
}),
2020
);
21+
22+
expect(errorEvent.request).toEqual({
23+
cookies: {},
24+
headers: expect.objectContaining({
25+
accept: expect.any(String),
26+
'user-agent': expect.any(String),
27+
}),
28+
method: 'GET',
29+
// SvelteKit's node adapter defaults to https in the protocol even if served on http
30+
url: 'http://localhost:3030/universal-load-error',
31+
});
2132
});
2233

2334
test('captures server load error', async ({ page }) => {
24-
const errorEventPromise = waitForError('sveltekit-2', errorEvent => {
35+
const errorEventPromise = waitForError('sveltekit-3', errorEvent => {
2536
return errorEvent?.exception?.values?.[0]?.value === 'Server Load Error';
2637
});
2738

@@ -32,14 +43,24 @@ test.describe('server-side errors', () => {
3243

3344
expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
3445
expect.objectContaining({
35-
function: 'load$1',
46+
function: 'load',
3647
in_app: true,
3748
}),
3849
);
50+
51+
expect(errorEvent.request).toEqual({
52+
cookies: {},
53+
headers: expect.objectContaining({
54+
accept: expect.any(String),
55+
'user-agent': expect.any(String),
56+
}),
57+
method: 'GET',
58+
url: 'http://localhost:3030/server-load-error',
59+
});
3960
});
4061

4162
test('captures server route (GET) error', async ({ page }) => {
42-
const errorEventPromise = waitForError('sveltekit-2', errorEvent => {
63+
const errorEventPromise = waitForError('sveltekit-3', errorEvent => {
4364
return errorEvent?.exception?.values?.[0]?.value === 'Server Route Error';
4465
});
4566

@@ -50,43 +71,21 @@ test.describe('server-side errors', () => {
5071

5172
expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
5273
expect.objectContaining({
53-
filename: expect.stringContaining('app:///_server.ts'),
74+
filename: expect.stringMatching(/app:\/\/\/_server.ts-.+.js/),
5475
function: 'GET',
5576
in_app: true,
5677
}),
5778
);
5879

5980
expect(errorEvent.transaction).toEqual('GET /server-route-error');
60-
});
6181

62-
test('captures error() thrown in server route with `wrapServerRouteWithSentry`', async ({ page }) => {
63-
const errorEventPromise = waitForError('sveltekit-2', errorEvent => {
64-
return errorEvent?.exception?.values?.[0]?.value === "'HttpError' captured as exception with keys: body, status";
65-
});
66-
67-
await page.goto('/wrap-server-route');
68-
69-
expect(await errorEventPromise).toMatchObject({
70-
exception: {
71-
values: [
72-
{
73-
value: "'HttpError' captured as exception with keys: body, status",
74-
mechanism: {
75-
handled: false,
76-
type: 'auto.function.sveltekit.server_route',
77-
},
78-
stacktrace: { frames: expect.any(Array) },
79-
},
80-
],
81-
},
82-
extra: {
83-
__serialized__: {
84-
body: {
85-
message: 'error() error',
86-
},
87-
status: 500,
88-
},
89-
},
82+
expect(errorEvent.request).toEqual({
83+
cookies: {},
84+
headers: expect.objectContaining({
85+
accept: expect.any(String),
86+
}),
87+
method: 'GET',
88+
url: 'http://localhost:3030/server-route-error',
9089
});
9190
});
9291
});

dev-packages/e2e-tests/test-applications/sveltekit-3/tests/performance.server.test.ts

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)