diff --git a/packages/remix/test/integration/app/routes/capture-exception.tsx b/packages/remix/test/integration/app/routes/capture-exception.tsx new file mode 100644 index 000000000000..296d50af21f4 --- /dev/null +++ b/packages/remix/test/integration/app/routes/capture-exception.tsx @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/remix'; + +export default function ErrorBoundaryCapture() { + Sentry.captureException(new Error('Sentry Manually Captured Error')); + + return
; +} diff --git a/packages/remix/test/integration/app/routes/capture-message.tsx b/packages/remix/test/integration/app/routes/capture-message.tsx new file mode 100644 index 000000000000..459e25e1b4ee --- /dev/null +++ b/packages/remix/test/integration/app/routes/capture-message.tsx @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/remix'; + +export default function ErrorBoundaryCapture() { + Sentry.captureMessage('Sentry Manually Captured Message'); + + return
; +} diff --git a/packages/remix/test/integration/app/routes/error-boundary-capture/$id.tsx b/packages/remix/test/integration/app/routes/error-boundary-capture/$id.tsx new file mode 100644 index 000000000000..4e5330621191 --- /dev/null +++ b/packages/remix/test/integration/app/routes/error-boundary-capture/$id.tsx @@ -0,0 +1,13 @@ +import { useState } from 'react'; + +export default function ErrorBoundaryCapture() { + const [count, setCount] = useState(0); + + if (count > 0) { + throw new Error('Sentry React Component Error'); + } else { + setTimeout(() => setCount(count + 1), 0); + } + + return
{count}
; +} diff --git a/packages/remix/test/integration/app/routes/manual-tracing/$id.tsx b/packages/remix/test/integration/app/routes/manual-tracing/$id.tsx new file mode 100644 index 000000000000..75cf8574819a --- /dev/null +++ b/packages/remix/test/integration/app/routes/manual-tracing/$id.tsx @@ -0,0 +1,7 @@ +import * as Sentry from '@sentry/remix'; + +export default function ManualTracing() { + const transaction = Sentry.startTransaction({ name: 'test_transaction_1' }); + transaction.finish(); + return
; +} diff --git a/packages/remix/test/integration/test/client/capture-exception.test.ts b/packages/remix/test/integration/test/client/capture-exception.test.ts new file mode 100644 index 000000000000..8f2db9d8704b --- /dev/null +++ b/packages/remix/test/integration/test/client/capture-exception.test.ts @@ -0,0 +1,25 @@ +import { getMultipleSentryEnvelopeRequests } from './utils/helpers'; +import { test, expect } from '@playwright/test'; +import { Event } from '@sentry/types'; + +test('should report a manually captured error.', async ({ page }) => { + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url: '/capture-exception' }); + + const [errorEnvelope, pageloadEnvelope] = envelopes; + + expect(errorEnvelope.level).toBe('error'); + expect(errorEnvelope.tags?.transaction).toBe('/capture-exception'); + expect(errorEnvelope.exception?.values).toMatchObject([ + { + type: 'Error', + value: 'Sentry Manually Captured Error', + stacktrace: { frames: expect.any(Array) }, + mechanism: { type: 'generic', handled: true }, + }, + ]); + + expect(pageloadEnvelope.contexts?.trace.op).toBe('pageload'); + expect(pageloadEnvelope.tags?.['routing.instrumentation']).toBe('remix-router'); + expect(pageloadEnvelope.type).toBe('transaction'); + expect(pageloadEnvelope.transaction).toBe('routes/capture-exception'); +}); diff --git a/packages/remix/test/integration/test/client/capture-message.test.ts b/packages/remix/test/integration/test/client/capture-message.test.ts new file mode 100644 index 000000000000..8ae743f8419a --- /dev/null +++ b/packages/remix/test/integration/test/client/capture-message.test.ts @@ -0,0 +1,18 @@ +import { getMultipleSentryEnvelopeRequests } from './utils/helpers'; +import { test, expect } from '@playwright/test'; +import { Event } from '@sentry/types'; + +test('should report a manually captured message.', async ({ page }) => { + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { url: '/capture-message' }); + + const [messageEnvelope, pageloadEnvelope] = envelopes; + + expect(messageEnvelope.level).toBe('info'); + expect(messageEnvelope.tags?.transaction).toBe('/capture-message'); + expect(messageEnvelope.message).toBe('Sentry Manually Captured Message'); + + expect(pageloadEnvelope.contexts?.trace.op).toBe('pageload'); + expect(pageloadEnvelope.tags?.['routing.instrumentation']).toBe('remix-router'); + expect(pageloadEnvelope.type).toBe('transaction'); + expect(pageloadEnvelope.transaction).toBe('routes/capture-message'); +}); diff --git a/packages/remix/test/integration/test/client/errorboundary.test.ts b/packages/remix/test/integration/test/client/errorboundary.test.ts new file mode 100644 index 000000000000..6bf6314095fd --- /dev/null +++ b/packages/remix/test/integration/test/client/errorboundary.test.ts @@ -0,0 +1,32 @@ +import { getMultipleSentryEnvelopeRequests } from './utils/helpers'; +import { test, expect } from '@playwright/test'; +import { Event } from '@sentry/types'; + +test('should capture React component errors.', async ({ page }) => { + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { + url: '/error-boundary-capture/0', + }); + + const [pageloadEnvelope, errorEnvelope] = envelopes; + + expect(pageloadEnvelope.contexts?.trace.op).toBe('pageload'); + expect(pageloadEnvelope.tags?.['routing.instrumentation']).toBe('remix-router'); + expect(pageloadEnvelope.type).toBe('transaction'); + expect(pageloadEnvelope.transaction).toBe('routes/error-boundary-capture/$id'); + + expect(errorEnvelope.level).toBe('error'); + expect(errorEnvelope.sdk?.name).toBe('sentry.javascript.remix'); + expect(errorEnvelope.exception?.values).toMatchObject([ + { + type: 'React ErrorBoundary Error', + value: 'Sentry React Component Error', + stacktrace: { frames: expect.any(Array) }, + }, + { + type: 'Error', + value: 'Sentry React Component Error', + stacktrace: { frames: expect.any(Array) }, + mechanism: { type: 'generic', handled: true }, + }, + ]); +}); diff --git a/packages/remix/test/integration/test/client/manualtracing.test.ts b/packages/remix/test/integration/test/client/manualtracing.test.ts new file mode 100644 index 000000000000..90291cf2dacf --- /dev/null +++ b/packages/remix/test/integration/test/client/manualtracing.test.ts @@ -0,0 +1,21 @@ +import { getMultipleSentryEnvelopeRequests } from './utils/helpers'; +import { test, expect } from '@playwright/test'; +import { Event } from '@sentry/types'; + +test('should report a manually created / finished transaction.', async ({ page }) => { + const envelopes = await getMultipleSentryEnvelopeRequests(page, 2, { + url: '/manual-tracing/0', + }); + + const [manualTransactionEnvelope, pageloadEnvelope] = envelopes; + + expect(manualTransactionEnvelope.transaction).toBe('test_transaction_1'); + expect(manualTransactionEnvelope.sdk?.name).toBe('sentry.javascript.remix'); + expect(manualTransactionEnvelope.start_timestamp).toBeDefined(); + expect(manualTransactionEnvelope.timestamp).toBeDefined(); + + expect(pageloadEnvelope.contexts?.trace.op).toBe('pageload'); + expect(pageloadEnvelope.tags?.['routing.instrumentation']).toBe('remix-router'); + expect(pageloadEnvelope.type).toBe('transaction'); + expect(pageloadEnvelope.transaction).toBe('routes/manual-tracing/$id'); +}); diff --git a/packages/remix/test/integration/test/client/meta-tags.test.ts b/packages/remix/test/integration/test/client/meta-tags.test.ts new file mode 100644 index 000000000000..9ae66122f705 --- /dev/null +++ b/packages/remix/test/integration/test/client/meta-tags.test.ts @@ -0,0 +1,29 @@ +import { test, expect } from '@playwright/test'; + +test('should inject `sentry-trace` and `baggage` meta tags inside the root page.', async ({ page }) => { + await page.goto('/'); + + const sentryTraceTag = await page.$('meta[name="sentry-trace"]'); + const sentryTraceContent = await sentryTraceTag?.getAttribute('content'); + + expect(sentryTraceContent).toEqual(expect.any(String)); + + const sentryBaggageTag = await page.$('meta[name="baggage"]'); + const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content'); + + expect(sentryBaggageContent).toEqual(expect.any(String)); +}); + +test('should inject `sentry-trace` and `baggage` meta tags inside a parameterized route.', async ({ page }) => { + await page.goto('/loader-json-response/0'); + + const sentryTraceTag = await page.$('meta[name="sentry-trace"]'); + const sentryTraceContent = await sentryTraceTag?.getAttribute('content'); + + expect(sentryTraceContent).toEqual(expect.any(String)); + + const sentryBaggageTag = await page.$('meta[name="baggage"]'); + const sentryBaggageContent = await sentryBaggageTag?.getAttribute('content'); + + expect(sentryBaggageContent).toEqual(expect.any(String)); +});