From 4ae9d29ef505a2ef383f312dc726633d8b769905 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Wed, 2 Sep 2026 13:40:51 +0200 Subject: [PATCH] test(e2e): Port Svelte E2E test app to span streaming Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Xo7LcmA5Cte2uiSmp7hXSN --- .../svelte-5-static/.gitignore | 24 ++++++ .../svelte-5-static/README.md | 62 +++++++++++++++ .../svelte-5-static/index.html | 13 +++ .../svelte-5-static/package.json | 32 ++++++++ .../svelte-5-static/playwright.config.mjs | 8 ++ .../svelte-5-static/public/vite.svg | 1 + .../svelte-5-static/src/App.svelte | 52 ++++++++++++ .../svelte-5-static/src/app.css | 79 +++++++++++++++++++ .../svelte-5-static/src/assets/svelte.svg | 1 + .../svelte-5-static/src/lib/Counter.svelte | 17 ++++ .../svelte-5-static/src/main.ts | 25 ++++++ .../svelte-5-static/src/vite-env.d.ts | 2 + .../svelte-5-static/start-event-proxy.mjs | 6 ++ .../svelte-5-static/svelte.config.js | 14 ++++ .../svelte-5-static/tests/errors.test.ts | 30 +++++++ .../tests/performance.test.ts | 2 +- .../svelte-5-static/tsconfig.json | 20 +++++ .../svelte-5-static/tsconfig.node.json | 10 +++ .../svelte-5-static/vite.config.ts | 8 ++ .../test-applications/svelte-5/src/main.ts | 1 - .../svelte-5/tests/spans.test.ts | 40 ++++++++++ 21 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/README.md create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/index.html create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/package.json create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/public/vite.svg create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/src/App.svelte create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/src/app.css create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/src/assets/svelte.svg create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/src/lib/Counter.svelte create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/src/main.ts create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/src/vite-env.d.ts create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/svelte.config.js create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/tests/errors.test.ts rename dev-packages/e2e-tests/test-applications/{svelte-5 => svelte-5-static}/tests/performance.test.ts (92%) create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.node.json create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5-static/vite.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/svelte-5/tests/spans.test.ts diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/.gitignore b/dev-packages/e2e-tests/test-applications/svelte-5-static/.gitignore new file mode 100644 index 000000000000..a547bf36d8d1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/README.md b/dev-packages/e2e-tests/test-applications/svelte-5-static/README.md new file mode 100644 index 000000000000..89a8fa13539e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/README.md @@ -0,0 +1,62 @@ +# Svelte + TS + Vite + +This template should help get you started developing with Svelte and TypeScript in Vite. + +## Recommended IDE Setup + +[VS Code](https://code.visualstudio.com/) + +[Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). + +## Need an official Svelte framework? + +Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its +serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, +and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. + +## Technical considerations + +**Why use this over SvelteKit?** + +- It brings its own routing solution which might not be preferable for some users. +- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. + +This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account +the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other +`create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. + +Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been +structured similarly to SvelteKit so that it is easy to migrate. + +**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** + +Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash +references keeps the default TypeScript setting of accepting type information from the entire workspace, while also +adding `svelte` and `vite/client` type information. + +**Why include `.vscode/extensions.json`?** + +Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to +install the recommended extension upon opening the project. + +**Why enable `allowJs` in the TS template?** + +While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of +JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: +not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing +JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant. + +**Why is HMR not preserving my local component state?** + +HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and +`@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details +[here](https://github.com/rixo/svelte-hmr#svelte-hmr). + +If you have state that's important to retain within a component, consider creating an external store which would not be +replaced by HMR. + +```ts +// store.ts +// An extremely simple external store +import { writable } from 'svelte/store'; +export default writable(0); +``` diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/index.html b/dev-packages/e2e-tests/test-applications/svelte-5-static/index.html new file mode 100644 index 000000000000..b6c5f0afafd6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + Svelte + TS + + +
+ + + diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/package.json b/dev-packages/e2e-tests/test-applications/svelte-5-static/package.json new file mode 100644 index 000000000000..9a508ab13017 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/package.json @@ -0,0 +1,32 @@ +{ + "name": "svelte-5-static", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.json", + "test:prod": "TEST_ENV=production playwright test", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test:prod" + }, + "devDependencies": { + "@playwright/test": "~1.56.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@sveltejs/vite-plugin-svelte": "^3.0.2", + "@tsconfig/svelte": "^5.0.2", + "svelte": "^5.0.0-next.115", + "svelte-check": "^3.6.7", + "tslib": "^2.6.2", + "typescript": "^5.2.2", + "vite": "^5.4.11" + }, + "dependencies": { + "@sentry/svelte": "file:../../packed/sentry-svelte-packed.tgz" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/svelte-5-static/playwright.config.mjs new file mode 100644 index 000000000000..0c468af7d879 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/playwright.config.mjs @@ -0,0 +1,8 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: 'pnpm preview --port 3030', + port: 3030, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/public/vite.svg b/dev-packages/e2e-tests/test-applications/svelte-5-static/public/vite.svg new file mode 100644 index 000000000000..e7b8dfb1b2a6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/src/App.svelte b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/App.svelte new file mode 100644 index 000000000000..47d6be1385b4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/App.svelte @@ -0,0 +1,52 @@ + + +
+
+ + + + + + +
+

Vite + Svelte

+ +
+ + +
+ +

+ Check out SvelteKit, the official Svelte app framework powered by Vite! +

+ +

+ Click on the Vite and Svelte logos to learn more +

+
+ + diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/src/app.css b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/app.css new file mode 100644 index 000000000000..617f5e93c0a7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/app.css @@ -0,0 +1,79 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/src/assets/svelte.svg b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/assets/svelte.svg new file mode 100644 index 000000000000..c5e08481f8ae --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/assets/svelte.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/src/lib/Counter.svelte b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/lib/Counter.svelte new file mode 100644 index 000000000000..397bfd1c7321 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/lib/Counter.svelte @@ -0,0 +1,17 @@ + + + + +

+ doubled: {doubled} +

diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/src/main.ts b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/main.ts new file mode 100644 index 000000000000..5a475cb7ab95 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/main.ts @@ -0,0 +1,25 @@ +import { mount } from 'svelte'; +import App from './App.svelte'; +import './app.css'; + +import * as Sentry from '@sentry/svelte'; + +Sentry.init({ + traceLifecycle: 'static', + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, + tracesSampleRate: 1.0, + integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()], + tunnel: 'http://localhost:3031/', // proxy server + debug: !!process.env.DEBUG, +}); + +const target = document.getElementById('app'); + +if (!target) { + throw new Error('Could not find target element'); +} + +const app = mount(App, { target }); + +export default app; diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/src/vite-env.d.ts b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/vite-env.d.ts new file mode 100644 index 000000000000..4078e7476a2e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/svelte-5-static/start-event-proxy.mjs new file mode 100644 index 000000000000..185667e2a854 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'svelte-5-static', +}); diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/svelte.config.js b/dev-packages/e2e-tests/test-applications/svelte-5-static/svelte.config.js new file mode 100644 index 000000000000..15884bc6346a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/svelte.config.js @@ -0,0 +1,14 @@ +import { withSentryConfig } from '@sentry/svelte'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +const config = { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), +}; + +const configWithSentry = withSentryConfig(config, { + componentTracking: { trackComponents: true, trackInit: true }, +}); + +export default configWithSentry; diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/svelte-5-static/tests/errors.test.ts new file mode 100644 index 000000000000..34e4920d0cf9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/tests/errors.test.ts @@ -0,0 +1,30 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('sends an error', async ({ page }) => { + const errorPromise = waitForError('svelte-5-static', async errorEvent => { + return !errorEvent.type; + }); + + await page.goto(`/`); + + await page.locator('#errorBtn').click(); + + const error = await errorPromise; + + expect(error).toMatchObject({ + exception: { + values: [ + { + type: 'Error', + value: 'Error thrown from Svelte 5 E2E test app', + mechanism: { + type: 'auto.browser.browserapierrors.addEventListener', + handled: false, + }, + }, + ], + }, + transaction: '/', + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/svelte-5/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/svelte-5-static/tests/performance.test.ts similarity index 92% rename from dev-packages/e2e-tests/test-applications/svelte-5/tests/performance.test.ts rename to dev-packages/e2e-tests/test-applications/svelte-5-static/tests/performance.test.ts index e3623b62eb47..7ffd622648b0 100644 --- a/dev-packages/e2e-tests/test-applications/svelte-5/tests/performance.test.ts +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/tests/performance.test.ts @@ -2,7 +2,7 @@ import { expect, test } from '@playwright/test'; import { waitForTransaction } from '@sentry-internal/test-utils'; test('sends a pageload transaction with component tracking init spans', async ({ page }) => { - const transactionPromise = waitForTransaction('svelte-5', async transactionEvent => { + const transactionPromise = waitForTransaction('svelte-5-static', async transactionEvent => { return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; }); diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.json b/dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.json new file mode 100644 index 000000000000..5fb548f2b4f6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + /** + * Typecheck JS in `.svelte` and `.js` files by default. + * Disable checkJs if you'd like to use dynamic types in JS. + * Note that setting allowJs false does not prevent the use + * of JS in `.svelte` files. + */ + "allowJs": true, + "checkJs": true, + "isolatedModules": true + }, + "include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.node.json b/dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.node.json new file mode 100644 index 000000000000..d02c37dfe5fd --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true + }, + "include": ["vite.config.ts"] +} diff --git a/dev-packages/e2e-tests/test-applications/svelte-5-static/vite.config.ts b/dev-packages/e2e-tests/test-applications/svelte-5-static/vite.config.ts new file mode 100644 index 000000000000..73f8ad0c1a43 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5-static/vite.config.ts @@ -0,0 +1,8 @@ +import { svelte } from '@sveltejs/vite-plugin-svelte'; +import { defineConfig } from 'vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()], + envPrefix: 'PUBLIC_', +}); diff --git a/dev-packages/e2e-tests/test-applications/svelte-5/src/main.ts b/dev-packages/e2e-tests/test-applications/svelte-5/src/main.ts index 5a475cb7ab95..9f5fb70cea94 100644 --- a/dev-packages/e2e-tests/test-applications/svelte-5/src/main.ts +++ b/dev-packages/e2e-tests/test-applications/svelte-5/src/main.ts @@ -5,7 +5,6 @@ import './app.css'; import * as Sentry from '@sentry/svelte'; Sentry.init({ - traceLifecycle: 'static', environment: 'qa', // dynamic sampling bias to keep transactions dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, tracesSampleRate: 1.0, diff --git a/dev-packages/e2e-tests/test-applications/svelte-5/tests/spans.test.ts b/dev-packages/e2e-tests/test-applications/svelte-5/tests/spans.test.ts new file mode 100644 index 000000000000..45d1fae25167 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/svelte-5/tests/spans.test.ts @@ -0,0 +1,40 @@ +import { expect, test } from '@playwright/test'; +import { collectStreamedSpans, getSpanOp } from '@sentry-internal/test-utils'; + +test('sends a pageload span with component tracking init spans', async ({ page }) => { + const spansPromise = collectStreamedSpans('svelte-5', spansOfTrace => + spansOfTrace.some(span => getSpanOp(span) === 'pageload' && span.is_segment), + ); + + await page.goto(`/`); + + const spans = await spansPromise; + const pageloadSpan = spans.find(span => getSpanOp(span) === 'pageload' && span.is_segment); + + expect(pageloadSpan?.name).toBe('Pageload'); + expect(pageloadSpan?.attributes).toMatchObject({ + 'sentry.op': { value: 'pageload', type: 'string' }, + 'sentry.origin': { value: 'auto.pageload.browser', type: 'string' }, + 'sentry.segment.name.source': { value: 'url', type: 'string' }, + 'url.path': { value: '/', type: 'string' }, + }); + + expect(spans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + name: '', + attributes: expect.objectContaining({ + 'sentry.op': { value: 'ui.mount', type: 'string' }, + 'sentry.origin': { value: 'auto.ui.svelte', type: 'string' }, + }), + }), + expect.objectContaining({ + name: '', + attributes: expect.objectContaining({ + 'sentry.op': { value: 'ui.mount', type: 'string' }, + 'sentry.origin': { value: 'auto.ui.svelte', type: 'string' }, + }), + }), + ]), + ); +});