Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as Sentry from '@sentry/browser';

Sentry.init({
traceLifecycle: 'static',
environment: 'qa',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
tracesSampleRate: 1.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import handler from '@tanstack/react-start/server-entry';

export default Sentry.withSentry(
(env: Env) => ({
traceLifecycle: 'static',
dsn: env.E2E_TEST_DSN,
tunnel: 'http://localhost:3031/',
tracesSampleRate: 1.0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
import { getSpanOp, waitForError, waitForStreamedSpan } from '@sentry-internal/test-utils';

test('Sends client-side error to Sentry', async ({ page }) => {
const errorEventPromise = waitForError('tanstackstart-react-cloudflare', errorEvent => {
Expand Down Expand Up @@ -107,16 +107,16 @@ test('Does not send SSR loader error to Sentry', async ({ baseURL, page }) => {
if (!event.type && event.exception?.values?.[0]?.value === 'Sentry SSR Test Error') {
errorEventOccurred = true;
}
return event?.transaction === 'GET /ssr-error';
return false;
});

const transactionEventPromise = waitForTransaction('tanstackstart-react-cloudflare', transactionEvent => {
return transactionEvent?.transaction === 'GET /ssr-error';
const serverSpanPromise = waitForStreamedSpan('tanstackstart-react-cloudflare', span => {
return span.is_segment && getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/ssr-error';
});

await page.goto('/ssr-error');

await transactionEventPromise;
await serverSpanPromise;

await (await fetch(`${baseURL}/api/flush`)).text();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
import { collectStreamedSpans, getSpanOp } from '@sentry-internal/test-utils';

test.describe('Trace propagation', () => {
test('should inject metatags in ssr pageload', async ({ page }) => {
Expand All @@ -18,19 +18,30 @@ test.describe('Trace propagation', () => {
});

test('should have trace connection between server and client', async ({ page }) => {
const serverTxPromise = waitForTransaction('tanstackstart-react-cloudflare', transactionEvent => {
return transactionEvent?.contexts?.trace?.op === 'http.server' && transactionEvent?.transaction === 'GET /';
});

const clientTxPromise = waitForTransaction('tanstackstart-react-cloudflare', transactionEvent => {
return transactionEvent?.contexts?.trace?.op === 'pageload' && transactionEvent?.transaction === '/';
const spansPromise = collectStreamedSpans('tanstackstart-react-cloudflare', spans => {
return (
spans.some(
span => span.is_segment && getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/',
) &&
spans.some(
span =>
span.is_segment &&
getSpanOp(span) === 'pageload' &&
(span.name === '/' || span.attributes['url.path']?.value === '/'),
)
);
});

await page.goto('/');

const serverTx = await serverTxPromise;
const clientTx = await clientTxPromise;
const spans = await spansPromise;
const serverSpan = spans.find(
span => span.is_segment && getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/',
);
const clientSpan = spans.find(span => span.is_segment && getSpanOp(span) === 'pageload');

expect(clientTx.contexts?.trace?.trace_id).toBe(serverTx.contexts?.trace?.trace_id);
expect(serverSpan).toBeDefined();
expect(clientSpan).toBeDefined();
expect(clientSpan?.trace_id).toBe(serverSpan?.trace_id);
});
});
Original file line number Diff line number Diff line change
@@ -1,99 +1,103 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';

test('Sends a server function transaction with span from wrapFetchWithSentry', async ({ page }) => {
const transactionEventPromise = waitForTransaction('tanstackstart-react-cloudflare', transactionEvent => {
return (
transactionEvent?.contexts?.trace?.op === 'http.server' &&
!!transactionEvent?.transaction?.startsWith('GET /_serverFn')
);
});
import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '@sentry-internal/test-utils';

function isServerFnSegment(span: Parameters<typeof getSpanOp>[0]): boolean {
return (
!!span.is_segment &&
getSpanOp(span) === 'http.server' &&
String(span.attributes['url.path']?.value ?? '').startsWith('/_serverFn')
);
}

test('Sends a server function span with wrapFetchWithSentry', async ({ page }) => {
const spansPromise = collectStreamedSpans(
'tanstackstart-react-cloudflare',
spans => spans.some(isServerFnSegment) && spans.some(span => span.name === 'GET /_serverFn/testLog'),
);

await page.goto('/test-serverFn');

await expect(page.locator('#server-fn-btn')).toBeVisible();

await page.locator('#server-fn-btn').click();

const transactionEvent = await transactionEventPromise;
const spans = await spansPromise;

expect(transactionEvent.contexts?.trace).toMatchObject({
op: 'http.server',
origin: 'auto.http.cloudflare',
const serverSegment = spans.find(isServerFnSegment);
expect(serverSegment?.attributes).toMatchObject({
'sentry.op': { type: 'string', value: 'http.server' },
'sentry.origin': { type: 'string', value: 'auto.http.cloudflare' },
});

expect(transactionEvent?.spans).toHaveLength(1);
expect(transactionEvent?.spans).toEqual([
expect.objectContaining({
description: 'GET /_serverFn/testLog',
op: 'function',
origin: 'auto.function.tanstackstart.server',
data: {
'sentry.op': 'function',
'sentry.origin': 'auto.function.tanstackstart.server',
'tanstackstart.function.id': expect.any(String),
'tanstackstart.function.filename': 'src/routes/test-serverFn.tsx',
},
}),
]);
expect(spans).toEqual(
expect.arrayContaining([
expect.objectContaining({
name: 'GET /_serverFn/testLog',
attributes: expect.objectContaining({
'sentry.op': { type: 'string', value: 'function' },
'sentry.origin': { type: 'string', value: 'auto.function.tanstackstart.server' },
'tanstackstart.function.filename': { type: 'string', value: 'src/routes/test-serverFn.tsx' },
}),
}),
]),
);
});

test('Sends a server function transaction for a nested server function with manual span', async ({ page }) => {
const transactionEventPromise = waitForTransaction('tanstackstart-react-cloudflare', transactionEvent => {
return (
transactionEvent?.contexts?.trace?.op === 'http.server' &&
!!transactionEvent?.transaction?.startsWith('GET /_serverFn')
);
});
test('Sends a server function span for a nested server function with manual span', async ({ page }) => {
const spansPromise = collectStreamedSpans(
'tanstackstart-react-cloudflare',
spans =>
spans.some(isServerFnSegment) &&
spans.some(span => span.name === 'GET /_serverFn/testNestedLog') &&
spans.some(span => span.name === 'testNestedLog'),
);

await page.goto('/test-serverFn');

await expect(page.locator('#server-fn-nested-btn')).toBeVisible();

await page.locator('#server-fn-nested-btn').click();

const transactionEvent = await transactionEventPromise;
const spans = await spansPromise;

expect(transactionEvent.contexts?.trace).toMatchObject({
op: 'http.server',
origin: 'auto.http.cloudflare',
const serverSegment = spans.find(isServerFnSegment);
expect(serverSegment?.attributes).toMatchObject({
'sentry.op': { type: 'string', value: 'http.server' },
'sentry.origin': { type: 'string', value: 'auto.http.cloudflare' },
});

expect(transactionEvent?.spans).toHaveLength(2);
expect(transactionEvent?.spans).toEqual(
expect(spans).toEqual(
expect.arrayContaining([
expect.objectContaining({
description: 'GET /_serverFn/testNestedLog',
op: 'function',
origin: 'auto.function.tanstackstart.server',
data: {
'sentry.op': 'function',
'sentry.origin': 'auto.function.tanstackstart.server',
'tanstackstart.function.id': expect.any(String),
'tanstackstart.function.filename': 'src/routes/test-serverFn.tsx',
},
name: 'GET /_serverFn/testNestedLog',
attributes: expect.objectContaining({
'sentry.op': { type: 'string', value: 'function' },
'sentry.origin': { type: 'string', value: 'auto.function.tanstackstart.server' },
'tanstackstart.function.filename': { type: 'string', value: 'src/routes/test-serverFn.tsx' },
}),
}),
expect.objectContaining({
description: 'testNestedLog',
origin: 'manual',
name: 'testNestedLog',
attributes: expect.objectContaining({
'sentry.origin': { type: 'string', value: 'manual' },
}),
}),
]),
);
});

test('Sends server-side transaction for page request', async ({ baseURL }) => {
const transactionEventPromise = waitForTransaction('tanstackstart-react-cloudflare', transactionEvent => {
return transactionEvent?.contexts?.trace?.op === 'http.server' && transactionEvent?.transaction === 'GET /';
test('Sends server-side span for page request', async ({ baseURL }) => {
const serverSpanPromise = waitForStreamedSpan('tanstackstart-react-cloudflare', span => {
return span.is_segment && getSpanOp(span) === 'http.server' && span.attributes['url.path']?.value === '/';
});

await fetch(`${baseURL}/`);

const transactionEvent = await transactionEventPromise;
const serverSpan = await serverSpanPromise;

expect(transactionEvent.transaction).toBe('GET /');
expect(transactionEvent.contexts?.trace).toMatchObject({
op: 'http.server',
origin: 'auto.http.cloudflare',
status: 'ok',
expect(serverSpan.attributes).toMatchObject({
'sentry.op': { type: 'string', value: 'http.server' },
'sentry.origin': { type: 'string', value: 'auto.http.cloudflare' },
});
expect(serverSpan.status).toBe('ok');
});
Loading