From 5c08d03dbe6c298ed94b847253c6674f2a3f3855 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 14 Aug 2024 16:32:33 +0200 Subject: [PATCH] test(e2e): Add Astro 4 E2E test app (#13375) --- .github/workflows/build.yml | 1 + .../test-applications/astro-4/.gitignore | 26 ++++ .../test-applications/astro-4/.npmrc | 2 + .../test-applications/astro-4/README.md | 6 + .../astro-4/astro.config.mjs | 29 +++++ .../test-applications/astro-4/package.json | 27 ++++ .../astro-4/playwright.config.mjs | 13 ++ .../astro-4/public/favicon.svg | 9 ++ .../astro-4/sentry.client.config.js | 8 ++ .../astro-4/sentry.server.config.js | 9 ++ .../test-applications/astro-4/src/env.d.ts | 1 + .../astro-4/src/layouts/Layout.astro | 39 ++++++ .../src/pages/client-error/index.astro | 11 ++ .../astro-4/src/pages/endpoint-error/api.ts | 15 +++ .../src/pages/endpoint-error/index.astro | 9 ++ .../astro-4/src/pages/index.astro | 36 +++++ .../astro-4/src/pages/ssr-error/index.astro | 13 ++ .../astro-4/src/pages/test-ssr/index.astro | 15 +++ .../astro-4/src/pages/test-static/index.astro | 15 +++ .../astro-4/start-event-proxy.mjs | 6 + .../astro-4/tests/errors.client.test.ts | 79 +++++++++++ .../astro-4/tests/errors.server.test.ts | 115 ++++++++++++++++ .../astro-4/tests/tracing.dynamic.test.ts | 123 ++++++++++++++++++ .../astro-4/tests/tracing.static.test.ts | 62 +++++++++ .../test-applications/astro-4/tsconfig.json | 3 + packages/astro/src/server/middleware.ts | 2 +- 26 files changed, 673 insertions(+), 1 deletion(-) create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/README.md create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/package.json create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d8ccc2c75dec..516b1d47b624 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -847,6 +847,7 @@ jobs: [ 'angular-17', 'angular-18', + 'astro-4', 'aws-lambda-layer-cjs', 'aws-serverless-esm', 'node-express', diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.gitignore b/dev-packages/e2e-tests/test-applications/astro-4/.gitignore new file mode 100644 index 000000000000..560782d47d98 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/.gitignore @@ -0,0 +1,26 @@ +# 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/ + +test-results diff --git a/dev-packages/e2e-tests/test-applications/astro-4/.npmrc b/dev-packages/e2e-tests/test-applications/astro-4/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/.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-4/README.md b/dev-packages/e2e-tests/test-applications/astro-4/README.md new file mode 100644 index 000000000000..28e41344b910 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/README.md @@ -0,0 +1,6 @@ +# Astro 4 E2E test app + +- Astro 4.x +- Output mode `hybrid` (== opt into SSR routes) +- Node adapter +- Configured for Tracing and Performance diff --git a/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs new file mode 100644 index 000000000000..96db58759ba2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/astro.config.mjs @@ -0,0 +1,29 @@ +import node from '@astrojs/node'; +import sentry from '@sentry/astro'; +import { defineConfig } from 'astro/config'; + +import spotlightjs from '@spotlightjs/astro'; + +// https://astro.build/config +export default defineConfig({ + output: 'hybrid', + integrations: [ + sentry({ + debug: true, + sourceMapsUploadOptions: { + enabled: false, + }, + }), + spotlightjs(), + ], + adapter: node({ + mode: 'standalone', + }), + vite: { + build: { + rollupOptions: { + external: ['https'], + }, + }, + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-4/package.json b/dev-packages/e2e-tests/test-applications/astro-4/package.json new file mode 100644 index 000000000000..5bc5233ef7cc --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/package.json @@ -0,0 +1,27 @@ +{ + "name": "astro-hybrid-with-static-routes", + "type": "module", + "version": "0.0.1", + "scripts": { + "dev": "astro dev --force", + "start": "astro dev", + "build": "astro check && 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/check": "^0.9.2", + "@astrojs/node": "^8.3.2", + "@playwright/test": "^1.46.0", + "@sentry/astro": "* || latest", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@spotlightjs/astro": "^2.1.6", + "astro": "^4.13.3", + "typescript": "^5.5.4" + }, + "devDependencies": { + "@astrojs/internal-helpers": "^0.4.1" + } +} diff --git a/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/astro-4/playwright.config.mjs new file mode 100644 index 000000000000..cd6ed611fb4a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/public/favicon.svg b/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg new file mode 100644 index 000000000000..f157bd1c5e28 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js b/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js new file mode 100644 index 000000000000..2b79ec0ed337 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/sentry.client.config.js @@ -0,0 +1,8 @@ +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 +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js b/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.config.js new file mode 100644 index 000000000000..0662d678dc7c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/sentry.server.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, + spotlight: true, + tunnel: 'http://localhost:3031/', // proxy server +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts b/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts new file mode 100644 index 000000000000..f964fe0cffd8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/src/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro new file mode 100644 index 000000000000..c4e54b834656 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/src/layouts/Layout.astro @@ -0,0 +1,39 @@ +--- +interface Props { + title: string; +} + +const { title } = Astro.props; +--- + + + + + + + + + + {title} + + + + + + diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/client-error/index.astro new file mode 100644 index 000000000000..facd6f077a6e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/src/pages/endpoint-error/api.ts b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/api.ts new file mode 100644 index 000000000000..a76accdba010 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/src/pages/endpoint-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/endpoint-error/index.astro new file mode 100644 index 000000000000..f025c76f8365 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/src/pages/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro new file mode 100644 index 000000000000..088205fc4028 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/index.astro @@ -0,0 +1,36 @@ +--- +import Layout from '../layouts/Layout.astro'; +--- + + +
+

Astro E2E Test App

+ +
+
+ + diff --git a/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/ssr-error/index.astro new file mode 100644 index 000000000000..4ecb7466de70 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/src/pages/test-ssr/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-ssr/index.astro new file mode 100644 index 000000000000..58f5d80198d7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/src/pages/test-static/index.astro b/dev-packages/e2e-tests/test-applications/astro-4/src/pages/test-static/index.astro new file mode 100644 index 000000000000..f71bf00c9adf --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs new file mode 100644 index 000000000000..a657dae0f425 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'astro-4', +}); diff --git a/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts new file mode 100644 index 000000000000..4cbf4bf36604 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.client.test.ts @@ -0,0 +1,79 @@ +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-4', 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', + '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-4/tests/errors.server.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts new file mode 100644 index 000000000000..d5f07ebe239a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/tests/errors.server.test.ts @@ -0,0 +1,115 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test.describe('server-side errors', () => { + test('captures SSR error', async ({ page }) => { + const errorEventPromise = waitForError('astro-4', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === "Cannot read properties of undefined (reading 'x')"; + }); + + await page.goto('/ssr-error'); + + const errorEvent = await errorEventPromise; + + 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: '', //TODO: This is a bug! We should expect.stringMatching(/[a-f0-9]{16}/) instead of '' + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + 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-4', errorEvent => { + return errorEvent?.exception?.values?.[0]?.value === 'Endpoint Error'; + }); + + await page.goto('/endpoint-error'); + await page.getByText('Get Data').click(); + + const errorEvent = await errorEventPromise; + + expect(errorEvent).toMatchObject({ + contexts: { + trace: { + parent_span_id: expect.stringMatching(/[a-f0-9]{16}/), + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: expect.stringMatching(/[a-f0-9]{32}/), + }, + }, + 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-4/tests/tracing.dynamic.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.dynamic.test.ts new file mode 100644 index 000000000000..9a295f677d96 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4', txnEvent => { + return txnEvent?.transaction === '/test-ssr'; + }); + + const serverPageRequestTxnPromise = waitForTransaction('astro-4', 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-4/tests/tracing.static.test.ts b/dev-packages/e2e-tests/test-applications/astro-4/tests/tracing.static.test.ts new file mode 100644 index 000000000000..8817b2b22aa7 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/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-4', txnEvent => { + return txnEvent?.transaction === '/test-static'; + }); + + waitForTransaction('astro-4', 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-4/tsconfig.json b/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json new file mode 100644 index 000000000000..77da9dd00982 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/astro-4/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "astro/tsconfigs/strict" +} \ No newline at end of file diff --git a/packages/astro/src/server/middleware.ts b/packages/astro/src/server/middleware.ts index 3752bd30d448..95d099ff0526 100644 --- a/packages/astro/src/server/middleware.ts +++ b/packages/astro/src/server/middleware.ts @@ -147,7 +147,7 @@ async function instrumentRequest( async span => { const originalResponse = await next(); - if (span && originalResponse.status) { + if (originalResponse.status) { setHttpStatus(span, originalResponse.status); }