From 5ba79d6c5c7f745452d9e002491818ab10bc3f49 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 11:58:42 +0100 Subject: [PATCH 1/7] feat(astro): Add Astro 5 support --- packages/astro/package.json | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index 48d2424414ba..a678100bbd90 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -4,22 +4,14 @@ "description": "Official Sentry SDK for Astro", "repository": "git://github.com/getsentry/sentry-javascript.git", "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/astro", - "keywords": [ - "withastro", - "astro-component", - "astro-integration", - "sentry", - "apm" - ], + "keywords": ["withastro", "astro-component", "astro-integration", "sentry", "apm"], "author": "Sentry", "license": "MIT", "engines": { "node": ">=18.14.1" }, "type": "module", - "files": [ - "/build" - ], + "files": ["/build"], "main": "build/cjs/index.client.js", "module": "build/esm/index.server.js", "browser": "build/esm/index.client.js", @@ -53,7 +45,7 @@ "access": "public" }, "peerDependencies": { - "astro": ">=3.x || >=4.0.0-beta" + "astro": ">=3.x || >=4.0.0-beta || >=5.x" }, "dependencies": { "@sentry/browser": "8.42.0", From fafd584aed055031d1cdee7b716475e3e3df814b Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 13:51:48 +0100 Subject: [PATCH 2/7] add astro 5 e2e test app with server island --- .../test-applications/astro-5/.gitignore | 24 ++ .../test-applications/astro-5/.npmrc | 2 + .../astro-5/.vscode/extensions.json | 4 + .../astro-5/.vscode/launch.json | 11 + .../test-applications/astro-5/README.md | 48 ++++ .../astro-5/astro.config.mjs | 21 ++ .../test-applications/astro-5/package.json | 21 ++ .../astro-5/playwright.config.mjs | 13 ++ .../astro-5/public/favicon.svg | 9 + .../astro-5/sentry.client.config.js | 9 + .../astro-5/sentry.server.config.js | 12 + .../astro-5/src/assets/astro.svg | 1 + .../astro-5/src/assets/background.svg | 1 + .../astro-5/src/components/Avatar.astro | 6 + .../astro-5/src/components/Welcome.astro | 209 ++++++++++++++++++ .../astro-5/src/layouts/Layout.astro | 22 ++ .../src/pages/client-error/index.astro | 11 + .../astro-5/src/pages/endpoint-error/api.ts | 15 ++ .../src/pages/endpoint-error/index.astro | 9 + .../astro-5/src/pages/index.astro | 21 ++ .../src/pages/server-island/index.astro | 16 ++ .../astro-5/src/pages/ssr-error/index.astro | 13 ++ .../astro-5/src/pages/test-ssr/index.astro | 15 ++ .../astro-5/src/pages/test-static/index.astro | 15 ++ .../astro-5/start-event-proxy.mjs | 6 + .../astro-5/tests/errors.client.test.ts | 80 +++++++ .../astro-5/tests/errors.server.test.ts | 164 ++++++++++++++ .../astro-5/tests/tracing.dynamic.test.ts | 123 +++++++++++ .../tests/tracing.serverIslands.test.ts | 86 +++++++ .../astro-5/tests/tracing.static.test.ts | 62 ++++++ .../test-applications/astro-5/tsconfig.json | 5 + 31 files changed, 1054 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/README.md create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/package.json create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/public/favicon.svg create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/sentry.client.config.js create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/assets/astro.svg create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/assets/background.svg create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/components/Welcome.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/layouts/Layout.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/client-error/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/api.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/server-island/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/ssr-error/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-ssr/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-static/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/tests/errors.client.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.dynamic.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.static.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-5/tsconfig.json diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.gitignore b/dev-packages/e2e-tests/test-applications/astro-5/.gitignore new file mode 100644 index 000000000000..016b59ea143d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/.gitignore @@ -0,0 +1,24 @@ +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.npmrc b/dev-packages/e2e-tests/test-applications/astro-5/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-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/astro-5/.vscode/extensions.json b/dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json new file mode 100644 index 000000000000..22a15055d638 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json b/dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json new file mode 100644 index 000000000000..d6422097621f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/dev-packages/e2e-tests/test-applications/astro-5/README.md b/dev-packages/e2e-tests/test-applications/astro-5/README.md new file mode 100644 index 000000000000..ff19a3e7ece8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/README.md @@ -0,0 +1,48 @@ +# Astro Starter Kit: Basics + +```sh +npm create astro@latest -- --template basics +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554) + +## 🚀 Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +```text +/ +├── public/ +│ └── favicon.svg +├── src/ +│ ├── layouts/ +│ │ └── Layout.astro +│ └── pages/ +│ └── index.astro +└── package.json +``` + +To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/). + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs new file mode 100644 index 000000000000..80efcc4291c4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs @@ -0,0 +1,21 @@ +// @ts-check +import { defineConfig } from 'astro/config'; +import sentry from '@sentry/astro'; + +import node from '@astrojs/node'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + sentry({ + debug: true, + sourceMapsUploadOptions: { + enabled: false, + }, + }), + ], + output: 'server', + adapter: node({ + mode: 'standalone', + }), +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/package.json b/dev-packages/e2e-tests/test-applications/astro-5/package.json new file mode 100644 index 000000000000..a41a919b0283 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/package.json @@ -0,0 +1,21 @@ +{ + "name": "astro-5", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "TEST_ENV=production playwright test" + }, + "dependencies": { + "@astrojs/internal-helpers": "^0.4.2", + "@astrojs/node": "^9.0.0", + "@playwright/test": "^1.46.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@sentry/astro": "^8.42.0", + "astro": "^5.0.3" + } +} diff --git a/dev-packages/e2e-tests/test-applications/astro-5/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/astro-5/playwright.config.mjs new file mode 100644 index 000000000000..cd6ed611fb4a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/playwright.config.mjs @@ -0,0 +1,13 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const testEnv = process.env.TEST_ENV; + +if (!testEnv) { + throw new Error('No test env defined'); +} + +const config = getPlaywrightConfig({ + startCommand: 'node ./dist/server/entry.mjs', +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/astro-5/public/favicon.svg b/dev-packages/e2e-tests/test-applications/astro-5/public/favicon.svg new file mode 100644 index 000000000000..f157bd1c5e28 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/sentry.client.config.js b/dev-packages/e2e-tests/test-applications/astro-5/sentry.client.config.js new file mode 100644 index 000000000000..7bb40f0c60d4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/sentry.client.config.js @@ -0,0 +1,9 @@ +import * as Sentry from '@sentry/astro'; + +Sentry.init({ + dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, + environment: 'qa', + tracesSampleRate: 1.0, + tunnel: 'http://localhost:3031/', // proxy server + integrations: [Sentry.browserTracingIntegration()], +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js b/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js new file mode 100644 index 000000000000..9aac4ca99d74 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js @@ -0,0 +1,12 @@ +import * as Sentry from '@sentry/astro'; + +Sentry.init({ + dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, + environment: 'qa', + tracesSampleRate: 1.0, + tunnel: 'http://localhost:3031/', // proxy server + beforeSendTransaction: transaction => { + console.log('beforeSendTransaction', transaction); + return transaction; + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/assets/astro.svg b/dev-packages/e2e-tests/test-applications/astro-5/src/assets/astro.svg new file mode 100644 index 000000000000..8cf8fb0c7da6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/assets/astro.svg @@ -0,0 +1 @@ + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/assets/background.svg b/dev-packages/e2e-tests/test-applications/astro-5/src/assets/background.svg new file mode 100644 index 000000000000..4b2be0ac0e47 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/assets/background.svg @@ -0,0 +1 @@ + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro new file mode 100644 index 000000000000..f2473b98e164 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro @@ -0,0 +1,6 @@ +--- +--- + +User avatar diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/components/Welcome.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/components/Welcome.astro new file mode 100644 index 000000000000..6b7b9c70e869 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/components/Welcome.astro @@ -0,0 +1,209 @@ +--- +import astroLogo from '../assets/astro.svg'; +import background from '../assets/background.svg'; +--- + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/layouts/Layout.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/layouts/Layout.astro new file mode 100644 index 000000000000..e455c6106729 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/layouts/Layout.astro @@ -0,0 +1,22 @@ + + + + + + + + Astro Basics + + + + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/client-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/client-error/index.astro new file mode 100644 index 000000000000..facd6f077a6e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/client-error/index.astro @@ -0,0 +1,11 @@ +--- +import Layout from "../../layouts/Layout.astro"; +--- + + + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/api.ts b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/api.ts new file mode 100644 index 000000000000..a76accdba010 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/api.ts @@ -0,0 +1,15 @@ +import type { APIRoute } from 'astro'; + +export const prerender = false; + +export const GET: APIRoute = ({ request, url }) => { + if (url.searchParams.has('error')) { + throw new Error('Endpoint Error'); + } + return new Response( + JSON.stringify({ + search: url.search, + sp: url.searchParams, + }), + ); +}; diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/index.astro new file mode 100644 index 000000000000..f025c76f8365 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/endpoint-error/index.astro @@ -0,0 +1,9 @@ +--- +import Layout from "../../layouts/Layout.astro"; + +export const prerender = false; +--- + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/index.astro new file mode 100644 index 000000000000..457d94f43457 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/index.astro @@ -0,0 +1,21 @@ +--- +import Welcome from '../components/Welcome.astro'; +import Layout from '../layouts/Layout.astro'; + +// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build +// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh. +--- + + +
+

Astro E2E Test App

+ +
+
diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/server-island/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/server-island/index.astro new file mode 100644 index 000000000000..d0544ac4f32f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/server-island/index.astro @@ -0,0 +1,16 @@ +--- +import Avatar from '../../components/Avatar.astro'; +import Layout from '../../layouts/Layout.astro'; + +export const prerender = true; +--- + + +

This page is static, except for the avatar which is loaded dynamically from the server

+ + +

Fallback

+
+ +
+ diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/ssr-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/ssr-error/index.astro new file mode 100644 index 000000000000..4ecb7466de70 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/ssr-error/index.astro @@ -0,0 +1,13 @@ +--- +import Layout from "../../layouts/Layout.astro"; + +const a = {} as any; +console.log(a.foo.x); +export const prerender = false; +--- + + + +

Page with SSR error

+ +
diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-ssr/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-ssr/index.astro new file mode 100644 index 000000000000..58f5d80198d7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-ssr/index.astro @@ -0,0 +1,15 @@ +--- +import Layout from "../../layouts/Layout.astro" + +export const prerender = false +--- + + + +

+ This is a server page +

+ + + +
diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-static/index.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-static/index.astro new file mode 100644 index 000000000000..f71bf00c9adf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/pages/test-static/index.astro @@ -0,0 +1,15 @@ +--- +import Layout from "../../layouts/Layout.astro"; + +export const prerender = true; +--- + + + +

+ This is a static page +

+ + + +
diff --git a/dev-packages/e2e-tests/test-applications/astro-5/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/astro-5/start-event-proxy.mjs new file mode 100644 index 000000000000..875a9a2afac1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'astro-5', +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.client.test.ts new file mode 100644 index 000000000000..22572d009202 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.client.test.ts @@ -0,0 +1,80 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test.describe('client-side errors', () => { + test('captures error thrown on click', async ({ page }) => { + const errorEventPromise = waitForError('astro-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'client error'; + }); + + await page.goto('/client-error'); + + await page.getByText('Throw Error').click(); + + const errorEvent = await errorEventPromise; + + const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames; + + expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual( + expect.objectContaining({ + colno: expect.any(Number), + lineno: expect.any(Number), + filename: expect.stringContaining('/client-error'), + function: 'HTMLButtonElement.onclick', + in_app: true, + }), + ); + + expect(errorEvent).toMatchObject({ + exception: { + values: [ + { + mechanism: { + handled: false, + type: 'onerror', + }, + type: 'Error', + value: 'client error', + stacktrace: expect.any(Object), // detailed check above + }, + ], + }, + level: 'error', + platform: 'javascript', + request: { + url: expect.stringContaining('/client-error'), + headers: { + 'User-Agent': expect.any(String), + }, + }, + event_id: expect.stringMatching(/[a-f0-9]{32}/), + timestamp: expect.any(Number), + sdk: { + integrations: expect.arrayContaining([ + 'InboundFilters', + 'FunctionToString', + 'BrowserApiErrors', + 'Breadcrumbs', + 'GlobalHandlers', + 'LinkedErrors', + 'Dedupe', + 'HttpContext', + 'BrowserSession', + 'BrowserTracing', + ]), + name: 'sentry.javascript.astro', + version: expect.any(String), + packages: expect.any(Array), + }, + transaction: '/client-error', + contexts: { + trace: { + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + }, + }, + environment: 'qa', + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts new file mode 100644 index 000000000000..d6a9514da1d1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/errors.server.test.ts @@ -0,0 +1,164 @@ +import { expect, test } from '@playwright/test'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; + +test.describe('server-side errors', () => { + test('captures SSR error', async ({ page }) => { + const errorEventPromise = waitForError('astro-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === "Cannot read properties of undefined (reading 'x')"; + }); + + const transactionEventPromise = waitForTransaction('astro-5', transactionEvent => { + return transactionEvent.transaction === 'GET /ssr-error'; + }); + + await page.goto('/ssr-error'); + + const errorEvent = await errorEventPromise; + const transactionEvent = await transactionEventPromise; + + expect(transactionEvent).toMatchObject({ + transaction: 'GET /ssr-error', + spans: [], + }); + + const traceId = transactionEvent.contexts?.trace?.trace_id; + const spanId = transactionEvent.contexts?.trace?.span_id; + + expect(traceId).toMatch(/[a-f0-9]{32}/); + expect(spanId).toMatch(/[a-f0-9]{16}/); + expect(transactionEvent.contexts?.trace?.parent_span_id).toBeUndefined(); + + expect(errorEvent).toMatchObject({ + contexts: { + app: expect.any(Object), + cloud_resource: expect.any(Object), + culture: expect.any(Object), + device: expect.any(Object), + os: expect.any(Object), + runtime: expect.any(Object), + trace: { + span_id: spanId, + trace_id: traceId, + }, + }, + environment: 'qa', + event_id: expect.stringMatching(/[a-f0-9]{32}/), + exception: { + values: [ + { + mechanism: { + data: { + function: 'astroMiddleware', + }, + handled: false, + type: 'astro', + }, + stacktrace: expect.any(Object), + type: 'TypeError', + value: "Cannot read properties of undefined (reading 'x')", + }, + ], + }, + platform: 'node', + request: { + cookies: {}, + headers: expect.objectContaining({ + // demonstrates that requestData integration is getting data + host: 'localhost:3030', + 'user-agent': expect.any(String), + }), + method: 'GET', + url: expect.stringContaining('/ssr-error'), + }, + sdk: { + integrations: expect.any(Array), + name: 'sentry.javascript.astro', + packages: expect.any(Array), + version: expect.any(String), + }, + server_name: expect.any(String), + timestamp: expect.any(Number), + transaction: 'GET /ssr-error', + }); + }); + + test('captures endpoint error', async ({ page }) => { + const errorEventPromise = waitForError('astro-5', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Endpoint Error'; + }); + const transactionEventApiPromise = waitForTransaction('astro-5', transactionEvent => { + return transactionEvent.transaction === 'GET /endpoint-error/api'; + }); + const transactionEventEndpointPromise = waitForTransaction('astro-5', transactionEvent => { + return transactionEvent.transaction === 'GET /endpoint-error'; + }); + + await page.goto('/endpoint-error'); + await page.getByText('Get Data').click(); + + const errorEvent = await errorEventPromise; + const transactionEventApi = await transactionEventApiPromise; + const transactionEventEndpoint = await transactionEventEndpointPromise; + + expect(transactionEventEndpoint).toMatchObject({ + transaction: 'GET /endpoint-error', + spans: [], + }); + + const traceId = transactionEventEndpoint.contexts?.trace?.trace_id; + const endpointSpanId = transactionEventApi.contexts?.trace?.span_id; + + expect(traceId).toMatch(/[a-f0-9]{32}/); + expect(endpointSpanId).toMatch(/[a-f0-9]{16}/); + + expect(transactionEventApi).toMatchObject({ + transaction: 'GET /endpoint-error/api', + spans: [], + }); + + const spanId = transactionEventApi.contexts?.trace?.span_id; + const parentSpanId = transactionEventApi.contexts?.trace?.parent_span_id; + + expect(spanId).toMatch(/[a-f0-9]{16}/); + // TODO: This is incorrect, for whatever reason, it should be the endpointSpanId ideally + expect(parentSpanId).toMatch(/[a-f0-9]{16}/); + expect(parentSpanId).not.toEqual(endpointSpanId); + + expect(errorEvent).toMatchObject({ + contexts: { + trace: { + parent_span_id: parentSpanId, + span_id: spanId, + trace_id: traceId, + }, + }, + exception: { + values: [ + { + mechanism: { + data: { + function: 'astroMiddleware', + }, + handled: false, + type: 'astro', + }, + stacktrace: expect.any(Object), + type: 'Error', + value: 'Endpoint Error', + }, + ], + }, + platform: 'node', + request: { + cookies: {}, + headers: expect.objectContaining({ + accept: expect.any(String), + }), + method: 'GET', + query_string: 'error=1', + url: expect.stringContaining('endpoint-error/api?error=1'), + }, + transaction: 'GET /endpoint-error/api', + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.dynamic.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.dynamic.test.ts new file mode 100644 index 000000000000..8c0e2c0c8850 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.dynamic.test.ts @@ -0,0 +1,123 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test.describe('tracing in dynamically rendered (ssr) routes', () => { + test('sends server and client pageload spans with the same trace id', async ({ page }) => { + const clientPageloadTxnPromise = waitForTransaction('astro-5', txnEvent => { + return txnEvent?.transaction === '/test-ssr'; + }); + + const serverPageRequestTxnPromise = waitForTransaction('astro-5', txnEvent => { + return txnEvent?.transaction === 'GET /test-ssr'; + }); + + await page.goto('/test-ssr'); + + const clientPageloadTxn = await clientPageloadTxnPromise; + const serverPageRequestTxn = await serverPageRequestTxnPromise; + + const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id; + const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id; + + const serverPageRequestTraceId = serverPageRequestTxn.contexts?.trace?.trace_id; + const serverPageloadSpanId = serverPageRequestTxn.contexts?.trace?.span_id; + + expect(clientPageloadTraceId).toEqual(serverPageRequestTraceId); + expect(clientPageloadParentSpanId).toEqual(serverPageloadSpanId); + + expect(clientPageloadTxn).toMatchObject({ + contexts: { + trace: { + data: expect.objectContaining({ + 'sentry.op': 'pageload', + 'sentry.origin': 'auto.pageload.browser', + 'sentry.sample_rate': 1, + 'sentry.source': 'url', + }), + op: 'pageload', + origin: 'auto.pageload.browser', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + environment: 'qa', + event_id: expect.stringMatching(/[a-f0-9]{32}/), + measurements: expect.any(Object), + platform: 'javascript', + request: expect.any(Object), + sdk: { + integrations: expect.any(Array), + name: 'sentry.javascript.astro', + packages: expect.any(Array), + version: expect.any(String), + }, + spans: expect.any(Array), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + transaction: '/test-ssr', + transaction_info: { + source: 'url', + }, + type: 'transaction', + }); + + expect(serverPageRequestTxn).toMatchObject({ + breadcrumbs: expect.any(Array), + contexts: { + app: expect.any(Object), + cloud_resource: expect.any(Object), + culture: expect.any(Object), + device: expect.any(Object), + os: expect.any(Object), + otel: expect.any(Object), + runtime: expect.any(Object), + trace: { + data: { + 'http.response.status_code': 200, + method: 'GET', + 'sentry.op': 'http.server', + 'sentry.origin': 'auto.http.astro', + 'sentry.sample_rate': 1, + 'sentry.source': 'route', + url: expect.stringContaining('/test-ssr'), + }, + op: 'http.server', + origin: 'auto.http.astro', + status: 'ok', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + environment: 'qa', + event_id: expect.stringMatching(/[a-f0-9]{32}/), + platform: 'node', + request: { + cookies: {}, + headers: expect.objectContaining({ + // demonstrates that request data integration can extract headers + accept: expect.any(String), + 'accept-encoding': expect.any(String), + 'user-agent': expect.any(String), + }), + method: 'GET', + url: expect.stringContaining('/test-ssr'), + }, + sdk: { + integrations: expect.any(Array), + name: 'sentry.javascript.astro', + packages: expect.any(Array), + version: expect.any(String), + }, + server_name: expect.any(String), + spans: expect.any(Array), + start_timestamp: expect.any(Number), + timestamp: expect.any(Number), + transaction: 'GET /test-ssr', + transaction_info: { + source: 'route', + }, + type: 'transaction', + }); + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts new file mode 100644 index 000000000000..22b936e9ecca --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts @@ -0,0 +1,86 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test.describe('tracing in static routes with server islands', () => { + test('only sends client pageload transaction and server island endpoint transaction', async ({ page }) => { + const clientPageloadTxnPromise = waitForTransaction('astro-5', txnEvent => { + return txnEvent?.transaction === '/server-island'; + }); + + const serverIslandEndpointTxnPromise = waitForTransaction('astro-5', evt => { + return !!evt.transaction?.startsWith('GET /_server-islands'); + }); + + await page.goto('/server-island'); + + const clientPageloadTxn = await clientPageloadTxnPromise; + + const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id; + const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id; + + const sentryTraceMetaTagContent = await page.locator('meta[name="sentry-trace"]').getAttribute('content'); + const baggageMetaTagContent = await page.locator('meta[name="baggage"]').getAttribute('content'); + + const [metaTraceId, metaParentSpanId, metaSampled] = sentryTraceMetaTagContent?.split('-') || []; + + expect(clientPageloadTraceId).toMatch(/[a-f0-9]{32}/); + expect(clientPageloadParentSpanId).toMatch(/[a-f0-9]{16}/); + expect(metaSampled).toBe('1'); + + expect(clientPageloadTxn).toMatchObject({ + contexts: { + trace: { + data: expect.objectContaining({ + 'sentry.op': 'pageload', + 'sentry.origin': 'auto.pageload.browser', + 'sentry.sample_rate': 1, + 'sentry.source': 'url', + }), + op: 'pageload', + origin: 'auto.pageload.browser', + parent_span_id: metaParentSpanId, + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: metaTraceId, + }, + }, + platform: 'javascript', + transaction: '/server-island', + transaction_info: { + source: 'url', + }, + type: 'transaction', + }); + + expect(baggageMetaTagContent).toContain('sentry-transaction=GET%20%2Fserver-island%2F'); // URL-encoded for 'GET /test-static/' + expect(baggageMetaTagContent).toContain('sentry-sampled=true'); + + const serverIslandEndpointTxn = await serverIslandEndpointTxnPromise; + + expect(serverIslandEndpointTxn).toMatchObject({ + contexts: { + trace: { + data: expect.objectContaining({ + 'sentry.op': 'http.server', + 'sentry.origin': 'auto.http.astro', + 'sentry.sample_rate': 1, + 'sentry.source': 'route', + }), + op: 'http.server', + origin: 'auto.http.astro', + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + transaction: 'GET /_server-islands/[name]', + }); + + const serverIslandEndpointTraceId = serverIslandEndpointTxn.contexts?.trace?.trace_id; + + // unfortunately, the server island trace id is not the same as the client pageload trace id + // this is because the server island endpoint request is made as a resource link request, + // meaning our fetch instrumentation can't attach headers to the request :( + expect(serverIslandEndpointTraceId).not.toBe(clientPageloadTraceId); + + await page.waitForTimeout(1000); // wait another sec to ensure no server transaction is sent + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.static.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.static.test.ts new file mode 100644 index 000000000000..9c202da53542 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.static.test.ts @@ -0,0 +1,62 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test.describe('tracing in static/pre-rendered routes', () => { + test('only sends client pageload span with traceId from pre-rendered tags', async ({ page }) => { + const clientPageloadTxnPromise = waitForTransaction('astro-5', txnEvent => { + return txnEvent?.transaction === '/test-static'; + }); + + waitForTransaction('astro-5', evt => { + if (evt.platform !== 'javascript') { + throw new Error('Server transaction should not be sent'); + } + return false; + }); + + await page.goto('/test-static'); + + const clientPageloadTxn = await clientPageloadTxnPromise; + + const clientPageloadTraceId = clientPageloadTxn.contexts?.trace?.trace_id; + const clientPageloadParentSpanId = clientPageloadTxn.contexts?.trace?.parent_span_id; + + const sentryTraceMetaTagContent = await page.locator('meta[name="sentry-trace"]').getAttribute('content'); + const baggageMetaTagContent = await page.locator('meta[name="baggage"]').getAttribute('content'); + + const [metaTraceId, metaParentSpanId, metaSampled] = sentryTraceMetaTagContent?.split('-') || []; + + expect(clientPageloadTraceId).toMatch(/[a-f0-9]{32}/); + expect(clientPageloadParentSpanId).toMatch(/[a-f0-9]{16}/); + expect(metaSampled).toBe('1'); + + expect(clientPageloadTxn).toMatchObject({ + contexts: { + trace: { + data: expect.objectContaining({ + 'sentry.op': 'pageload', + 'sentry.origin': 'auto.pageload.browser', + 'sentry.sample_rate': 1, + 'sentry.source': 'url', + }), + op: 'pageload', + origin: 'auto.pageload.browser', + parent_span_id: metaParentSpanId, + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: metaTraceId, + }, + }, + platform: 'javascript', + transaction: '/test-static', + transaction_info: { + source: 'url', + }, + type: 'transaction', + }); + + expect(baggageMetaTagContent).toContain('sentry-transaction=GET%20%2Ftest-static%2F'); // URL-encoded for 'GET /test-static/' + expect(baggageMetaTagContent).toContain('sentry-sampled=true'); + + await page.waitForTimeout(1000); // wait another sec to ensure no server transaction is sent + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tsconfig.json b/dev-packages/e2e-tests/test-applications/astro-5/tsconfig.json new file mode 100644 index 000000000000..8bf91d3bb997 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-5/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +} From f82ad85c51c378c2edad174f41f4db4bdf077cf3 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 13:54:05 +0100 Subject: [PATCH 3/7] formatting --- .../e2e-tests/test-applications/astro-5/astro.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs index 80efcc4291c4..f38dac6171bf 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs +++ b/dev-packages/e2e-tests/test-applications/astro-5/astro.config.mjs @@ -1,6 +1,6 @@ +import sentry from '@sentry/astro'; // @ts-check import { defineConfig } from 'astro/config'; -import sentry from '@sentry/astro'; import node from '@astrojs/node'; From 772f51b9b99fbd0b92793c25128550f4da9f2d24 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 14:03:44 +0100 Subject: [PATCH 4/7] cleanup --- .../test-applications/astro-5/sentry.server.config.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js b/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js index 9aac4ca99d74..2b79ec0ed337 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js +++ b/dev-packages/e2e-tests/test-applications/astro-5/sentry.server.config.js @@ -5,8 +5,4 @@ Sentry.init({ environment: 'qa', tracesSampleRate: 1.0, tunnel: 'http://localhost:3031/', // proxy server - beforeSendTransaction: transaction => { - console.log('beforeSendTransaction', transaction); - return transaction; - }, }); From c83e0d5d945247e10fb6049ef95211858d331e36 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 14:10:59 +0100 Subject: [PATCH 5/7] more cleanup --- .../test-applications/astro-5/src/components/Avatar.astro | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro b/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro index f2473b98e164..09a539f14e64 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro +++ b/dev-packages/e2e-tests/test-applications/astro-5/src/components/Avatar.astro @@ -1,6 +1,3 @@ --- --- - User avatar From 28609377f2b7ac1c903ae3040f41786acd2dfb83 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 14:16:02 +0100 Subject: [PATCH 6/7] assert on server island resource.link span in pageload transaction --- .../astro-5/tests/tracing.serverIslands.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts index 22b936e9ecca..a6b288f4de71 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts +++ b/dev-packages/e2e-tests/test-applications/astro-5/tests/tracing.serverIslands.test.ts @@ -51,6 +51,19 @@ test.describe('tracing in static routes with server islands', () => { type: 'transaction', }); + const pageloadSpans = clientPageloadTxn.spans; + + // pageload transaction contains a resource link span for the preloaded server island request + expect(pageloadSpans).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + op: 'resource.link', + origin: 'auto.resource.browser.metrics', + description: expect.stringMatching(/\/_server-islands\/Avatar.*$/), + }), + ]), + ); + expect(baggageMetaTagContent).toContain('sentry-transaction=GET%20%2Fserver-island%2F'); // URL-encoded for 'GET /test-static/' expect(baggageMetaTagContent).toContain('sentry-sampled=true'); From 78c32ab0532fd2462b38c87c18ba66783dd17cad Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 6 Dec 2024 14:48:34 +0100 Subject: [PATCH 7/7] rm -rf .vscode --- .../e2e-tests/test-applications/astro-5/.gitignore | 2 ++ .../test-applications/astro-5/.vscode/extensions.json | 4 ---- .../test-applications/astro-5/.vscode/launch.json | 11 ----------- 3 files changed, 2 insertions(+), 15 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json delete mode 100644 dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.gitignore b/dev-packages/e2e-tests/test-applications/astro-5/.gitignore index 016b59ea143d..560782d47d98 100644 --- a/dev-packages/e2e-tests/test-applications/astro-5/.gitignore +++ b/dev-packages/e2e-tests/test-applications/astro-5/.gitignore @@ -22,3 +22,5 @@ pnpm-debug.log* # jetbrains setting folder .idea/ + +test-results diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json b/dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json deleted file mode 100644 index 22a15055d638..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-5/.vscode/extensions.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "recommendations": ["astro-build.astro-vscode"], - "unwantedRecommendations": [] -} diff --git a/dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json b/dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json deleted file mode 100644 index d6422097621f..000000000000 --- a/dev-packages/e2e-tests/test-applications/astro-5/.vscode/launch.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "command": "./node_modules/.bin/astro dev", - "name": "Development server", - "request": "launch", - "type": "node-terminal" - } - ] -}