From 026c3d816657cd5730c0a59da3f02ec84362385d Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 12:12:31 +0200 Subject: [PATCH 1/8] feat(svelte): Add Svelte 5 support --- packages/svelte/src/performance.ts | 7 ++----- packages/sveltekit/src/index.types.ts | 2 ++ packages/sveltekit/src/server/index.ts | 9 +++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/svelte/src/performance.ts b/packages/svelte/src/performance.ts index 100f9f732ddc..acbf2576b540 100644 --- a/packages/svelte/src/performance.ts +++ b/packages/svelte/src/performance.ts @@ -1,7 +1,6 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, getActiveSpan } from '@sentry/browser'; import type { Span } from '@sentry/types'; import { afterUpdate, beforeUpdate, onMount } from 'svelte'; -import { current_component } from 'svelte/internal'; import { startInactiveSpan } from '@sentry/core'; import { DEFAULT_COMPONENT_NAME, UI_SVELTE_INIT, UI_SVELTE_UPDATE } from './constants'; @@ -17,7 +16,7 @@ const defaultTrackComponentOptions: { }; /** - * Tracks the Svelte component's intialization and mounting operation as well as + * Tracks the Svelte component's initialization and mounting operation as well as * updates and records them as spans. * * This function is injected automatically into your Svelte components' code @@ -30,9 +29,7 @@ export function trackComponent(options?: TrackComponentOptions): void { const customComponentName = mergedOptions.componentName; - // current_component.ctor.name is likely to give us the component's name automatically - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - const componentName = `<${customComponentName || current_component.constructor.name || DEFAULT_COMPONENT_NAME}>`; + const componentName = `<${customComponentName || DEFAULT_COMPONENT_NAME}>`; if (mergedOptions.trackInit) { recordInitSpan(componentName); diff --git a/packages/sveltekit/src/index.types.ts b/packages/sveltekit/src/index.types.ts index 18e734d01677..81d1ac96aa2e 100644 --- a/packages/sveltekit/src/index.types.ts +++ b/packages/sveltekit/src/index.types.ts @@ -52,3 +52,5 @@ export declare function flush(timeout?: number | undefined): PromiseLike; diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 9a6c22b2dfe2..07c297de608b 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -83,3 +83,12 @@ export { init } from './sdk'; export { handleErrorWithSentry } from './handleError'; export { wrapLoadWithSentry, wrapServerLoadWithSentry } from './load'; export { sentryHandle } from './handle'; + +/** + * Tracks the Svelte component's initialization and mounting operation as well as + * updates and records them as spans. These spans are only recorded on the client-side. + * Sever-side, during SSR, this function will not record any spans. + */ +export function trackComponent(_options?: unknown): void { + // no-op on the server side +} From c60d84d6269a48470b961f95a8275cee2d5389da Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 12:15:22 +0200 Subject: [PATCH 2/8] peer deps --- packages/svelte/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 2117691bfbf7..dfa9be17d144 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -49,7 +49,7 @@ "magic-string": "^0.30.0" }, "peerDependencies": { - "svelte": "3.x || 4.x" + "svelte": "3.x || 4.x || 5.x" }, "devDependencies": { "@sveltejs/vite-plugin-svelte": "1.4.0", From 286559ffc94ecdc30d2fdfa197efe3399a1967a4 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 12:16:32 +0200 Subject: [PATCH 3/8] add sveltekit-2-svelte-5 E2E test app --- .../sveltekit-2-svelte-5/.gitignore | 10 + .../sveltekit-2-svelte-5/.npmrc | 2 + .../sveltekit-2-svelte-5/README.md | 38 ++ .../sveltekit-2-svelte-5/package.json | 38 ++ .../sveltekit-2-svelte-5/playwright.config.ts | 77 ++++ .../sveltekit-2-svelte-5/src/app.d.ts | 13 + .../sveltekit-2-svelte-5/src/app.html | 12 + .../sveltekit-2-svelte-5/src/hooks.client.ts | 23 + .../sveltekit-2-svelte-5/src/hooks.server.ts | 21 + .../src/routes/+layout.svelte | 15 + .../src/routes/+page.svelte | 41 ++ .../src/routes/api/users/+server.ts | 3 + .../src/routes/building/+page.server.ts | 5 + .../src/routes/building/+page.svelte | 21 + .../src/routes/building/+page.ts | 5 + .../src/routes/client-error/+page.svelte | 9 + .../src/routes/components/+page.svelte | 15 + .../src/routes/components/Component1.svelte | 10 + .../src/routes/components/Component2.svelte | 9 + .../src/routes/components/Component3.svelte | 6 + .../src/routes/nav1/+page.svelte | 1 + .../src/routes/nav2/+page.svelte | 1 + .../src/routes/redirect1/+page.ts | 5 + .../src/routes/redirect2/+page.ts | 5 + .../routes/server-load-error/+page.server.ts | 6 + .../src/routes/server-load-error/+page.svelte | 9 + .../routes/server-load-fetch/+page.server.ts | 5 + .../src/routes/server-load-fetch/+page.svelte | 8 + .../routes/server-route-error/+page.svelte | 9 + .../src/routes/server-route-error/+page.ts | 7 + .../src/routes/server-route-error/+server.ts | 6 + .../routes/universal-load-error/+page.svelte | 17 + .../src/routes/universal-load-error/+page.ts | 8 + .../routes/universal-load-fetch/+page.svelte | 14 + .../src/routes/universal-load-fetch/+page.ts | 5 + .../src/routes/users/+page.server.ts | 5 + .../src/routes/users/+page.svelte | 10 + .../src/routes/users/[id]/+page.server.ts | 5 + .../src/routes/users/[id]/+page.svelte | 14 + .../start-event-proxy.cjs | 6 + .../sveltekit-2-svelte-5/static/favicon.png | Bin 0 -> 1571 bytes .../sveltekit-2-svelte-5/svelte.config.js | 18 + .../test/errors.client.test.ts | 59 +++ .../test/errors.server.test.ts | 65 +++ .../test/performance.client.test.ts | 139 ++++++ .../test/performance.server.test.ts | 35 ++ .../test/performance.test.ts | 404 ++++++++++++++++++ .../sveltekit-2-svelte-5/test/utils.ts | 49 +++ .../sveltekit-2-svelte-5/tsconfig.json | 19 + .../sveltekit-2-svelte-5/vite.config.ts | 12 + 50 files changed, 1319 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/static/favicon.png create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/svelte.config.js create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.client.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore new file mode 100644 index 000000000000..6635cf554275 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.gitignore @@ -0,0 +1,10 @@ +.DS_Store +node_modules +/build +/.svelte-kit +/package +.env +.env.* +!.env.example +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md new file mode 100644 index 000000000000..5ce676612ebf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md @@ -0,0 +1,38 @@ +# create-svelte + +Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npm create svelte@latest + +# create a new project in my-app +npm create svelte@latest my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json new file mode 100644 index 000000000000..8929cfecad10 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/package.json @@ -0,0 +1,38 @@ +{ + "name": "sveltekit-2-svelte-5", + "version": "0.0.1", + "private": true, + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "proxy": "node start-event-proxy.cjs", + "clean": "npx rimraf node_modules,pnpm-lock.yaml", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "test:prod": "TEST_ENV=production playwright test", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "pnpm test:prod" + }, + "dependencies": { + "@sentry/sveltekit": "latest || *", + "@spotlightjs/spotlight": "2.0.0-alpha.1" + }, + "devDependencies": { + "@playwright/test": "^1.36.2", + "@sentry-internal/event-proxy-server": "link:../../../event-proxy-server", + "@sentry/types": "latest || *", + "@sentry/utils": "latest || *", + "@sveltejs/adapter-auto": "^3.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^5.0.0-next.115", + "svelte-check": "^3.6.0", + "ts-node": "10.9.1", + "tslib": "^2.4.1", + "typescript": "^5.0.0", + "vite": "^5.0.3", + "wait-port": "1.0.4" + }, + "type": "module" +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts new file mode 100644 index 000000000000..6265e4154cf3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/playwright.config.ts @@ -0,0 +1,77 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +// Fix urls not resolving to localhost on Node v17+ +// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575 +import { setDefaultResultOrder } from 'dns'; +setDefaultResultOrder('ipv4first'); + +const testEnv = process.env.TEST_ENV; + +if (!testEnv) { + throw new Error('No test env defined'); +} + +const svelteKitPort = 3030; +const eventProxyPort = 3031; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './test', + /* Maximum time one test can run for. */ + timeout: 150_000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 10000, + }, + /* Run tests in files in parallel */ + fullyParallel: false, + workers: 1, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* `next dev` is incredibly buggy with the app dir */ + retries: testEnv === 'development' ? 3 : 0, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'list', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: `http://localhost:${svelteKitPort}`, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: [ + { + command: 'node start-event-proxy.cjs', + port: eventProxyPort, + reuseExistingServer: false, + }, + { + command: `pnpm wait-port ${eventProxyPort} && pnpm preview --port ${svelteKitPort}`, + port: svelteKitPort, + reuseExistingServer: false, + }, + ], +}; + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts new file mode 100644 index 000000000000..743f07b2e50a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://kit.svelte.dev/docs/types#app +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html new file mode 100644 index 000000000000..77a5ff52c923 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts new file mode 100644 index 000000000000..606379b59760 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.client.ts @@ -0,0 +1,23 @@ +import { env } from '$env/dynamic/public'; +import * as Sentry from '@sentry/sveltekit'; +import * as Spotlight from '@spotlightjs/spotlight'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: env.PUBLIC_E2E_TEST_DSN, + debug: true, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1.0, +}); + +const myErrorHandler = ({ error, event }: any) => { + console.error('An error occurred on the client side:', error, event); +}; + +export const handleError = Sentry.handleErrorWithSentry(myErrorHandler); + +if (import.meta.env.DEV) { + Spotlight.init({ + injectImmediately: true, + }); +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts new file mode 100644 index 000000000000..3fedded42858 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/hooks.server.ts @@ -0,0 +1,21 @@ +import { E2E_TEST_DSN } from '$env/static/private'; +import * as Sentry from '@sentry/sveltekit'; +import { setupSidecar } from '@spotlightjs/spotlight/sidecar'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: E2E_TEST_DSN, + debug: true, + tunnel: `http://localhost:3031/`, // proxy server + tracesSampleRate: 1.0, + spotlight: import.meta.env.DEV, +}); + +// not logging anything to console to avoid noise in the test output +export const handleError = Sentry.handleErrorWithSentry(() => {}); + +export const handle = Sentry.sentryHandle(); + +if (import.meta.env.DEV) { + setupSidecar(); +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte new file mode 100644 index 000000000000..d1fadd2ea5a3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+layout.svelte @@ -0,0 +1,15 @@ + + +

Sveltekit E2E Test app

+
+ +
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte new file mode 100644 index 000000000000..575e6827693f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/+page.svelte @@ -0,0 +1,41 @@ +

Welcome to SvelteKit 2 with Svelte 5!

+

Visit kit.svelte.dev to read the documentation

+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts new file mode 100644 index 000000000000..d0e4371c594b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/api/users/+server.ts @@ -0,0 +1,3 @@ +export const GET = () => { + return new Response(JSON.stringify({ users: ['alice', 'bob', 'carol'] })); +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts new file mode 100644 index 000000000000..b07376ba97c9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.server.ts @@ -0,0 +1,5 @@ +import type { PageServerLoad } from './$types'; + +export const load = (async _event => { + return { name: 'building (server)' }; +}) satisfies PageServerLoad; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte new file mode 100644 index 000000000000..b27edb70053d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.svelte @@ -0,0 +1,21 @@ + + +

Check Build

+ +

+ This route only exists to check that Typescript definitions + and auto instrumentation are working when the project is built. +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts new file mode 100644 index 000000000000..049acdc1fafa --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/building/+page.ts @@ -0,0 +1,5 @@ +import type { PageLoad } from './$types'; + +export const load = (async _event => { + return { name: 'building' }; +}) satisfies PageLoad; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte new file mode 100644 index 000000000000..ba6b464e9324 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/client-error/+page.svelte @@ -0,0 +1,9 @@ + + +

Client error

+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte new file mode 100644 index 000000000000..eff3fa3f2e8d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/+page.svelte @@ -0,0 +1,15 @@ + +

Demonstrating Component Tracking

+ + + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte new file mode 100644 index 000000000000..a675711e4b68 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component1.svelte @@ -0,0 +1,10 @@ + +

Howdy, I'm component 1

+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte new file mode 100644 index 000000000000..2b2f38308077 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component2.svelte @@ -0,0 +1,9 @@ + +

Howdy, I'm component 2

+ + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte new file mode 100644 index 000000000000..9b4e028f78e7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/components/Component3.svelte @@ -0,0 +1,6 @@ + + +

Howdy, I'm component 3

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte new file mode 100644 index 000000000000..31abffc512a2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav1/+page.svelte @@ -0,0 +1 @@ +

Navigation 1

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte new file mode 100644 index 000000000000..20b44bb32da9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/nav2/+page.svelte @@ -0,0 +1 @@ +

Navigation 2

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts new file mode 100644 index 000000000000..3f462bf810fd --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect1/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export const load = async () => { + redirect(301, '/redirect2'); +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts new file mode 100644 index 000000000000..99a810761d18 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/redirect2/+page.ts @@ -0,0 +1,5 @@ +import { redirect } from '@sveltejs/kit'; + +export const load = async () => { + redirect(301, '/users/789'); +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts new file mode 100644 index 000000000000..17dd53fb5bbb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.server.ts @@ -0,0 +1,6 @@ +export const load = async () => { + throw new Error('Server Load Error'); + return { + msg: 'Hello World', + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte new file mode 100644 index 000000000000..3a0942971d06 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-error/+page.svelte @@ -0,0 +1,9 @@ + + +

Server load error

+ +

+ Message: {data.msg} +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts new file mode 100644 index 000000000000..709e52bcf351 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.server.ts @@ -0,0 +1,5 @@ +export const load = async ({ fetch }) => { + const res = await fetch('/api/users'); + const data = await res.json(); + return { data }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte new file mode 100644 index 000000000000..f7f814d31b4d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-load-fetch/+page.svelte @@ -0,0 +1,8 @@ + + +
+

Server Load Fetch

+

{JSON.stringify(data, null, 2)}

+
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte new file mode 100644 index 000000000000..3d682e7e3462 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.svelte @@ -0,0 +1,9 @@ + + +

Server Route error

+ +

+ Message: {data.msg} +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts new file mode 100644 index 000000000000..298240827714 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+page.ts @@ -0,0 +1,7 @@ +export const load = async ({ fetch }) => { + const res = await fetch('/server-route-error'); + const data = await res.json(); + return { + msg: data, + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts new file mode 100644 index 000000000000..f1a4b94b7706 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/server-route-error/+server.ts @@ -0,0 +1,6 @@ +export const GET = async () => { + throw new Error('Server Route Error'); + return { + msg: 'Hello World', + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte new file mode 100644 index 000000000000..dc2d311a0ece --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.svelte @@ -0,0 +1,17 @@ + + +

Universal load error

+ +

+ To trigger from client: Load on another route, then navigate to this route. +

+ +

+ To trigger from server: Load on this route +

+ +

+ Message: {data.msg} +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts new file mode 100644 index 000000000000..3d72bf4a890f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-error/+page.ts @@ -0,0 +1,8 @@ +import { browser } from '$app/environment'; + +export const load = async () => { + throw new Error(`Universal Load Error (${browser ? 'browser' : 'server'})`); + return { + msg: 'Hello World', + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte new file mode 100644 index 000000000000..563c51e8c850 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.svelte @@ -0,0 +1,14 @@ + + +

Fetching in universal load

+ +

Here's a list of a few users:

+ +
    + {#each data.users as user} +
  • {user}
  • + {/each} +
diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts new file mode 100644 index 000000000000..63c1ee68e1cb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/universal-load-fetch/+page.ts @@ -0,0 +1,5 @@ +export const load = async ({ fetch }) => { + const usersRes = await fetch('/api/users'); + const data = await usersRes.json(); + return { users: data.users }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts new file mode 100644 index 000000000000..a34c5450f682 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.server.ts @@ -0,0 +1,5 @@ +export const load = async () => { + return { + msg: 'Hi everyone!', + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte new file mode 100644 index 000000000000..aa804a4518fa --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/+page.svelte @@ -0,0 +1,10 @@ + +

+ All Users: +

+ +

+ message: {data.msg} +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts new file mode 100644 index 000000000000..9388f3927018 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.server.ts @@ -0,0 +1,5 @@ +export const load = async ({ params }) => { + return { + msg: `This is a special message for user ${params.id}`, + }; +}; diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte new file mode 100644 index 000000000000..d348a8c57dad --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/routes/users/[id]/+page.svelte @@ -0,0 +1,14 @@ + + +

Route with dynamic params

+ +

+ User id: {$page.params.id} +

+ +

+ Secret message for user: {data.msg} +

diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs new file mode 100644 index 000000000000..1ac8b9604239 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/start-event-proxy.cjs @@ -0,0 +1,6 @@ +const { startEventProxyServer } = require('@sentry-internal/event-proxy-server'); + +startEventProxyServer({ + port: 3031, + proxyServerName: 'sveltekit-2-svelte-5', +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/static/favicon.png b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/static/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..825b9e65af7c104cfb07089bb28659393b4f2097 GIT binary patch literal 1571 zcmV+;2Hg3HP)Px)-AP12RCwC$UE6KzI1p6{F2N z1VK2vi|pOpn{~#djwYcWXTI_im_u^TJgMZ4JMOsSj!0ma>B?-(Hr@X&W@|R-$}W@Z zgj#$x=!~7LGqHW?IO8+*oE1MyDp!G=L0#^lUx?;!fXv@l^6SvTnf^ac{5OurzC#ZMYc20lI%HhX816AYVs1T3heS1*WaWH z%;x>)-J}YB5#CLzU@GBR6sXYrD>Vw(Fmt#|JP;+}<#6b63Ike{Fuo!?M{yEffez;| zp!PfsuaC)>h>-AdbnwN13g*1LowNjT5?+lFVd#9$!8Z9HA|$*6dQ8EHLu}U|obW6f z2%uGv?vr=KNq7YYa2Roj;|zooo<)lf=&2yxM@e`kM$CmCR#x>gI>I|*Ubr({5Y^rb zghxQU22N}F51}^yfDSt786oMTc!W&V;d?76)9KXX1 z+6Okem(d}YXmmOiZq$!IPk5t8nnS{%?+vDFz3BevmFNgpIod~R{>@#@5x9zJKEHLHv!gHeK~n)Ld!M8DB|Kfe%~123&Hz1Z(86nU7*G5chmyDe ziV7$pB7pJ=96hpxHv9rCR29%bLOXlKU<_13_M8x)6;P8E1Kz6G<&P?$P^%c!M5`2` zfY2zg;VK5~^>TJGQzc+33-n~gKt{{of8GzUkWmU110IgI0DLxRIM>0US|TsM=L|@F z0Bun8U!cRB7-2apz=y-7*UxOxz@Z0)@QM)9wSGki1AZ38ceG7Q72z5`i;i=J`ILzL z@iUO?SBBG-0cQuo+an4TsLy-g-x;8P4UVwk|D8{W@U1Zi z!M)+jqy@nQ$p?5tsHp-6J304Q={v-B>66$P0IDx&YT(`IcZ~bZfmn11#rXd7<5s}y zBi9eim&zQc0Dk|2>$bs0PnLmDfMP5lcXRY&cvJ=zKxI^f0%-d$tD!`LBf9^jMSYUA zI8U?CWdY@}cRq6{5~y+)#h1!*-HcGW@+gZ4B};0OnC~`xQOyH19z*TA!!BJ%9s0V3F?CAJ{hTd#*tf+ur-W9MOURF-@B77_-OshsY}6 zOXRY=5%C^*26z?l)1=$bz30!so5tfABdSYzO+H=CpV~aaUefmjvfZ3Ttu9W&W3Iu6 zROlh0MFA5h;my}8lB0tAV-Rvc2Zs_CCSJnx@d`**$idgy-iMob4dJWWw|21b4NB=LfsYp0Aeh{Ov)yztQi;eL4y5 zMi>8^SzKqk8~k?UiQK^^-5d8c%bV?$F8%X~czyiaKCI2=UH { + test('captures error thrown on click', async ({ page }) => { + await waitForInitialPageload(page, { route: '/client-error' }); + + const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Click Error'; + }); + + await page.getByText('Throw error').click(); + + await expect(errorEventPromise).resolves.toBeDefined(); + + const errorEvent = await errorEventPromise; + + const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + + expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual( + expect.objectContaining({ + function: expect.stringContaining('HTMLButtonElement'), + lineno: 1, + in_app: true, + }), + ); + + expect(errorEvent.tags).toMatchObject({ runtime: 'browser' }); + + expect(errorEvent.transaction).toEqual('/client-error'); + }); + + test('captures universal load error', async ({ page }) => { + await waitForInitialPageload(page); + await page.reload(); + + const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Universal Load Error (browser)'; + }); + + // navigating triggers the error on the client + await page.getByText('Universal Load error').click(); + + const errorEvent = await errorEventPromise; + const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + + const lastFrame = errorEventFrames?.[errorEventFrames?.length - 1]; + expect(lastFrame).toEqual( + expect.objectContaining({ + lineno: 1, + in_app: true, + }), + ); + + expect(errorEvent.tags).toMatchObject({ runtime: 'browser' }); + expect(errorEvent.transaction).toEqual('/universal-load-error'); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts new file mode 100644 index 000000000000..f169b0bf6b68 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/errors.server.test.ts @@ -0,0 +1,65 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/event-proxy-server'; + +test.describe('server-side errors', () => { + test('captures universal load error', async ({ page }) => { + const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Universal Load Error (server)'; + }); + + await page.goto('/universal-load-error'); + + const errorEvent = await errorEventPromise; + const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + + expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual( + expect.objectContaining({ + function: 'load$1', + in_app: true, + }), + ); + + expect(errorEvent.tags).toMatchObject({ runtime: 'node' }); + }); + + test('captures server load error', async ({ page }) => { + const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Server Load Error'; + }); + + await page.goto('/server-load-error'); + + const errorEvent = await errorEventPromise; + const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + + expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual( + expect.objectContaining({ + function: 'load$1', + in_app: true, + }), + ); + + expect(errorEvent.tags).toMatchObject({ runtime: 'node' }); + }); + + test('captures server route (GET) error', async ({ page }) => { + const errorEventPromise = waitForError('sveltekit-2-svelte-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Server Route Error'; + }); + + await page.goto('/server-route-error'); + + const errorEvent = await errorEventPromise; + const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + + expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual( + expect.objectContaining({ + filename: expect.stringContaining('app:///_server.ts'), + function: 'GET', + in_app: true, + }), + ); + + expect(errorEvent.transaction).toEqual('GET /server-route-error'); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts new file mode 100644 index 000000000000..324b83977ba1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.client.test.ts @@ -0,0 +1,139 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/event-proxy-server'; +import { waitForInitialPageload } from './utils'; + +test.describe('client-specific performance events', () => { + test('multiple navigations have distinct traces', async ({ page }) => { + const navigationTxn1EventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/nav1' && txnEvent.contexts?.trace?.op === 'navigation'; + }); + + const navigationTxn2EventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/' && txnEvent.contexts?.trace?.op === 'navigation'; + }); + + const navigationTxn3EventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/nav2' && txnEvent.contexts?.trace?.op === 'navigation'; + }); + + await waitForInitialPageload(page); + + await page.getByText('Nav 1').click(); + const navigationTxn1Event = await navigationTxn1EventPromise; + + await page.goBack(); + const navigationTxn2Event = await navigationTxn2EventPromise; + + await page.getByText('Nav 2').click(); + const navigationTxn3Event = await navigationTxn3EventPromise; + + expect(navigationTxn1Event).toMatchObject({ + transaction: '/nav1', + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + }); + + expect(navigationTxn2Event).toMatchObject({ + transaction: '/', + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + }); + + expect(navigationTxn3Event).toMatchObject({ + transaction: '/nav2', + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + }); + + // traces should NOT be connected + expect(navigationTxn1Event.contexts?.trace?.trace_id).not.toBe(navigationTxn2Event.contexts?.trace?.trace_id); + expect(navigationTxn2Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id); + expect(navigationTxn1Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id); + }); + + test('records manually added component tracking spans', async ({ page }) => { + const componentTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/components'; + }); + + await waitForInitialPageload(page); + + await page.getByText('Component Tracking').click(); + + const componentTxnEvent = await componentTxnEventPromise; + + expect(componentTxnEvent.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + ]), + ); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts new file mode 100644 index 000000000000..d43941f2a0c2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.server.test.ts @@ -0,0 +1,35 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/event-proxy-server'; + +test('server pageload request span has nested request span for sub request', async ({ page }) => { + const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === 'GET /server-load-fetch'; + }); + + await page.goto('/server-load-fetch'); + + const serverTxnEvent = await serverTxnEventPromise; + const spans = serverTxnEvent.spans; + + expect(serverTxnEvent).toMatchObject({ + transaction: 'GET /server-load-fetch', + tags: { runtime: 'node' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'http.server', + origin: 'auto.http.sveltekit', + }, + }, + }); + + expect(spans).toEqual( + expect.arrayContaining([ + // load span where the server load function initiates the sub request: + expect.objectContaining({ op: 'function.sveltekit.server.load', description: '/server-load-fetch' }), + // sub request span: + expect.objectContaining({ op: 'http.server', description: 'GET /api/users' }), + ]), + ); +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts new file mode 100644 index 000000000000..8ef420eb8eb1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/performance.test.ts @@ -0,0 +1,404 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/event-proxy-server'; +import { waitForInitialPageload } from './utils'; + +test.describe('performance events', () => { + test('capture a distributed pageload trace', async ({ page }) => { + const clientTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/users/[id]'; + }); + + const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === 'GET /users/[id]'; + }); + + const [_, clientTxnEvent, serverTxnEvent] = await Promise.all([ + page.goto('/users/123xyz'), + clientTxnEventPromise, + serverTxnEventPromise, + expect(page.getByText('User id: 123xyz')).toBeVisible(), + ]); + + expect(clientTxnEvent).toMatchObject({ + transaction: '/users/[id]', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'pageload', + origin: 'auto.pageload.sveltekit', + }, + }, + }); + + expect(serverTxnEvent).toMatchObject({ + transaction: 'GET /users/[id]', + tags: { runtime: 'node' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'http.server', + origin: 'auto.http.sveltekit', + }, + }, + }); + + expect(clientTxnEvent.spans?.length).toBeGreaterThan(5); + + // connected trace + expect(clientTxnEvent.contexts?.trace?.trace_id).toBe(serverTxnEvent.contexts?.trace?.trace_id); + + // weird but server txn is parent of client txn + expect(clientTxnEvent.contexts?.trace?.parent_span_id).toBe(serverTxnEvent.contexts?.trace?.span_id); + }); + + test('capture a distributed navigation trace', async ({ page }) => { + const clientNavigationTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/users' && txnEvent.contexts?.trace?.op === 'navigation'; + }); + + const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === 'GET /users'; + }); + + await waitForInitialPageload(page); + + // navigation to page + const clickPromise = page.getByText('Route with Server Load').click(); + + const [clientTxnEvent, serverTxnEvent, _1, _2] = await Promise.all([ + clientNavigationTxnEventPromise, + serverTxnEventPromise, + clickPromise, + expect(page.getByText('Hi everyone')).toBeVisible(), + ]); + + expect(clientTxnEvent).toMatchObject({ + transaction: '/users', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + }, + }, + }); + + expect(serverTxnEvent).toMatchObject({ + transaction: 'GET /users', + tags: { runtime: 'node' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'http.server', + origin: 'auto.http.sveltekit', + }, + }, + }); + + // trace is connected + expect(clientTxnEvent.contexts?.trace?.trace_id).toBe(serverTxnEvent.contexts?.trace?.trace_id); + }); + + test('record client-side universal load fetch span and trace', async ({ page }) => { + await waitForInitialPageload(page); + + const clientNavigationTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === '/universal-load-fetch' && txnEvent.contexts?.trace?.op === 'navigation'; + }); + + // this transaction should be created because of the fetch call + // it should also be part of the trace + const serverTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.transaction === 'GET /api/users'; + }); + + // navigation to page + const clickPromise = page.getByText('Route with fetch in universal load').click(); + + const [clientTxnEvent, serverTxnEvent, _1, _2] = await Promise.all([ + clientNavigationTxnEventPromise, + serverTxnEventPromise, + clickPromise, + expect(page.getByText('alice')).toBeVisible(), + ]); + + expect(clientTxnEvent).toMatchObject({ + transaction: '/universal-load-fetch', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + }, + }, + }); + + expect(serverTxnEvent).toMatchObject({ + transaction: 'GET /api/users', + tags: { runtime: 'node' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'http.server', + origin: 'auto.http.sveltekit', + }, + }, + }); + + // trace is connected + expect(clientTxnEvent.contexts?.trace?.trace_id).toBe(serverTxnEvent.contexts?.trace?.trace_id); + + const clientFetchSpan = clientTxnEvent.spans?.find(s => s.op === 'http.client'); + + expect(clientFetchSpan).toMatchObject({ + description: expect.stringMatching(/^GET.*\/api\/users/), + op: 'http.client', + origin: 'auto.http.browser', + data: { + url: expect.stringContaining('/api/users'), + type: 'fetch', + 'http.method': 'GET', + 'http.response.status_code': 200, + 'network.protocol.version': '1.1', + 'network.protocol.name': 'http', + 'http.request.redirect_start': expect.any(Number), + 'http.request.fetch_start': expect.any(Number), + 'http.request.domain_lookup_start': expect.any(Number), + 'http.request.domain_lookup_end': expect.any(Number), + 'http.request.connect_start': expect.any(Number), + 'http.request.secure_connection_start': expect.any(Number), + 'http.request.connection_end': expect.any(Number), + 'http.request.request_start': expect.any(Number), + 'http.request.response_start': expect.any(Number), + 'http.request.response_end': expect.any(Number), + }, + }); + }); + + test('captures a navigation transaction directly after pageload', async ({ page }) => { + await page.goto('/'); + + const clientPageloadTxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.contexts?.trace?.op === 'pageload' && txnEvent?.tags?.runtime === 'browser'; + }); + + const clientNavigationTxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return txnEvent?.contexts?.trace?.op === 'navigation' && txnEvent?.tags?.runtime === 'browser'; + }); + + const navigationClickPromise = page.locator('#routeWithParamsLink').click(); + + const [pageloadTxnEvent, navigationTxnEvent, _] = await Promise.all([ + clientPageloadTxnPromise, + clientNavigationTxnPromise, + navigationClickPromise, + ]); + + expect(pageloadTxnEvent).toMatchObject({ + transaction: '/', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'pageload', + origin: 'auto.pageload.sveltekit', + }, + }, + }); + + expect(navigationTxnEvent).toMatchObject({ + transaction: '/users/[id]', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + data: { + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/users/[id]', + 'sentry.sveltekit.navigation.type': 'link', + }, + }, + }, + }); + + const routingSpans = navigationTxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing'); + expect(routingSpans).toHaveLength(1); + + const routingSpan = routingSpans && routingSpans[0]; + expect(routingSpan).toMatchObject({ + op: 'ui.sveltekit.routing', + description: 'SvelteKit Route Change', + data: { + 'sentry.op': 'ui.sveltekit.routing', + 'sentry.origin': 'auto.ui.sveltekit', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/users/[id]', + 'sentry.sveltekit.navigation.type': 'link', + }, + }); + }); + + test('captures one navigation transaction per redirect', async ({ page }) => { + await page.goto('/'); + + const clientNavigationRedirect1TxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return ( + txnEvent?.contexts?.trace?.op === 'navigation' && + txnEvent?.tags?.runtime === 'browser' && + txnEvent?.transaction === '/redirect1' + ); + }); + + const clientNavigationRedirect2TxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return ( + txnEvent?.contexts?.trace?.op === 'navigation' && + txnEvent?.tags?.runtime === 'browser' && + txnEvent?.transaction === '/redirect2' + ); + }); + + const clientNavigationRedirect3TxnPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + return ( + txnEvent?.contexts?.trace?.op === 'navigation' && + txnEvent?.tags?.runtime === 'browser' && + txnEvent?.transaction === '/users/[id]' + ); + }); + + const navigationClickPromise = page.locator('#redirectLink').click(); + + const [redirect1TxnEvent, redirect2TxnEvent, redirect3TxnEvent, _] = await Promise.all([ + clientNavigationRedirect1TxnPromise, + clientNavigationRedirect2TxnPromise, + clientNavigationRedirect3TxnPromise, + navigationClickPromise, + ]); + + expect(redirect1TxnEvent).toMatchObject({ + transaction: '/redirect1', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + data: { + 'sentry.origin': 'auto.navigation.sveltekit', + 'sentry.op': 'navigation', + 'sentry.source': 'route', + 'sentry.sveltekit.navigation.type': 'link', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/redirect1', + 'sentry.sample_rate': 1, + }, + }, + }, + }); + + const redirect1Spans = redirect1TxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing'); + expect(redirect1Spans).toHaveLength(1); + + const redirect1Span = redirect1Spans && redirect1Spans[0]; + expect(redirect1Span).toMatchObject({ + op: 'ui.sveltekit.routing', + description: 'SvelteKit Route Change', + data: { + 'sentry.op': 'ui.sveltekit.routing', + 'sentry.origin': 'auto.ui.sveltekit', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/redirect1', + 'sentry.sveltekit.navigation.type': 'link', + }, + }); + + expect(redirect2TxnEvent).toMatchObject({ + transaction: '/redirect2', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + data: { + 'sentry.origin': 'auto.navigation.sveltekit', + 'sentry.op': 'navigation', + 'sentry.source': 'route', + 'sentry.sveltekit.navigation.type': 'goto', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/redirect2', + 'sentry.sample_rate': 1, + }, + }, + }, + }); + + const redirect2Spans = redirect2TxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing'); + expect(redirect2Spans).toHaveLength(1); + + const redirect2Span = redirect2Spans && redirect2Spans[0]; + expect(redirect2Span).toMatchObject({ + op: 'ui.sveltekit.routing', + description: 'SvelteKit Route Change', + data: { + 'sentry.op': 'ui.sveltekit.routing', + 'sentry.origin': 'auto.ui.sveltekit', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/redirect2', + 'sentry.sveltekit.navigation.type': 'goto', + }, + }); + + expect(redirect3TxnEvent).toMatchObject({ + transaction: '/users/[id]', + tags: { runtime: 'browser' }, + transaction_info: { source: 'route' }, + type: 'transaction', + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.sveltekit', + data: { + 'sentry.origin': 'auto.navigation.sveltekit', + 'sentry.op': 'navigation', + 'sentry.source': 'route', + 'sentry.sveltekit.navigation.type': 'goto', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/users/[id]', + 'sentry.sample_rate': 1, + }, + }, + }, + }); + + const redirect3Spans = redirect3TxnEvent.spans?.filter(s => s.op === 'ui.sveltekit.routing'); + expect(redirect3Spans).toHaveLength(1); + + const redirect3Span = redirect3Spans && redirect3Spans[0]; + expect(redirect3Span).toMatchObject({ + op: 'ui.sveltekit.routing', + description: 'SvelteKit Route Change', + data: { + 'sentry.op': 'ui.sveltekit.routing', + 'sentry.origin': 'auto.ui.sveltekit', + 'sentry.sveltekit.navigation.from': '/', + 'sentry.sveltekit.navigation.to': '/users/[id]', + 'sentry.sveltekit.navigation.type': 'goto', + }, + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts new file mode 100644 index 000000000000..5dcc01bbe7f8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/test/utils.ts @@ -0,0 +1,49 @@ +import { Page } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/event-proxy-server'; + +/** + * Helper function that waits for the initial pageload to complete. + * + * This function + * - loads the given route ("/" by default) + * - waits for SvelteKit's hydration + * - waits for the pageload transaction to be sent (doesn't assert on it though) + * + * Useful for tests that test outcomes of _navigations_ after an initial pageload. + * Waiting on the pageload transaction excludes edge cases where navigations occur + * so quickly that the pageload idle transaction is still active. This might lead + * to cases where the routing span would be attached to the pageload transaction + * and hence eliminates a lot of flakiness. + * + */ +export async function waitForInitialPageload( + page: Page, + opts?: { route?: string; parameterizedRoute?: string; debug?: boolean }, +) { + const route = opts?.route ?? '/'; + const txnName = opts?.parameterizedRoute ?? route; + const debug = opts?.debug ?? false; + + const clientPageloadTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => { + debug && + console.log({ + txn: txnEvent?.transaction, + op: txnEvent.contexts?.trace?.op, + trace: txnEvent.contexts?.trace?.trace_id, + span: txnEvent.contexts?.trace?.span_id, + parent: txnEvent.contexts?.trace?.parent_span_id, + }); + + return txnEvent?.transaction === txnName && txnEvent.contexts?.trace?.op === 'pageload'; + }); + + await Promise.all([ + page.goto(route), + // the test app adds the "hydrated" class to the body when hydrating + page.waitForSelector('body.hydrated'), + // also waiting for the initial pageload txn so that later navigations don't interfere + clientPageloadTxnEventPromise, + ]); + + debug && console.log('hydrated'); +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json new file mode 100644 index 000000000000..ba6aa4e6610a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + }, + // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias + // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts new file mode 100644 index 000000000000..1a410bee7e11 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/vite.config.ts @@ -0,0 +1,12 @@ +import { sentrySvelteKit } from '@sentry/sveltekit'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sentrySvelteKit({ + autoUploadSourceMaps: false, + }), + sveltekit(), + ], +}); From b5ffdfcc1118930e96c5a3206fa523ba2c6f5ced Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 12:30:07 +0200 Subject: [PATCH 4/8] formatting... --- .../sveltekit-2-svelte-5/src/app.d.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts index 743f07b2e50a..ede601ab93e2 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/src/app.d.ts @@ -1,13 +1,13 @@ // See https://kit.svelte.dev/docs/types#app // for information about these interfaces declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } } export {}; From 64e95258ad0cc876edc390504d9b89e49e4c2257 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 12:30:53 +0200 Subject: [PATCH 5/8] add new e2e test app to GH matrix --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3e000957653d..9bcf44c39024 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1008,6 +1008,7 @@ jobs: 'standard-frontend-react', 'sveltekit', 'sveltekit-2', + 'sveltekit-2-svelte-5', 'generic-ts3.8', 'node-fastify-app', # TODO(v8): Re-enable hapi tests From 070dc32862014e59032541347e2fff259cf5e9fb Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 12:52:17 +0200 Subject: [PATCH 6/8] backport component tracking e2e test --- .../sveltekit-2/src/routes/+page.svelte | 3 + .../src/routes/components/+page.svelte | 15 ++++ .../src/routes/components/Component1.svelte | 10 +++ .../src/routes/components/Component2.svelte | 9 +++ .../src/routes/components/Component3.svelte | 6 ++ .../test/performance.client.test.ts | 65 ++++++++++++++++++ .../sveltekit/src/routes/+page.svelte | 3 + .../src/routes/components/+page.svelte | 15 ++++ .../src/routes/components/Component1.svelte | 10 +++ .../src/routes/components/Component2.svelte | 9 +++ .../src/routes/components/Component3.svelte | 6 ++ .../sveltekit/test/performance.client.test.ts | 68 +++++++++++++++++++ 12 files changed, 219 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte create mode 100644 dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte index 71a0ce872185..e7788b6433cd 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/+page.svelte @@ -35,4 +35,7 @@
  • Nav 2
  • +
  • + Component Tracking +
  • diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte new file mode 100644 index 000000000000..eff3fa3f2e8d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/+page.svelte @@ -0,0 +1,15 @@ + +

    Demonstrating Component Tracking

    + + + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte new file mode 100644 index 000000000000..a675711e4b68 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component1.svelte @@ -0,0 +1,10 @@ + +

    Howdy, I'm component 1

    + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte new file mode 100644 index 000000000000..2b2f38308077 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component2.svelte @@ -0,0 +1,9 @@ + +

    Howdy, I'm component 2

    + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte new file mode 100644 index 000000000000..9b4e028f78e7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/src/routes/components/Component3.svelte @@ -0,0 +1,6 @@ + + +

    Howdy, I'm component 3

    diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts index 11e647ff07ff..403701a075be 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2/test/performance.client.test.ts @@ -66,4 +66,69 @@ test.describe('client-specific performance events', () => { expect(navigationTxn2Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id); expect(navigationTxn1Event.contexts?.trace?.trace_id).not.toBe(navigationTxn3Event.contexts?.trace?.trace_id); }); + + test('records manually added component tracking spans', async ({ page }) => { + const componentTxnEventPromise = waitForTransaction('sveltekit-2', txnEvent => { + return txnEvent?.transaction === '/components'; + }); + + await waitForInitialPageload(page); + + await page.getByText('Component Tracking').click(); + + const componentTxnEvent = await componentTxnEventPromise; + + expect(componentTxnEvent.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + ]), + ); + }); }); diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte index 62dbf7856ab7..31f6cb802950 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte +++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/+page.svelte @@ -26,4 +26,7 @@
  • Route with nested fetch in server load
  • +
  • + Component Tracking +
  • diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte new file mode 100644 index 000000000000..eff3fa3f2e8d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/+page.svelte @@ -0,0 +1,15 @@ + +

    Demonstrating Component Tracking

    + + + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte new file mode 100644 index 000000000000..a675711e4b68 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component1.svelte @@ -0,0 +1,10 @@ + +

    Howdy, I'm component 1

    + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte new file mode 100644 index 000000000000..2b2f38308077 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component2.svelte @@ -0,0 +1,9 @@ + +

    Howdy, I'm component 2

    + + diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte new file mode 100644 index 000000000000..9b4e028f78e7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit/src/routes/components/Component3.svelte @@ -0,0 +1,6 @@ + + +

    Howdy, I'm component 3

    diff --git a/dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts b/dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts new file mode 100644 index 000000000000..d7107d5e7744 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/sveltekit/test/performance.client.test.ts @@ -0,0 +1,68 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/event-proxy-server'; +import { waitForInitialPageload } from '../utils.js'; + +test('records manually added component tracking spans', async ({ page }) => { + const componentTxnEventPromise = waitForTransaction('sveltekit', txnEvent => { + return txnEvent?.transaction === '/components'; + }); + + await waitForInitialPageload(page); + + await page.getByText('Component Tracking').click(); + + const componentTxnEvent = await componentTxnEventPromise; + + expect(componentTxnEvent.spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.init', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + expect.objectContaining({ + data: { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte' }, + description: '', + op: 'ui.svelte.update', + origin: 'auto.ui.svelte', + }), + ]), + ); +}); From 161082d15299ee34e27a71aa5ac893527a5b3373 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 13:10:52 +0200 Subject: [PATCH 7/8] fix format --- .../test-applications/sveltekit-2-svelte-5/README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md index 5ce676612ebf..684cabccfe02 100644 --- a/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md +++ b/dev-packages/e2e-tests/test-applications/sveltekit-2-svelte-5/README.md @@ -1,6 +1,7 @@ # create-svelte -Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). +Everything you need to build a Svelte project, powered by +[`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). ## Creating a project @@ -16,7 +17,8 @@ npm create svelte@latest my-app ## Developing -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a +development server: ```bash npm run dev @@ -35,4 +37,5 @@ npm run build You can preview the production build with `npm run preview`. -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. +> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target +> environment. From d189f7b6b7027796b429074a3d6ae55ffc51ce33 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 26 Apr 2024 13:19:29 +0200 Subject: [PATCH 8/8] fix unit tests --- packages/svelte/test/performance.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/svelte/test/performance.test.ts b/packages/svelte/test/performance.test.ts index 53cf80d04d17..99ac99698682 100644 --- a/packages/svelte/test/performance.test.ts +++ b/packages/svelte/test/performance.test.ts @@ -53,7 +53,7 @@ describe('Sentry.trackComponent()', () => { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte', }, - description: '', + description: '', op: 'ui.svelte.init', origin: 'auto.ui.svelte', parent_span_id: rootSpanId, @@ -68,7 +68,7 @@ describe('Sentry.trackComponent()', () => { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte', }, - description: '', + description: '', op: 'ui.svelte.update', origin: 'auto.ui.svelte', parent_span_id: rootSpanId, @@ -108,7 +108,7 @@ describe('Sentry.trackComponent()', () => { 'sentry.op': 'ui.svelte.init', 'sentry.origin': 'auto.ui.svelte', }, - description: '', + description: '', op: 'ui.svelte.init', origin: 'auto.ui.svelte', parent_span_id: rootSpanId, @@ -123,7 +123,7 @@ describe('Sentry.trackComponent()', () => { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte', }, - description: '', + description: '', op: 'ui.svelte.update', origin: 'auto.ui.svelte', parent_span_id: rootSpanId, @@ -138,7 +138,7 @@ describe('Sentry.trackComponent()', () => { 'sentry.op': 'ui.svelte.update', 'sentry.origin': 'auto.ui.svelte', }, - description: '', + description: '', op: 'ui.svelte.update', origin: 'auto.ui.svelte', parent_span_id: rootSpanId,