Skip to content
Merged
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 => {
Comment thread
RulaKhaled marked this conversation as resolved.
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,105 @@
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.id': { type: 'string', value: expect.any(String) },
'tanstackstart.function.filename': { type: 'string', value: 'src/routes/test-serverFn.tsx' },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was the assertion on tanstackstart.function.id dropped on purpose?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was accidentally dropped, thanks for catching!

}),
}),
]),
);
});

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.id': { type: 'string', value: expect.any(String) },
'tanstackstart.function.filename': { type: 'string', value: 'src/routes/test-serverFn.tsx' },

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

}),
}),
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