): React.ReactNode => {
- if (!props.children) {
- return null;
- }
-
- if (typeof props.children === 'function') {
- return (props.children as () => React.ReactNode)();
- }
-
- // since Next.js >= 10 requires React ^16.6.0 we are allowed to return children like this here
- return props.children as React.ReactNode;
-};
-
-/**
- * A passthrough redux enhancer for the server that doesn't depend on anything from the `@sentry/react` package.
- */
-export function createReduxEnhancer() {
- return (createStore: unknown) => createStore;
-}
-
-/**
- * A passthrough error boundary wrapper for the server that doesn't depend on any react. Error boundaries don't catch
- * SSR errors so they should simply be a passthrough.
- */
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-export function withErrorBoundary>(
- WrappedComponent: React.ComponentType
,
-): React.FC
{
- return WrappedComponent as React.FC
;
-}
-
-/**
- * Just a passthrough since we're on the server and showing the report dialog on the server doesn't make any sense.
- */
-export function showReportDialog(): void {
- return;
-}
-
-/** Inits the Sentry NextJS SDK on node. */
-export function init(options: NodeOptions): NodeClient | undefined {
- if (isBuild()) {
- return;
- }
-
- const customDefaultIntegrations = getDefaultIntegrations(options);
-
- // Turn off Next.js' own fetch instrumentation
- // https://github.com/lforst/nextjs-fork/blob/1994fd186defda77ad971c36dc3163db263c993f/packages/next/src/server/lib/patch-fetch.ts#L245
- process.env.NEXT_OTEL_FETCH_DISABLED = '1';
-
- // This value is injected at build time, based on the output directory specified in the build config. Though a default
- // is set there, we set it here as well, just in case something has gone wrong with the injection.
- const distDirName = globalWithInjectedValues.__rewriteFramesDistDir__;
- if (distDirName) {
- customDefaultIntegrations.push(distDirRewriteFramesIntegration({ distDirName }));
- }
-
- const opts: NodeOptions = {
- environment: process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV,
- defaultIntegrations: customDefaultIntegrations,
- ...options,
- // Right now we only capture frontend sessions for Next.js
- autoSessionTracking: false,
- };
-
- if (DEBUG_BUILD && opts.debug) {
- logger.enable();
- }
-
- DEBUG_BUILD && logger.log('Initializing SDK...');
-
- if (sdkAlreadyInitialized()) {
- DEBUG_BUILD && logger.log('SDK already initialized');
- return;
- }
-
- applySdkMetadata(opts, 'nextjs', ['nextjs', 'node']);
-
- const client = nodeInit(opts);
- client?.on('beforeSampling', ({ spanAttributes, spanName, parentSampled, parentContext }, samplingDecision) => {
- // We allowlist the "BaseServer.handleRequest" span, since that one is responsible for App Router requests, which are actually useful for us.
- // HOWEVER, that span is not only responsible for App Router requests, which is why we additionally filter for certain transactions in an
- // event processor further below.
- if (spanAttributes['next.span_type'] === 'BaseServer.handleRequest') {
- return;
- }
-
- // If we encounter a span emitted by Next.js, we do not want to sample it
- // The reason for this is that the data quality of the spans varies, it is different per version of Next,
- // and we need to keep our manual instrumentation around for the edge runtime anyhow.
- // BUT we only do this if we don't have a parent span with a sampling decision yet (or if the parent is remote)
- if (
- (spanAttributes['next.span_type'] || NEXTJS_SPAN_NAME_PREFIXES.some(prefix => spanName.startsWith(prefix))) &&
- (parentSampled === undefined || parentContext?.isRemote)
- ) {
- samplingDecision.decision = false;
- }
-
- // There are situations where the Next.js Node.js server forwards requests for the Edge Runtime server (e.g. in
- // middleware) and this causes spans for Sentry ingest requests to be created. These are not exempt from our tracing
- // because we didn't get the chance to do `suppressTracing`, since this happens outside of userland.
- // We need to drop these spans.
- if (
- typeof spanAttributes[SEMATTRS_HTTP_TARGET] === 'string' &&
- spanAttributes[SEMATTRS_HTTP_TARGET].includes('sentry_key') &&
- spanAttributes[SEMATTRS_HTTP_TARGET].includes('sentry_client')
- ) {
- samplingDecision.decision = false;
- }
- });
-
- client?.on('spanStart', span => {
- const spanAttributes = spanToJSON(span).data;
-
- // What we do in this glorious piece of code, is hoist any information about parameterized routes from spans emitted
- // by Next.js via the `next.route` attribute, up to the transaction by setting the http.route attribute.
- if (spanAttributes?.['next.route']) {
- const rootSpan = getRootSpan(span);
- const rootSpanAttributes = spanToJSON(rootSpan).data;
-
- // Only hoist the http.route attribute if the transaction doesn't already have it
- if (rootSpanAttributes?.[SEMATTRS_HTTP_METHOD] && !rootSpanAttributes?.[SEMATTRS_HTTP_ROUTE]) {
- rootSpan.setAttribute(SEMATTRS_HTTP_ROUTE, spanAttributes['next.route']);
- }
- }
-
- // We want to skip span data inference for any spans generated by Next.js. Reason being that Next.js emits spans
- // with patterns (e.g. http.server spans) that will produce confusing data.
- if (spanAttributes?.['next.span_type'] !== undefined) {
- span.setAttribute('sentry.skip_span_data_inference', true);
- }
-
- // We want to rename these spans because they look like "GET /path/to/route" and we already emit spans that look
- // like this with our own http instrumentation.
- if (spanAttributes?.['next.span_type'] === 'BaseServer.handleRequest') {
- span.updateName('next server handler'); // This is all lowercase because the spans that Next.js emits by itself generally look like this.
- }
- });
-
- getGlobalScope().addEventProcessor(
- Object.assign(
- (event => {
- if (event.type === 'transaction') {
- // Filter out transactions for static assets
- // This regex matches the default path to the static assets (`_next/static`) and could potentially filter out too many transactions.
- // We match `/_next/static/` anywhere in the transaction name because its location may change with the basePath setting.
- if (event.transaction?.match(/^GET (\/.*)?\/_next\/static\//)) {
- return null;
- }
-
- // We only want to use our HTTP integration/instrumentation for app router requests, which are marked with the `sentry.rsc` attribute.
- if (
- (event.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN] === 'auto.http.otel.http' ||
- event.contexts?.trace?.data?.['next.span_type'] === 'BaseServer.handleRequest') &&
- event.contexts?.trace?.data?.['sentry.rsc'] !== true
- ) {
- return null;
- }
-
- // Filter out transactions for requests to the tunnel route
- if (
- globalWithInjectedValues.__sentryRewritesTunnelPath__ &&
- event.transaction === `POST ${globalWithInjectedValues.__sentryRewritesTunnelPath__}`
- ) {
- return null;
- }
-
- // Filter out requests to resolve source maps for stack frames in dev mode
- if (event.transaction?.match(/\/__nextjs_original-stack-frame/)) {
- return null;
- }
-
- // Filter out /404 transactions for pages-router which seem to be created excessively
- if (event.transaction === '/404') {
- return null;
- }
-
- return event;
- } else {
- return event;
- }
- }) satisfies EventProcessor,
- { id: 'NextLowQualityTransactionsFilter' },
- ),
- );
-
- getGlobalScope().addEventProcessor(
- Object.assign(
- ((event, hint) => {
- if (event.type !== undefined) {
- return event;
- }
-
- const originalException = hint.originalException;
-
- const isPostponeError =
- typeof originalException === 'object' &&
- originalException !== null &&
- '$$typeof' in originalException &&
- originalException.$$typeof === Symbol.for('react.postpone');
-
- if (isPostponeError) {
- // Postpone errors are used for partial-pre-rendering (PPR)
- return null;
- }
-
- // We don't want to capture suspense errors as they are simply used by React/Next.js for control flow
- const exceptionMessage = event.exception?.values?.[0]?.value;
- if (
- exceptionMessage?.includes('Suspense Exception: This is not a real error!') ||
- exceptionMessage?.includes('Suspense Exception: This is not a real error, and should not leak')
- ) {
- return null;
- }
-
- return event;
- }) satisfies EventProcessor,
- { id: 'DropReactControlFlowErrors' },
- ),
- );
-
- if (process.env.NODE_ENV === 'development') {
- getGlobalScope().addEventProcessor(devErrorSymbolicationEventProcessor);
- }
-
- DEBUG_BUILD && logger.log('SDK successfully initialized');
-
- return client;
-}
-
-function sdkAlreadyInitialized(): boolean {
- return !!getClient();
-}
-
-export * from '../common';
-
-export { wrapApiHandlerWithSentry } from '../common/wrapApiHandlerWithSentry';
diff --git a/packages/nextjs/src/server/rewriteFramesIntegration.ts b/packages/nextjs/src/server/rewriteFramesIntegration.ts
deleted file mode 100644
index 6438ccb0d922..000000000000
--- a/packages/nextjs/src/server/rewriteFramesIntegration.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import * as path from 'path';
-import { defineIntegration, rewriteFramesIntegration as originalRewriteFramesIntegration } from '@sentry/core';
-import type { IntegrationFn, StackFrame } from '@sentry/types';
-import { escapeStringForRegex } from '@sentry/utils';
-
-const globalWithInjectedValues = global as typeof global & {
- __rewriteFramesDistDir__?: string;
-};
-
-type StackFrameIteratee = (frame: StackFrame) => StackFrame;
-interface RewriteFramesOptions {
- root?: string;
- prefix?: string;
- iteratee?: StackFrameIteratee;
-}
-
-export const customRewriteFramesIntegration = ((options?: RewriteFramesOptions) => {
- // This value is injected at build time, based on the output directory specified in the build config. Though a default
- // is set there, we set it here as well, just in case something has gone wrong with the injection.
- const distDirName = globalWithInjectedValues.__rewriteFramesDistDir__;
-
- if (distDirName) {
- // nextjs always puts the build directory at the project root level, which is also where you run `next start` from, so
- // we can read in the project directory from the currently running process
- const distDirAbsPath = path.resolve(distDirName).replace(/(\/|\\)$/, ''); // We strip trailing slashes because "app:///_next" also doesn't have one
- // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor -- user input is escaped
- const SOURCEMAP_FILENAME_REGEX = new RegExp(escapeStringForRegex(distDirAbsPath));
-
- return originalRewriteFramesIntegration({
- iteratee: frame => {
- frame.filename = frame.filename?.replace(SOURCEMAP_FILENAME_REGEX, 'app:///_next');
- return frame;
- },
- ...options,
- });
- }
-
- // Do nothing if we can't find a distDirName
- return {
- name: 'RewriteFrames',
- };
-}) satisfies IntegrationFn;
-
-export const rewriteFramesIntegration = defineIntegration(customRewriteFramesIntegration);
diff --git a/packages/nextjs/test/clientSdk.test.ts b/packages/nextjs/test/clientSdk.test.ts
deleted file mode 100644
index ac159564410b..000000000000
--- a/packages/nextjs/test/clientSdk.test.ts
+++ /dev/null
@@ -1,162 +0,0 @@
-import { getGlobalScope, getIsolationScope } from '@sentry/core';
-import * as SentryReact from '@sentry/react';
-import { WINDOW, getClient, getCurrentScope } from '@sentry/react';
-import type { Integration } from '@sentry/types';
-import { logger } from '@sentry/utils';
-import { JSDOM } from 'jsdom';
-
-import { breadcrumbsIntegration, browserTracingIntegration, init } from '../src/client';
-
-const reactInit = jest.spyOn(SentryReact, 'init');
-const loggerLogSpy = jest.spyOn(logger, 'log');
-
-// We're setting up JSDom here because the Next.js routing instrumentations requires a few things to be present on pageload:
-// 1. Access to window.document API for `window.document.getElementById`
-// 2. Access to window.location API for `window.location.pathname`
-const dom = new JSDOM(undefined, { url: 'https://example.com/' });
-Object.defineProperty(global, 'document', { value: dom.window.document, writable: true });
-Object.defineProperty(global, 'location', { value: dom.window.document.location, writable: true });
-
-const originalGlobalDocument = WINDOW.document;
-const originalGlobalLocation = WINDOW.location;
-afterAll(() => {
- // Clean up JSDom
- Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument });
- Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation });
-});
-
-function findIntegrationByName(integrations: Integration[] = [], name: string): Integration | undefined {
- return integrations.find(integration => integration.name === name);
-}
-
-const TEST_DSN = 'https://public@dsn.ingest.sentry.io/1337';
-
-describe('Client init()', () => {
- afterEach(() => {
- jest.clearAllMocks();
-
- getGlobalScope().clear();
- getIsolationScope().clear();
- getCurrentScope().clear();
- getCurrentScope().setClient(undefined);
- });
-
- it('inits the React SDK', () => {
- expect(reactInit).toHaveBeenCalledTimes(0);
- init({});
- expect(reactInit).toHaveBeenCalledTimes(1);
- expect(reactInit).toHaveBeenCalledWith(
- expect.objectContaining({
- _metadata: {
- sdk: {
- name: 'sentry.javascript.nextjs',
- version: expect.any(String),
- packages: [
- {
- name: 'npm:@sentry/nextjs',
- version: expect.any(String),
- },
- {
- name: 'npm:@sentry/react',
- version: expect.any(String),
- },
- ],
- },
- },
- environment: 'test',
- defaultIntegrations: expect.arrayContaining([
- expect.objectContaining({
- name: 'NextjsClientStackFrameNormalization',
- }),
- ]),
- }),
- );
- });
-
- it('adds 404 transaction filter', () => {
- init({
- dsn: 'https://dogsarebadatkeepingsecrets@squirrelchasers.ingest.sentry.io/12312012',
- tracesSampleRate: 1.0,
- });
- const transportSend = jest.spyOn(getClient()!.getTransport()!, 'send');
-
- // Ensure we have no current span, so our next span is a transaction
- SentryReact.withActiveSpan(null, () => {
- SentryReact.startInactiveSpan({ name: '/404' })?.end();
- });
-
- expect(transportSend).not.toHaveBeenCalled();
- expect(loggerLogSpy).toHaveBeenCalledWith('An event processor returned `null`, will not send event.');
- });
-
- describe('integrations', () => {
- // Options passed by `@sentry/nextjs`'s `init` to `@sentry/react`'s `init` after modifying them
- type ModifiedInitOptionsIntegrationArray = { defaultIntegrations: Integration[]; integrations: Integration[] };
-
- it('supports passing unrelated integrations through options', () => {
- init({ integrations: [breadcrumbsIntegration({ console: false })] });
-
- const reactInitOptions = reactInit.mock.calls[0]![0] as ModifiedInitOptionsIntegrationArray;
- const installedBreadcrumbsIntegration = findIntegrationByName(reactInitOptions.integrations, 'Breadcrumbs');
-
- expect(installedBreadcrumbsIntegration).toBeDefined();
- });
-
- it('forces correct router instrumentation if user provides `browserTracingIntegration` in an array', () => {
- const providedBrowserTracingInstance = browserTracingIntegration();
-
- const client = init({
- dsn: TEST_DSN,
- tracesSampleRate: 1.0,
- integrations: [providedBrowserTracingInstance],
- });
-
- const integration = client?.getIntegrationByName('BrowserTracing');
- expect(integration).toBe(providedBrowserTracingInstance);
- });
-
- it('forces correct router instrumentation if user provides `BrowserTracing` in a function', () => {
- const providedBrowserTracingInstance = browserTracingIntegration();
-
- const client = init({
- dsn: TEST_DSN,
- tracesSampleRate: 1.0,
- integrations: defaults => [...defaults, providedBrowserTracingInstance],
- });
-
- const integration = client?.getIntegrationByName('BrowserTracing');
-
- expect(integration).toBe(providedBrowserTracingInstance);
- });
-
- describe('browserTracingIntegration()', () => {
- it('adds the browserTracingIntegration when `__SENTRY_TRACING__` is not set', () => {
- const client = init({
- dsn: TEST_DSN,
- });
-
- const browserTracingIntegration = client?.getIntegrationByName('BrowserTracing');
- expect(browserTracingIntegration).toBeDefined();
- });
-
- it("doesn't add a browserTracingIntegration if `__SENTRY_TRACING__` is set to false", () => {
- // @ts-expect-error Test setup for build-time flag
- globalThis.__SENTRY_TRACING__ = false;
-
- const client = init({
- dsn: TEST_DSN,
- });
-
- const browserTracingIntegration = client?.getIntegrationByName('BrowserTracing');
- expect(browserTracingIntegration).toBeUndefined();
-
- // @ts-expect-error Test setup for build-time flag
- delete globalThis.__SENTRY_TRACING__;
- });
- });
- });
-
- it('returns client from init', () => {
- expect(init({})).not.toBeUndefined();
- });
-});
diff --git a/packages/nextjs/test/config/fixtures.ts b/packages/nextjs/test/config/fixtures.ts
deleted file mode 100644
index a3c4feb0123b..000000000000
--- a/packages/nextjs/test/config/fixtures.ts
+++ /dev/null
@@ -1,112 +0,0 @@
-import type {
- BuildContext,
- EntryPropertyFunction,
- ExportedNextConfig,
- NextConfigObject,
- WebpackConfigObject,
-} from '../../src/config/types';
-
-export const SERVER_SDK_CONFIG_FILE = 'sentry.server.config.js';
-export const CLIENT_SDK_CONFIG_FILE = 'sentry.client.config.js';
-export const EDGE_SDK_CONFIG_FILE = 'sentry.edge.config.js';
-
-/** Mock next config object */
-export const userNextConfig: NextConfigObject = {
- publicRuntimeConfig: { location: 'dogpark', activities: ['fetch', 'chasing', 'digging'] },
- pageExtensions: ['jsx', 'js', 'tsx', 'ts', 'custom.jsx', 'custom.js', 'custom.tsx', 'custom.ts'],
- webpack: (incomingWebpackConfig: WebpackConfigObject, _options: BuildContext) => ({
- ...incomingWebpackConfig,
- mode: 'universal-sniffing',
- entry: async () =>
- Promise.resolve({
- ...(await (incomingWebpackConfig.entry as EntryPropertyFunction)()),
- simulatorBundle: './src/simulator/index.ts',
- }),
- }),
-};
-
-/** Mocks of the arguments passed to `withSentryConfig` */
-export const exportedNextConfig = userNextConfig;
-export const userSentryWebpackPluginConfig = { org: 'squirrelChasers', project: 'simulator' };
-process.env.SENTRY_AUTH_TOKEN = 'dogsarebadatkeepingsecrets';
-process.env.SENTRY_RELEASE = 'doGsaREgReaT';
-
-/** Mocks of the arguments passed to the result of `withSentryConfig` (when it's a function). */
-export const defaultRuntimePhase = 'ball-fetching';
-// `defaultConfig` is the defaults for all nextjs options (we don't use these at all in the tests, so for our purposes
-// here the values don't matter)
-export const defaultsObject = { defaultConfig: {} as NextConfigObject };
-
-/** mocks of the arguments passed to `nextConfig.webpack` */
-export const serverWebpackConfig: WebpackConfigObject = {
- entry: () =>
- Promise.resolve({
- 'pages/_error': 'private-next-pages/_error.js',
- 'pages/_app': 'private-next-pages/_app.js',
- 'pages/sniffTour': ['./node_modules/smellOVision/index.js', 'private-next-pages/sniffTour.js'],
- middleware: 'private-next-pages/middleware.js',
- 'pages/api/simulator/dogStats/[name]': { import: 'private-next-pages/api/simulator/dogStats/[name].js' },
- 'pages/simulator/leaderboard': {
- import: ['./node_modules/dogPoints/converter.js', 'private-next-pages/simulator/leaderboard.js'],
- },
- 'pages/api/tricks/[trickName]': {
- import: 'private-next-pages/api/tricks/[trickName].js',
- dependOn: 'treats',
- },
- treats: './node_modules/dogTreats/treatProvider.js',
- }),
- output: { filename: '[name].js', path: '/Users/Maisey/projects/squirrelChasingSimulator/.next' },
- target: 'node',
- context: '/Users/Maisey/projects/squirrelChasingSimulator',
- resolve: { alias: { 'private-next-pages': '/Users/Maisey/projects/squirrelChasingSimulator/pages' } },
-};
-export const clientWebpackConfig: WebpackConfigObject = {
- entry: () =>
- Promise.resolve({
- main: './src/index.ts',
- 'pages/_app': 'next-client-pages-loader?page=%2F_app',
- 'pages/_error': 'next-client-pages-loader?page=%2F_error',
- 'pages/sniffTour': ['./node_modules/smellOVision/index.js', 'private-next-pages/sniffTour.js'],
- 'pages/simulator/leaderboard': {
- import: ['./node_modules/dogPoints/converter.js', 'private-next-pages/simulator/leaderboard.js'],
- },
- }),
- output: { filename: 'static/chunks/[name].js', path: '/Users/Maisey/projects/squirrelChasingSimulator/.next' },
- target: 'web',
- context: '/Users/Maisey/projects/squirrelChasingSimulator',
-};
-
-/**
- * Return a mock build context, including the user's next config (which nextjs copies in in real life).
- *
- * @param buildTarget 'server' or 'client'
- * @param materializedNextConfig The user's next config
- * @param webpackVersion
- * @returns A mock build context for the given target
- */
-export function getBuildContext(
- buildTarget: 'server' | 'client' | 'edge',
- materializedNextConfig: ExportedNextConfig,
- webpackVersion: string = '5.4.15',
-): BuildContext {
- return {
- dev: false,
- buildId: 'sItStAyLiEdOwN',
- dir: '/Users/Maisey/projects/squirrelChasingSimulator',
- config: {
- // nextjs's default values
- target: 'server',
- distDir: '.next',
- ...materializedNextConfig,
- } as NextConfigObject,
- webpack: { version: webpackVersion, DefinePlugin: class {} as any },
- defaultLoaders: true,
- totalPages: 2,
- isServer: buildTarget === 'server' || buildTarget === 'edge',
- nextRuntime: ({ server: 'nodejs', client: undefined, edge: 'edge' } as const)[buildTarget],
- };
-}
-
-export const serverBuildContext = getBuildContext('server', exportedNextConfig);
-export const clientBuildContext = getBuildContext('client', exportedNextConfig);
-export const edgeBuildContext = getBuildContext('edge', exportedNextConfig);
diff --git a/packages/nextjs/test/config/loaders.test.ts b/packages/nextjs/test/config/loaders.test.ts
deleted file mode 100644
index c559ee643baf..000000000000
--- a/packages/nextjs/test/config/loaders.test.ts
+++ /dev/null
@@ -1,293 +0,0 @@
-// mock helper functions not tested directly in this file
-import './mocks';
-
-import * as fs from 'fs';
-
-import type { ModuleRuleUseProperty, WebpackModuleRule } from '../../src/config/types';
-import {
- clientBuildContext,
- clientWebpackConfig,
- exportedNextConfig,
- serverBuildContext,
- serverWebpackConfig,
-} from './fixtures';
-import { materializeFinalWebpackConfig } from './testUtils';
-
-const existsSyncSpy = jest.spyOn(fs, 'existsSync');
-const lstatSyncSpy = jest.spyOn(fs, 'lstatSync');
-
-type MatcherResult = { pass: boolean; message: () => string };
-
-expect.extend({
- stringEndingWith(received: string, expectedEnding: string): MatcherResult {
- const failsTest = !received.endsWith(expectedEnding);
- const generateErrorMessage = () =>
- failsTest
- ? // Regular error message for match failing
- `expected string ending with '${expectedEnding}', but got '${received}'`
- : // Error message for the match passing if someone has called it with `expect.not`
- `expected string not ending with '${expectedEnding}', but got '${received}'`;
-
- return {
- pass: !failsTest,
- message: generateErrorMessage,
- };
- },
-});
-
-declare global {
- // eslint-disable-next-line @typescript-eslint/no-namespace
- namespace jest {
- interface Expect {
- stringEndingWith: (expectedEnding: string) => MatcherResult;
- }
- }
-}
-
-function applyRuleToResource(rule: WebpackModuleRule, resourcePath: string): ModuleRuleUseProperty[] {
- const applications = [];
-
- let shouldApply: boolean = false;
- if (typeof rule.test === 'function') {
- shouldApply = rule.test(resourcePath);
- } else if (rule.test instanceof RegExp) {
- shouldApply = !!resourcePath.match(rule.test);
- } else if (rule.test) {
- shouldApply = resourcePath === rule.test;
- }
-
- if (shouldApply) {
- if (Array.isArray(rule.use)) {
- applications.push(...rule.use);
- } else if (rule.use) {
- applications.push(rule.use);
- }
- }
-
- return applications;
-}
-
-describe('webpack loaders', () => {
- describe('server loaders', () => {
- it('adds server `valueInjection` loader to server config', async () => {
- const finalWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: serverWebpackConfig,
- incomingWebpackBuildContext: serverBuildContext,
- });
-
- expect(finalWebpackConfig.module.rules).toContainEqual({
- test: expect.any(RegExp),
- use: [
- {
- loader: expect.stringEndingWith('valueInjectionLoader.js'),
- // We use `expect.objectContaining({})` rather than `expect.any(Object)` to match any plain object because
- // the latter will also match arrays, regexes, dates, sets, etc. - anything whose `typeof` value is
- // `'object'`.
- options: expect.objectContaining({ values: expect.objectContaining({}) }),
- },
- ],
- });
- });
-
- // For these tests we assume that we have an app and pages folder in {rootdir}/src
- it.each([
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/testPage.tsx',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/testPage.custom.tsx',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: './src/pages/testPage.tsx',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: './pages/testPage.tsx',
- expectedWrappingTargetKind: undefined,
- },
- {
- resourcePath: '../src/pages/testPage.tsx',
- expectedWrappingTargetKind: undefined,
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/nested/testPage.ts',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/nested/testPage.js',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/[nested]/[testPage].js',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/[...testPage].js',
- expectedWrappingTargetKind: 'page',
- },
- // Regression test for https://github.com/getsentry/sentry-javascript/issues/7122
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/apidoc/[version].tsx',
- expectedWrappingTargetKind: 'page',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/middleware.js',
- expectedWrappingTargetKind: 'middleware',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/middleware.custom.js',
- expectedWrappingTargetKind: 'middleware',
- },
- {
- resourcePath: './src/middleware.js',
- expectedWrappingTargetKind: 'middleware',
- },
- {
- resourcePath: './middleware.js',
- expectedWrappingTargetKind: undefined,
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/middleware.ts',
- expectedWrappingTargetKind: 'middleware',
- },
- // Since we assume we have a pages file in src middleware will only be included in the build if it is also in src
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/middleware.tsx',
- expectedWrappingTargetKind: undefined,
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/api/testApiRoute.ts',
- expectedWrappingTargetKind: 'api-route',
- },
- {
- resourcePath: './src/pages/api/testApiRoute.ts',
- expectedWrappingTargetKind: 'api-route',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/api/nested/testApiRoute.js',
- expectedWrappingTargetKind: 'api-route',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/pages/api/nested/testApiRoute.custom.js',
- expectedWrappingTargetKind: 'api-route',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/route.ts',
- expectedWrappingTargetKind: 'route-handler',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/route.custom.ts',
- expectedWrappingTargetKind: 'route-handler',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/page.js',
- expectedWrappingTargetKind: 'server-component',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/page.custom.js',
- expectedWrappingTargetKind: 'server-component',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.js',
- expectedWrappingTargetKind: 'server-component',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/nested/page.ts',
- expectedWrappingTargetKind: 'server-component',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/page.tsx',
- expectedWrappingTargetKind: 'server-component',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/(group)/nested/loading.ts',
- expectedWrappingTargetKind: 'server-component',
- },
- {
- resourcePath: '/Users/Maisey/projects/squirrelChasingSimulator/src/app/layout.js',
- expectedWrappingTargetKind: 'server-component',
- },
- ])(
- 'should apply the right wrappingTargetKind with wrapping loader ($resourcePath)',
- async ({ resourcePath, expectedWrappingTargetKind }) => {
- // We assume that we have an app and pages folder in {rootdir}/src
- existsSyncSpy.mockImplementation(path => {
- if (
- path.toString().startsWith('/Users/Maisey/projects/squirrelChasingSimulator/app') ||
- path.toString().startsWith('/Users/Maisey/projects/squirrelChasingSimulator/pages')
- ) {
- return false;
- }
- return true;
- });
-
- // @ts-expect-error Too lazy to mock the entire thing
- lstatSyncSpy.mockImplementation(() => ({
- isDirectory: () => true,
- }));
-
- const finalWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: serverWebpackConfig,
- incomingWebpackBuildContext: serverBuildContext,
- });
-
- const loaderApplications: ModuleRuleUseProperty[] = [];
- finalWebpackConfig.module.rules.forEach(rule => {
- loaderApplications.push(...applyRuleToResource(rule, resourcePath));
- });
-
- if (expectedWrappingTargetKind) {
- expect(loaderApplications).toContainEqual(
- expect.objectContaining({
- loader: expect.stringMatching(/wrappingLoader\.js$/),
- options: expect.objectContaining({
- wrappingTargetKind: expectedWrappingTargetKind,
- }),
- }),
- );
- } else {
- expect(loaderApplications).not.toContainEqual(
- expect.objectContaining({
- loader: expect.stringMatching(/wrappingLoader\.js$/),
- }),
- );
- }
- },
- );
- });
-
- describe('client loaders', () => {
- it('adds `valueInjection` loader to client config', async () => {
- const finalWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: clientWebpackConfig,
- incomingWebpackBuildContext: clientBuildContext,
- });
-
- expect(finalWebpackConfig.module.rules).toContainEqual({
- test: /sentry\.client\.config\.(jsx?|tsx?)/,
- use: [
- {
- loader: expect.stringEndingWith('valueInjectionLoader.js'),
- // We use `expect.objectContaining({})` rather than `expect.any(Object)` to match any plain object because
- // the latter will also match arrays, regexes, dates, sets, etc. - anything whose `typeof` value is
- // `'object'`.
- options: expect.objectContaining({ values: expect.objectContaining({}) }),
- },
- ],
- });
- });
- });
-});
-
-describe('`distDir` value in default server-side `RewriteFrames` integration', () => {
- describe('`RewriteFrames` ends up with correct `distDir` value', () => {
- // TODO: this, along with any number of other parts of the build process, should be tested with an integration
- // test which actually runs webpack and inspects the resulting bundles (and that integration test should test
- // custom `distDir` values with and without a `.`, to make sure the regex escaping is working)
- });
-});
diff --git a/packages/nextjs/test/config/mocks.ts b/packages/nextjs/test/config/mocks.ts
deleted file mode 100644
index 5c27c743c9f9..000000000000
--- a/packages/nextjs/test/config/mocks.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-// TODO: This mocking is why we have to use `--runInBand` when we run tests, since there's only a single temp directory
-// created
-
-import * as fs from 'fs';
-import * as os from 'os';
-import * as path from 'path';
-
-import { CLIENT_SDK_CONFIG_FILE, EDGE_SDK_CONFIG_FILE, SERVER_SDK_CONFIG_FILE } from './fixtures';
-
-// We use `fs.existsSync()` in `getUserConfigFile()`. When we're not testing `getUserConfigFile()` specifically, all we
-// need is for it to give us any valid answer, so make it always find what it's looking for. Since this is a core node
-// built-in, though, which jest itself uses, otherwise let it do the normal thing. Storing the real version of the
-// function also lets us restore the original when we do want to test `getUserConfigFile()`.
-export const realExistsSync = jest.requireActual('fs').existsSync;
-export const mockExistsSync = (path: fs.PathLike): ReturnType => {
- if (
- (path as string).endsWith(SERVER_SDK_CONFIG_FILE) ||
- (path as string).endsWith(CLIENT_SDK_CONFIG_FILE) ||
- (path as string).endsWith(EDGE_SDK_CONFIG_FILE)
- ) {
- return true;
- }
-
- return realExistsSync(path);
-};
-export const exitsSync = jest.spyOn(fs, 'existsSync').mockImplementation(mockExistsSync);
-
-/** Mocking of temporary directory creation (so that we have a place to stick files (like `sentry.client.config.js`) in
- * order to test that we can find them) */
-
-// Make it so that all temporary folders, either created directly by tests or by the code they're testing, will go into
-// one spot that we know about, which we can then clean up when we're done
-const realTmpdir = jest.requireActual('os').tmpdir;
-
-// Including the random number ensures that even if multiple test files using these mocks are running at once, they have
-// separate temporary folders
-const TEMP_DIR_PATH = path.join(realTmpdir(), `sentry-nextjs-test-${Math.random()}`);
-
-jest.spyOn(os, 'tmpdir').mockReturnValue(TEMP_DIR_PATH);
-// In theory, we should always land in the `else` here, but this saves the cases where the prior run got interrupted and
-// the `afterAll` below didn't happen.
-if (fs.existsSync(TEMP_DIR_PATH)) {
- fs.rmSync(TEMP_DIR_PATH, { recursive: true, force: true });
-}
-
-fs.mkdirSync(TEMP_DIR_PATH);
-
-afterAll(() => {
- fs.rmSync(TEMP_DIR_PATH, { recursive: true, force: true });
-});
-
-// In order to know what to expect in the webpack config `entry` property, we need to know the path of the temporary
-// directory created when doing the file injection, so wrap the real `mkdtempSync` and store the resulting path where we
-// can access it
-export const mkdtempSyncSpy = jest.spyOn(fs, 'mkdtempSync');
-
-afterEach(() => {
- mkdtempSyncSpy.mockClear();
-});
-
-// TODO (v8): This shouldn't be necessary once `hideSourceMaps` gets a default value, even for the updated error message
-// eslint-disable-next-line @typescript-eslint/unbound-method
-const realConsoleWarn = global.console.warn;
-global.console.warn = (...args: unknown[]) => {
- // Suppress the warning message about the `hideSourceMaps` option. This is better than forcing a value for
- // `hideSourceMaps` because that would mean we couldn't test it easily and would muddy the waters of other tests. Note
- // that doing this here, as a side effect, only works because the tests which trigger this warning are the same tests
- // which need other mocks from this file.
- if (typeof args[0] === 'string' && args[0]?.includes('your original code may be visible in browser devtools')) {
- return;
- }
-
- return realConsoleWarn(...args);
-};
diff --git a/packages/nextjs/test/config/testUtils.ts b/packages/nextjs/test/config/testUtils.ts
deleted file mode 100644
index 1e93e3740152..000000000000
--- a/packages/nextjs/test/config/testUtils.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import type {
- BuildContext,
- EntryPropertyFunction,
- ExportedNextConfig,
- NextConfigObject,
- SentryBuildOptions,
- WebpackConfigObject,
- WebpackConfigObjectWithModuleRules,
-} from '../../src/config/types';
-import { constructWebpackConfigFunction } from '../../src/config/webpack';
-import { withSentryConfig } from '../../src/config/withSentryConfig';
-import { defaultRuntimePhase, defaultsObject } from './fixtures';
-
-/**
- * Derive the final values of all next config options, by first applying `withSentryConfig` and then, if it returns a
- * function, running that function.
- *
- * @param exportedNextConfig Next config options provided by the user
- * @param userSentryWebpackPluginConfig SentryWebpackPlugin options provided by the user
- *
- * @returns The config values next will receive directly from `withSentryConfig` or when it calls the function returned
- * by `withSentryConfig`
- */
-export function materializeFinalNextConfig(
- exportedNextConfig: ExportedNextConfig,
- runtimePhase?: string,
- sentryBuildOptions?: SentryBuildOptions,
-): NextConfigObject {
- const sentrifiedConfig = withSentryConfig(exportedNextConfig, sentryBuildOptions);
- let finalConfigValues = sentrifiedConfig;
-
- if (typeof sentrifiedConfig === 'function') {
- // for some reason TS won't recognize that `finalConfigValues` is now a NextConfigObject, which is why the cast
- // below is necessary
- finalConfigValues = sentrifiedConfig(runtimePhase ?? defaultRuntimePhase, defaultsObject) as NextConfigObject;
- }
-
- return finalConfigValues as NextConfigObject;
-}
-
-/**
- * Derive the final values of all webpack config options, by first applying `constructWebpackConfigFunction` and then
- * running the resulting function. Since the `entry` property of the resulting object is itself a function, also call
- * that.
- *
- * @param options An object including the following:
- * - `exportedNextConfig` Next config options provided by the user
- * - `userSentryWebpackPluginConfig` SentryWebpackPlugin options provided by the user
- * - `incomingWebpackConfig` The existing webpack config, passed to the function as `config`
- * - `incomingWebpackBuildContext` The existing webpack build context, passed to the function as `options`
- *
- * @returns The webpack config values next will use when it calls the function that `createFinalWebpackConfig` returns
- */
-export async function materializeFinalWebpackConfig(options: {
- exportedNextConfig: ExportedNextConfig;
- incomingWebpackConfig: WebpackConfigObject;
- incomingWebpackBuildContext: BuildContext;
- sentryBuildTimeOptions?: SentryBuildOptions;
-}): Promise {
- const { exportedNextConfig, incomingWebpackConfig, incomingWebpackBuildContext } = options;
-
- // if the user's next config is a function, run it so we have access to the values
- const materializedUserNextConfig =
- typeof exportedNextConfig === 'function'
- ? await exportedNextConfig('phase-production-build', defaultsObject)
- : exportedNextConfig;
-
- // get the webpack config function we'd normally pass back to next
- const webpackConfigFunction = constructWebpackConfigFunction(
- materializedUserNextConfig,
- options.sentryBuildTimeOptions,
- );
-
- // call it to get concrete values for comparison
- const finalWebpackConfigValue = webpackConfigFunction(incomingWebpackConfig, incomingWebpackBuildContext);
- const webpackEntryProperty = finalWebpackConfigValue.entry as EntryPropertyFunction;
- finalWebpackConfigValue.entry = await webpackEntryProperty();
-
- return finalWebpackConfigValue as WebpackConfigObjectWithModuleRules;
-}
diff --git a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts b/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts
deleted file mode 100644
index a9d8b812f8a9..000000000000
--- a/packages/nextjs/test/config/webpack/constructWebpackConfig.test.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-// mock helper functions not tested directly in this file
-import '../mocks';
-
-import {
- CLIENT_SDK_CONFIG_FILE,
- clientBuildContext,
- clientWebpackConfig,
- exportedNextConfig,
- serverBuildContext,
- serverWebpackConfig,
- userNextConfig,
-} from '../fixtures';
-import { materializeFinalNextConfig, materializeFinalWebpackConfig } from '../testUtils';
-
-describe('constructWebpackConfigFunction()', () => {
- it('includes expected properties', async () => {
- const finalWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: serverWebpackConfig,
- incomingWebpackBuildContext: serverBuildContext,
- });
-
- expect(finalWebpackConfig).toEqual(
- expect.objectContaining({
- devtool: 'source-map',
- entry: expect.any(Object), // `entry` is tested specifically elsewhere
- plugins: expect.arrayContaining([expect.objectContaining({ _name: 'sentry-webpack-plugin' })]),
- }),
- );
- });
-
- it('preserves unrelated webpack config options', async () => {
- const finalWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: serverWebpackConfig,
- incomingWebpackBuildContext: serverBuildContext,
- });
-
- // Run the user's webpack config function, so we can check the results against ours. Delete `entry` because we'll
- // test it separately, and besides, it's one that we *should* be overwriting.
- const materializedUserWebpackConfig = userNextConfig.webpack!(serverWebpackConfig, serverBuildContext);
- // @ts-expect-error `entry` may be required in real life, but we don't need it for our tests
- delete materializedUserWebpackConfig.entry;
-
- expect(finalWebpackConfig).toEqual(expect.objectContaining(materializedUserWebpackConfig));
- });
-
- it("doesn't set devtool if webpack plugin is disabled", () => {
- const finalNextConfig = materializeFinalNextConfig(
- {
- ...exportedNextConfig,
- webpack: () =>
- ({
- ...serverWebpackConfig,
- devtool: 'something-besides-source-map',
- }) as any,
- },
- undefined,
- {
- sourcemaps: {
- disable: true,
- },
- },
- );
-
- const finalWebpackConfig = finalNextConfig.webpack?.(serverWebpackConfig, serverBuildContext);
-
- expect(finalWebpackConfig?.devtool).not.toEqual('source-map');
- });
-
- it('allows for the use of `hidden-source-map` as `devtool` value for client-side builds', async () => {
- const finalClientWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig: exportedNextConfig,
- incomingWebpackConfig: clientWebpackConfig,
- incomingWebpackBuildContext: clientBuildContext,
- sentryBuildTimeOptions: { hideSourceMaps: true },
- });
-
- const finalServerWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig: exportedNextConfig,
- incomingWebpackConfig: serverWebpackConfig,
- incomingWebpackBuildContext: serverBuildContext,
- sentryBuildTimeOptions: { hideSourceMaps: true },
- });
-
- expect(finalClientWebpackConfig.devtool).toEqual('hidden-source-map');
- expect(finalServerWebpackConfig.devtool).toEqual('source-map');
- });
-
- describe('webpack `entry` property config', () => {
- const clientConfigFilePath = `./${CLIENT_SDK_CONFIG_FILE}`;
-
- it('injects user config file into `_app` in server bundle and in the client bundle', async () => {
- const finalClientWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: clientWebpackConfig,
- incomingWebpackBuildContext: clientBuildContext,
- });
-
- expect(finalClientWebpackConfig.entry).toEqual(
- expect.objectContaining({
- 'pages/_app': expect.arrayContaining([clientConfigFilePath]),
- }),
- );
- });
-
- it('does not inject anything into non-_app pages during client build', async () => {
- const finalWebpackConfig = await materializeFinalWebpackConfig({
- exportedNextConfig,
- incomingWebpackConfig: clientWebpackConfig,
- incomingWebpackBuildContext: clientBuildContext,
- });
-
- expect(finalWebpackConfig.entry).toEqual({
- main: './src/index.ts',
- // only _app has config file injected
- 'pages/_app': ['./sentry.client.config.js', 'next-client-pages-loader?page=%2F_app'],
- 'pages/_error': 'next-client-pages-loader?page=%2F_error',
- 'pages/sniffTour': ['./node_modules/smellOVision/index.js', 'private-next-pages/sniffTour.js'],
- 'pages/simulator/leaderboard': {
- import: ['./node_modules/dogPoints/converter.js', 'private-next-pages/simulator/leaderboard.js'],
- },
- simulatorBundle: './src/simulator/index.ts',
- });
- });
- });
-});
diff --git a/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts b/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts
deleted file mode 100644
index 177077d2b5c4..000000000000
--- a/packages/nextjs/test/config/webpack/webpackPluginOptions.test.ts
+++ /dev/null
@@ -1,194 +0,0 @@
-import type { BuildContext, NextConfigObject } from '../../../src/config/types';
-import { getWebpackPluginOptions } from '../../../src/config/webpackPluginOptions';
-
-function generateBuildContext(overrides: {
- dir?: string;
- isServer: boolean;
- nextjsConfig?: NextConfigObject;
-}): BuildContext {
- return {
- dev: false, // The plugin is not included in dev mode
- isServer: overrides.isServer,
- buildId: 'test-build-id',
- dir: overrides.dir ?? '/my/project/dir',
- config: overrides.nextjsConfig ?? {},
- totalPages: 2,
- defaultLoaders: true,
- webpack: {
- version: '4.0.0',
- DefinePlugin: {} as any,
- },
- };
-}
-
-describe('getWebpackPluginOptions()', () => {
- it('forwards relevant options', () => {
- const buildContext = generateBuildContext({ isServer: false });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, {
- authToken: 'my-auth-token',
- headers: { 'my-test-header': 'test' },
- org: 'my-org',
- project: 'my-project',
- telemetry: false,
- reactComponentAnnotation: {
- enabled: true,
- },
- silent: false,
- debug: true,
- sentryUrl: 'my-url',
- sourcemaps: {
- assets: ['my-asset'],
- ignore: ['my-ignore'],
- },
- release: {
- name: 'my-release',
- create: false,
- finalize: false,
- dist: 'my-dist',
- vcsRemote: 'my-origin',
- setCommits: {
- auto: true,
- },
- deploy: {
- env: 'my-env',
- },
- },
- });
-
- expect(generatedPluginOptions.authToken).toBe('my-auth-token');
- expect(generatedPluginOptions.debug).toBe(true);
- expect(generatedPluginOptions.headers).toStrictEqual({ 'my-test-header': 'test' });
- expect(generatedPluginOptions.org).toBe('my-org');
- expect(generatedPluginOptions.project).toBe('my-project');
- expect(generatedPluginOptions.reactComponentAnnotation?.enabled).toBe(true);
- expect(generatedPluginOptions.release?.create).toBe(false);
- expect(generatedPluginOptions.release?.deploy?.env).toBe('my-env');
- expect(generatedPluginOptions.release?.dist).toBe('my-dist');
- expect(generatedPluginOptions.release?.finalize).toBe(false);
- expect(generatedPluginOptions.release?.name).toBe('my-release');
- expect(generatedPluginOptions.release?.setCommits?.auto).toBe(true);
- expect(generatedPluginOptions.release?.vcsRemote).toBe('my-origin');
- expect(generatedPluginOptions.silent).toBe(false);
- expect(generatedPluginOptions.sourcemaps?.assets).toStrictEqual(['my-asset']);
- expect(generatedPluginOptions.sourcemaps?.ignore).toStrictEqual(['my-ignore']);
- expect(generatedPluginOptions.telemetry).toBe(false);
- expect(generatedPluginOptions.url).toBe('my-url');
-
- expect(generatedPluginOptions).toMatchObject({
- authToken: 'my-auth-token',
- debug: true,
- headers: {
- 'my-test-header': 'test',
- },
- org: 'my-org',
- project: 'my-project',
- reactComponentAnnotation: {
- enabled: true,
- },
- release: {
- create: false,
- deploy: {
- env: 'my-env',
- },
- dist: 'my-dist',
- finalize: false,
- inject: false,
- name: 'my-release',
- setCommits: {
- auto: true,
- },
- vcsRemote: 'my-origin',
- },
- silent: false,
- sourcemaps: {
- assets: ['my-asset'],
- ignore: ['my-ignore'],
- },
- telemetry: false,
- url: 'my-url',
- });
- });
-
- it('forwards bundleSizeOptimization options', () => {
- const buildContext = generateBuildContext({ isServer: false });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, {
- bundleSizeOptimizations: {
- excludeTracing: true,
- excludeReplayShadowDom: false,
- },
- });
-
- expect(generatedPluginOptions).toMatchObject({
- bundleSizeOptimizations: {
- excludeTracing: true,
- excludeReplayShadowDom: false,
- },
- });
- });
-
- it('returns the right `assets` and `ignore` values during the server build', () => {
- const buildContext = generateBuildContext({ isServer: true });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, {});
- expect(generatedPluginOptions.sourcemaps).toMatchObject({
- assets: ['/my/project/dir/.next/server/**', '/my/project/dir/.next/serverless/**'],
- ignore: [],
- });
- });
-
- it('returns the right `assets` and `ignore` values during the client build', () => {
- const buildContext = generateBuildContext({ isServer: false });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, {});
- expect(generatedPluginOptions.sourcemaps).toMatchObject({
- assets: ['/my/project/dir/.next/static/chunks/pages/**', '/my/project/dir/.next/static/chunks/app/**'],
- ignore: [
- '/my/project/dir/.next/static/chunks/framework-*',
- '/my/project/dir/.next/static/chunks/framework.*',
- '/my/project/dir/.next/static/chunks/main-*',
- '/my/project/dir/.next/static/chunks/polyfills-*',
- '/my/project/dir/.next/static/chunks/webpack-*',
- ],
- });
- });
-
- it('returns the right `assets` and `ignore` values during the client build with `widenClientFileUpload`', () => {
- const buildContext = generateBuildContext({ isServer: false });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, { widenClientFileUpload: true });
- expect(generatedPluginOptions.sourcemaps).toMatchObject({
- assets: ['/my/project/dir/.next/static/chunks/**'],
- ignore: [
- '/my/project/dir/.next/static/chunks/framework-*',
- '/my/project/dir/.next/static/chunks/framework.*',
- '/my/project/dir/.next/static/chunks/main-*',
- '/my/project/dir/.next/static/chunks/polyfills-*',
- '/my/project/dir/.next/static/chunks/webpack-*',
- ],
- });
- });
-
- it('sets `sourcemaps.assets` to an empty array when `sourcemaps.disable` is true', () => {
- const buildContext = generateBuildContext({ isServer: false });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, { sourcemaps: { disable: true } });
- expect(generatedPluginOptions.sourcemaps).toMatchObject({
- assets: [],
- });
- });
-
- it('passes posix paths to the plugin', () => {
- const buildContext = generateBuildContext({
- dir: 'C:\\my\\windows\\project\\dir',
- nextjsConfig: { distDir: '.dist\\v1' },
- isServer: false,
- });
- const generatedPluginOptions = getWebpackPluginOptions(buildContext, { widenClientFileUpload: true });
- expect(generatedPluginOptions.sourcemaps).toMatchObject({
- assets: ['C:/my/windows/project/dir/.dist/v1/static/chunks/**'],
- ignore: [
- 'C:/my/windows/project/dir/.dist/v1/static/chunks/framework-*',
- 'C:/my/windows/project/dir/.dist/v1/static/chunks/framework.*',
- 'C:/my/windows/project/dir/.dist/v1/static/chunks/main-*',
- 'C:/my/windows/project/dir/.dist/v1/static/chunks/polyfills-*',
- 'C:/my/windows/project/dir/.dist/v1/static/chunks/webpack-*',
- ],
- });
- });
-});
diff --git a/packages/nextjs/test/config/withSentry.test.ts b/packages/nextjs/test/config/withSentry.test.ts
deleted file mode 100644
index 30f34634d9fd..000000000000
--- a/packages/nextjs/test/config/withSentry.test.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import * as SentryCore from '@sentry/core';
-import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
-import type { NextApiRequest, NextApiResponse } from 'next';
-
-import type { AugmentedNextApiResponse, NextApiHandler } from '../../src/common/types';
-import { wrapApiHandlerWithSentry } from '../../src/server';
-
-const startSpanManualSpy = jest.spyOn(SentryCore, 'startSpanManual');
-
-describe('withSentry', () => {
- let req: NextApiRequest, res: NextApiResponse;
-
- const origHandlerNoError: NextApiHandler = async (_req, res) => {
- res.send('Good dog, Maisey!');
- };
-
- const wrappedHandlerNoError = wrapApiHandlerWithSentry(origHandlerNoError, '/my-parameterized-route');
-
- beforeEach(() => {
- req = { url: 'http://dogs.are.great' } as NextApiRequest;
- res = {
- send: function (this: AugmentedNextApiResponse) {
- this.end();
- },
- end: function (this: AugmentedNextApiResponse) {
- // eslint-disable-next-line deprecation/deprecation
- this.finished = true;
- // @ts-expect-error This is a mock
- this.writableEnded = true;
- },
- } as unknown as AugmentedNextApiResponse;
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- describe('tracing', () => {
- it('starts a transaction when tracing is enabled', async () => {
- await wrappedHandlerNoError(req, res);
- expect(startSpanManualSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- name: 'GET /my-parameterized-route',
- op: 'http.server',
- attributes: {
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.nextjs',
- },
- }),
- expect.any(Function),
- );
- });
- });
-});
diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts
deleted file mode 100644
index e457174f5fa9..000000000000
--- a/packages/nextjs/test/config/withSentryConfig.test.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { defaultRuntimePhase, defaultsObject, exportedNextConfig, userNextConfig } from './fixtures';
-import { materializeFinalNextConfig } from './testUtils';
-
-describe('withSentryConfig', () => {
- it('includes expected properties', () => {
- const finalConfig = materializeFinalNextConfig(exportedNextConfig);
-
- expect(finalConfig).toEqual(
- expect.objectContaining({
- webpack: expect.any(Function), // `webpack` is tested specifically elsewhere
- }),
- );
- });
-
- it('preserves unrelated next config options', () => {
- const finalConfig = materializeFinalNextConfig(exportedNextConfig);
-
- expect(finalConfig.publicRuntimeConfig).toEqual(userNextConfig.publicRuntimeConfig);
- });
-
- it("works when user's overall config is an object", () => {
- const finalConfig = materializeFinalNextConfig(exportedNextConfig);
-
- expect(finalConfig).toEqual(
- expect.objectContaining({
- ...userNextConfig,
- webpack: expect.any(Function), // `webpack` is tested specifically elsewhere
- }),
- );
- });
-
- it("works when user's overall config is a function", () => {
- const exportedNextConfigFunction = () => userNextConfig;
-
- const finalConfig = materializeFinalNextConfig(exportedNextConfigFunction);
-
- expect(finalConfig).toEqual(
- expect.objectContaining({
- ...exportedNextConfigFunction(),
- webpack: expect.any(Function), // `webpack` is tested specifically elsewhere
- }),
- );
- });
-
- it('correctly passes `phase` and `defaultConfig` through to functional `userNextConfig`', () => {
- const exportedNextConfigFunction = jest.fn().mockReturnValue(userNextConfig);
-
- materializeFinalNextConfig(exportedNextConfigFunction);
-
- expect(exportedNextConfigFunction).toHaveBeenCalledWith(defaultRuntimePhase, defaultsObject);
- });
-});
diff --git a/packages/nextjs/test/config/wrappers.test.ts b/packages/nextjs/test/config/wrappers.test.ts
deleted file mode 100644
index 284737f4335d..000000000000
--- a/packages/nextjs/test/config/wrappers.test.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import type { IncomingMessage, ServerResponse } from 'http';
-import * as SentryCore from '@sentry/core';
-import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
-
-import type { Client } from '@sentry/types';
-import { wrapGetInitialPropsWithSentry, wrapGetServerSidePropsWithSentry } from '../../src/common';
-
-const startSpanManualSpy = jest.spyOn(SentryCore, 'startSpanManual');
-
-describe('data-fetching function wrappers should create spans', () => {
- const route = '/tricks/[trickName]';
- let req: IncomingMessage;
- let res: ServerResponse;
-
- beforeEach(() => {
- req = { headers: {}, url: 'http://dogs.are.great/tricks/kangaroo' } as IncomingMessage;
- res = { end: jest.fn() } as unknown as ServerResponse;
-
- jest.spyOn(SentryCore, 'hasTracingEnabled').mockReturnValue(true);
- jest.spyOn(SentryCore, 'getClient').mockImplementation(() => {
- return {
- getOptions: () => ({}),
- getDsn: () => {},
- } as Client;
- });
- });
-
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- test('wrapGetServerSidePropsWithSentry', async () => {
- const origFunction = jest.fn(async () => ({ props: {} }));
-
- const wrappedOriginal = wrapGetServerSidePropsWithSentry(origFunction, route);
- await wrappedOriginal({ req, res } as any);
-
- expect(startSpanManualSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- name: 'getServerSideProps (/tricks/[trickName])',
- op: 'function.nextjs',
- attributes: {
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- },
- }),
- expect.any(Function),
- );
- });
-
- test('wrapGetInitialPropsWithSentry', async () => {
- const origFunction = jest.fn(async () => ({}));
-
- const wrappedOriginal = wrapGetInitialPropsWithSentry(origFunction);
- await wrappedOriginal({ req, res, pathname: route } as any);
-
- expect(startSpanManualSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- name: 'getInitialProps (/tricks/[trickName])',
- op: 'function.nextjs',
- attributes: {
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- },
- }),
- expect.any(Function),
- );
- });
-});
diff --git a/packages/nextjs/test/config/wrappingLoader.test.ts b/packages/nextjs/test/config/wrappingLoader.test.ts
deleted file mode 100644
index 4458a9ce16b6..000000000000
--- a/packages/nextjs/test/config/wrappingLoader.test.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import * as fs from 'fs';
-import * as path from 'path';
-
-const originalReadfileSync = fs.readFileSync;
-
-jest.spyOn(fs, 'readFileSync').mockImplementation((filePath, options) => {
- if (filePath.toString().endsWith('/config/templates/apiWrapperTemplate.js')) {
- return originalReadfileSync(
- path.join(__dirname, '../../build/cjs/config/templates/apiWrapperTemplate.js'),
- options,
- );
- }
-
- if (filePath.toString().endsWith('/config/templates/pageWrapperTemplate.js')) {
- return originalReadfileSync(
- path.join(__dirname, '../../build/cjs/config/templates/pageWrapperTemplate.js'),
- options,
- );
- }
-
- if (filePath.toString().endsWith('/config/templates/middlewareWrapperTemplate.js')) {
- return originalReadfileSync(
- path.join(__dirname, '../../build/cjs/config/templates/middlewareWrapperTemplate.js'),
- options,
- );
- }
-
- if (filePath.toString().endsWith('/config/templates/sentryInitWrapperTemplate.js')) {
- return originalReadfileSync(
- path.join(__dirname, '../../build/cjs/config/templates/sentryInitWrapperTemplate.js'),
- options,
- );
- }
-
- if (filePath.toString().endsWith('/config/templates/serverComponentWrapperTemplate.js')) {
- return originalReadfileSync(
- path.join(__dirname, '../../build/cjs/config/templates/serverComponentWrapperTemplate.js'),
- options,
- );
- }
-
- if (filePath.toString().endsWith('/config/templates/routeHandlerWrapperTemplate.js')) {
- return originalReadfileSync(
- path.join(__dirname, '../../build/cjs/config/templates/routeHandlerWrapperTemplate.js'),
- options,
- );
- }
-
- return originalReadfileSync(filePath, options);
-});
-
-import type { LoaderThis } from '../../src/config/loaders/types';
-import type { WrappingLoaderOptions } from '../../src/config/loaders/wrappingLoader';
-import wrappingLoader from '../../src/config/loaders/wrappingLoader';
-
-const DEFAULT_PAGE_EXTENSION_REGEX = ['tsx', 'ts', 'jsx', 'js'].join('|');
-
-const defaultLoaderThis = {
- addDependency: () => undefined,
- async: () => undefined,
- cacheable: () => undefined,
-};
-
-describe('wrappingLoader', () => {
- it('should correctly wrap API routes on unix', async () => {
- const callback = jest.fn();
-
- const userCode = `
- export default function handler(req, res) {
- res.json({ foo: "bar" });
- }
- `;
- const userCodeSourceMap = undefined;
-
- const loaderPromise = new Promise(resolve => {
- const loaderThis = {
- ...defaultLoaderThis,
- resourcePath: '/my/pages/my/route.ts',
- callback: callback.mockImplementation(() => {
- resolve();
- }),
- getOptions() {
- return {
- pagesDir: '/my/pages',
- appDir: '/my/app',
- pageExtensionRegex: DEFAULT_PAGE_EXTENSION_REGEX,
- excludeServerRoutes: [],
- wrappingTargetKind: 'api-route',
- vercelCronsConfig: undefined,
- nextjsRequestAsyncStorageModulePath: '/my/request-async-storage.js',
- };
- },
- } satisfies LoaderThis;
-
- wrappingLoader.call(loaderThis, userCode, userCodeSourceMap);
- });
-
- await loaderPromise;
-
- expect(callback).toHaveBeenCalledWith(null, expect.stringContaining("'/my/route'"), expect.anything());
- });
-});
diff --git a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts b/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
deleted file mode 100644
index 029ee9d97fce..000000000000
--- a/packages/nextjs/test/edge/edgeWrapperUtils.test.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import * as coreSdk from '@sentry/core';
-import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
-
-import { withEdgeWrapping } from '../../src/common/utils/edgeWrapperUtils';
-
-const origRequest = global.Request;
-const origResponse = global.Response;
-
-// @ts-expect-error Request does not exist on type Global
-global.Request = class Request {
- headers = {
- get() {
- return null;
- },
- };
-};
-
-// @ts-expect-error Response does not exist on type Global
-global.Response = class Request {};
-
-afterAll(() => {
- global.Request = origRequest;
- global.Response = origResponse;
-});
-
-beforeEach(() => {
- jest.clearAllMocks();
-});
-
-describe('withEdgeWrapping', () => {
- it('should return a function that calls the passed function', async () => {
- const origFunctionReturnValue = new Response();
- const origFunction = jest.fn(_req => origFunctionReturnValue);
-
- const wrappedFunction = withEdgeWrapping(origFunction, {
- spanDescription: 'some label',
- mechanismFunctionName: 'some name',
- spanOp: 'some op',
- });
-
- const returnValue = await wrappedFunction(new Request('https://sentry.io/'));
-
- expect(returnValue).toBe(origFunctionReturnValue);
- expect(origFunction).toHaveBeenCalledTimes(1);
- });
-
- it('should return a function that calls captureException on error', async () => {
- const captureExceptionSpy = jest.spyOn(coreSdk, 'captureException');
- const error = new Error();
- const origFunction = jest.fn(_req => {
- throw error;
- });
-
- const wrappedFunction = withEdgeWrapping(origFunction, {
- spanDescription: 'some label',
- mechanismFunctionName: 'some name',
- spanOp: 'some op',
- });
-
- await expect(wrappedFunction(new Request('https://sentry.io/'))).rejects.toBe(error);
- expect(captureExceptionSpy).toHaveBeenCalledTimes(1);
- });
-
- it('should return a function that calls trace', async () => {
- const startSpanSpy = jest.spyOn(coreSdk, 'startSpan');
-
- const request = new Request('https://sentry.io/');
- const origFunction = jest.fn(_req => new Response());
-
- const wrappedFunction = withEdgeWrapping(origFunction, {
- spanDescription: 'some label',
- mechanismFunctionName: 'some name',
- spanOp: 'some op',
- });
-
- await wrappedFunction(request);
-
- expect(startSpanSpy).toHaveBeenCalledTimes(1);
- expect(startSpanSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- attributes: {
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- [coreSdk.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
- },
- name: 'some label',
- op: 'some op',
- }),
- expect.any(Function),
- );
-
- expect(coreSdk.getIsolationScope().getScopeData().sdkProcessingMetadata).toEqual({
- request: { headers: {} },
- });
- });
-
- it("should return a function that doesn't crash when req isn't passed", async () => {
- const origFunctionReturnValue = new Response();
- const origFunction = jest.fn(() => origFunctionReturnValue);
-
- const wrappedFunction = withEdgeWrapping(origFunction, {
- spanDescription: 'some label',
- mechanismFunctionName: 'some name',
- spanOp: 'some op',
- });
-
- await expect(wrappedFunction()).resolves.toBe(origFunctionReturnValue);
- expect(origFunction).toHaveBeenCalledTimes(1);
- });
-});
diff --git a/packages/nextjs/test/edge/withSentryAPI.test.ts b/packages/nextjs/test/edge/withSentryAPI.test.ts
deleted file mode 100644
index 6e24eca21bfe..000000000000
--- a/packages/nextjs/test/edge/withSentryAPI.test.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import * as coreSdk from '@sentry/core';
-import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/core';
-
-import { wrapApiHandlerWithSentry } from '../../src/edge';
-
-const origRequest = global.Request;
-const origResponse = global.Response;
-
-// @ts-expect-error Request does not exist on type Global
-global.Request = class Request {
- public url: string;
-
- public headers = {
- get() {
- return null;
- },
- };
-
- public method = 'POST';
-
- public constructor(input: string) {
- this.url = input;
- }
-};
-
-// @ts-expect-error Response does not exist on type Global
-global.Response = class Response {};
-
-afterAll(() => {
- global.Request = origRequest;
- global.Response = origResponse;
-});
-
-const startSpanSpy = jest.spyOn(coreSdk, 'startSpan');
-
-afterEach(() => {
- jest.clearAllMocks();
-});
-
-describe('wrapApiHandlerWithSentry', () => {
- it('should return a function that calls trace', async () => {
- const request = new Request('https://sentry.io/');
- const origFunction = jest.fn(_req => new Response());
-
- const wrappedFunction = wrapApiHandlerWithSentry(origFunction, '/user/[userId]/post/[postId]');
-
- await wrappedFunction(request);
-
- expect(startSpanSpy).toHaveBeenCalledTimes(1);
- expect(startSpanSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- attributes: {
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
- },
- name: 'POST /user/[userId]/post/[postId]',
- op: 'http.server',
- }),
- expect.any(Function),
- );
- });
-
- it('should return a function that calls trace without throwing when no request is passed', async () => {
- const origFunction = jest.fn(() => new Response());
-
- const wrappedFunction = wrapApiHandlerWithSentry(origFunction, '/user/[userId]/post/[postId]');
-
- await wrappedFunction();
-
- expect(startSpanSpy).toHaveBeenCalledTimes(1);
- expect(startSpanSpy).toHaveBeenCalledWith(
- expect.objectContaining({
- attributes: {
- [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
- [coreSdk.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.withEdgeWrapping',
- },
- name: 'handler (/user/[userId]/post/[postId])',
- op: 'http.server',
- }),
- expect.any(Function),
- );
- });
-});
diff --git a/packages/nextjs/test/performance/appRouterInstrumentation.test.ts b/packages/nextjs/test/performance/appRouterInstrumentation.test.ts
deleted file mode 100644
index 16992a498f83..000000000000
--- a/packages/nextjs/test/performance/appRouterInstrumentation.test.ts
+++ /dev/null
@@ -1,174 +0,0 @@
-import { WINDOW } from '@sentry/react';
-import type { Client, HandlerDataFetch } from '@sentry/types';
-import * as sentryUtils from '@sentry/utils';
-import { JSDOM } from 'jsdom';
-
-import {
- appRouterInstrumentNavigation,
- appRouterInstrumentPageLoad,
-} from '../../src/client/routing/appRouterRoutingInstrumentation';
-
-const addFetchInstrumentationHandlerSpy = jest.spyOn(sentryUtils, 'addFetchInstrumentationHandler');
-
-function setUpPage(url: string) {
- const dom = new JSDOM('nothingness
', { url });
-
- // The Next.js routing instrumentations requires a few things to be present on pageload:
- // 1. Access to window.document API for `window.document.getElementById`
- // 2. Access to window.location API for `window.location.pathname`
- Object.defineProperty(WINDOW, 'document', { value: dom.window.document, writable: true });
- Object.defineProperty(WINDOW, 'location', { value: dom.window.document.location, writable: true });
-}
-
-describe('appRouterInstrumentPageLoad', () => {
- const originalGlobalDocument = WINDOW.document;
- const originalGlobalLocation = WINDOW.location;
-
- afterEach(() => {
- // Clean up JSDom
- Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument });
- Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation });
- });
-
- it('should create a pageload transactions with the current location name', () => {
- setUpPage('https://example.com/some/page?someParam=foobar');
-
- const emit = jest.fn();
- const client = {
- emit,
- } as unknown as Client;
-
- appRouterInstrumentPageLoad(client);
-
- expect(emit).toHaveBeenCalledTimes(1);
- expect(emit).toHaveBeenCalledWith(
- 'startPageLoadSpan',
- expect.objectContaining({
- name: '/some/page',
- attributes: {
- 'sentry.op': 'pageload',
- 'sentry.origin': 'auto.pageload.nextjs.app_router_instrumentation',
- 'sentry.source': 'url',
- },
- }),
- undefined,
- );
- });
-});
-
-describe('appRouterInstrumentNavigation', () => {
- const originalGlobalDocument = WINDOW.document;
- const originalGlobalLocation = WINDOW.location;
-
- afterEach(() => {
- // Clean up JSDom
- Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument });
- Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation });
- });
-
- it('should create a navigation transactions when a navigation RSC request is sent', () => {
- setUpPage('https://example.com/some/page?someParam=foobar');
- let fetchInstrumentationHandlerCallback: (arg: HandlerDataFetch) => void;
-
- addFetchInstrumentationHandlerSpy.mockImplementationOnce(callback => {
- fetchInstrumentationHandlerCallback = callback;
- });
-
- const emit = jest.fn();
- const client = {
- emit,
- } as unknown as Client;
-
- appRouterInstrumentNavigation(client);
-
- fetchInstrumentationHandlerCallback!({
- args: [
- new URL('https://example.com/some/server/component/page?_rsc=2rs8t'),
- {
- headers: {
- RSC: '1',
- },
- },
- ],
- fetchData: { method: 'GET', url: 'https://example.com/some/server/component/page?_rsc=2rs8t' },
- startTimestamp: 1337,
- });
-
- expect(emit).toHaveBeenCalledTimes(1);
- expect(emit).toHaveBeenCalledWith('startNavigationSpan', {
- name: '/some/server/component/page',
- attributes: {
- 'sentry.op': 'navigation',
- 'sentry.origin': 'auto.navigation.nextjs.app_router_instrumentation',
- 'sentry.source': 'url',
- },
- });
- });
-
- it.each([
- [
- 'no RSC header',
- {
- args: [
- new URL('https://example.com/some/server/component/page?_rsc=2rs8t'),
- {
- headers: {},
- },
- ],
- fetchData: { method: 'GET', url: 'https://example.com/some/server/component/page?_rsc=2rs8t' },
- startTimestamp: 1337,
- },
- ],
- [
- 'no GET request',
- {
- args: [
- new URL('https://example.com/some/server/component/page?_rsc=2rs8t'),
- {
- headers: {
- RSC: '1',
- },
- },
- ],
- fetchData: { method: 'POST', url: 'https://example.com/some/server/component/page?_rsc=2rs8t' },
- startTimestamp: 1337,
- },
- ],
- [
- 'prefetch request',
- {
- args: [
- new URL('https://example.com/some/server/component/page?_rsc=2rs8t'),
- {
- headers: {
- RSC: '1',
- 'Next-Router-Prefetch': '1',
- },
- },
- ],
- fetchData: { method: 'GET', url: 'https://example.com/some/server/component/page?_rsc=2rs8t' },
- startTimestamp: 1337,
- },
- ],
- ])(
- 'should not create navigation transactions for fetch requests that are not navigating RSC requests (%s)',
- (_, fetchCallbackData) => {
- setUpPage('https://example.com/some/page?someParam=foobar');
- let fetchInstrumentationHandlerCallback: (arg: HandlerDataFetch) => void;
-
- addFetchInstrumentationHandlerSpy.mockImplementationOnce(callback => {
- fetchInstrumentationHandlerCallback = callback;
- });
-
- const emit = jest.fn();
- const client = {
- emit,
- } as unknown as Client;
-
- appRouterInstrumentNavigation(client);
- fetchInstrumentationHandlerCallback!(fetchCallbackData);
-
- expect(emit).toHaveBeenCalledTimes(0);
- },
- );
-});
diff --git a/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts b/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts
deleted file mode 100644
index 947b3c9f3f69..000000000000
--- a/packages/nextjs/test/performance/pagesRouterInstrumentation.test.ts
+++ /dev/null
@@ -1,338 +0,0 @@
-import { WINDOW } from '@sentry/react';
-import type { Client } from '@sentry/types';
-import { JSDOM } from 'jsdom';
-import type { NEXT_DATA } from 'next/dist/shared/lib/utils';
-import Router from 'next/router';
-
-import {
- pagesRouterInstrumentNavigation,
- pagesRouterInstrumentPageLoad,
-} from '../../src/client/routing/pagesRouterRoutingInstrumentation';
-
-const globalObject = WINDOW as typeof WINDOW & {
- __BUILD_MANIFEST?: {
- sortedPages?: string[];
- };
-};
-
-const originalBuildManifest = globalObject.__BUILD_MANIFEST;
-const originalBuildManifestRoutes = globalObject.__BUILD_MANIFEST?.sortedPages;
-
-let eventHandlers: { [eventName: string]: Set<(...args: any[]) => void> } = {};
-
-jest.mock('next/router', () => {
- return {
- default: {
- events: {
- on(type: string, handler: (...args: any[]) => void) {
- if (!eventHandlers[type]) {
- eventHandlers[type] = new Set();
- }
-
- eventHandlers[type]!.add(handler);
- },
- off: jest.fn((type: string, handler: (...args: any[]) => void) => {
- if (eventHandlers[type]) {
- eventHandlers[type]!.delete(handler);
- }
- }),
- emit(type: string, ...eventArgs: any[]) {
- if (eventHandlers[type]) {
- eventHandlers[type]!.forEach(eventHandler => {
- eventHandler(...eventArgs);
- });
- }
- },
- },
- },
- };
-});
-
-describe('pagesRouterInstrumentPageLoad', () => {
- const originalGlobalDocument = WINDOW.document;
- const originalGlobalLocation = WINDOW.location;
-
- function setUpNextPage(pageProperties: {
- url: string;
- route: string;
- query?: any;
- props?: any;
- navigatableRoutes?: string[];
- hasNextData: boolean;
- }) {
- const nextDataContent: NEXT_DATA = {
- props: pageProperties.props,
- page: pageProperties.route,
- query: pageProperties.query,
- buildId: 'y76hvndNJBAithejdVGLW',
- isFallback: false,
- gssp: true,
- appGip: true,
- scriptLoader: [],
- };
-
- const dom = new JSDOM(
- // Just an example of what a __NEXT_DATA__ tag might look like
- pageProperties.hasNextData
- ? ``
- : 'No next data :(
',
- { url: pageProperties.url },
- );
-
- // The Next.js routing instrumentations requires a few things to be present on pageload:
- // 1. Access to window.document API for `window.document.getElementById`
- // 2. Access to window.location API for `window.location.pathname`
- Object.defineProperty(WINDOW, 'document', { value: dom.window.document, writable: true });
- Object.defineProperty(WINDOW, 'location', { value: dom.window.document.location, writable: true });
-
- // Define Next.js clientside build manifest with navigatable routes
- globalObject.__BUILD_MANIFEST = {
- ...globalObject.__BUILD_MANIFEST,
- sortedPages: pageProperties.navigatableRoutes as string[],
- };
- }
-
- afterEach(() => {
- // Clean up JSDom
- Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument });
- Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation });
-
- // Reset Next.js' __BUILD_MANIFEST
- globalObject.__BUILD_MANIFEST = originalBuildManifest;
- if (globalObject.__BUILD_MANIFEST) {
- globalObject.__BUILD_MANIFEST.sortedPages = originalBuildManifestRoutes as string[];
- }
-
- // Clear all event handlers
- eventHandlers = {};
-
- // Necessary to clear all Router.events.off() mock call numbers
- jest.clearAllMocks();
- });
-
- it.each([
- [
- 'https://example.com/lforst/posts/1337?q=42',
- '/[user]/posts/[id]',
- { user: 'lforst', id: '1337', q: '42' },
- {
- pageProps: {
- _sentryTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1',
- _sentryBaggage: 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv',
- },
- },
- true,
- {
- name: '/[user]/posts/[id]',
- attributes: {
- 'sentry.op': 'pageload',
- 'sentry.origin': 'auto.pageload.nextjs.pages_router_instrumentation',
- 'sentry.source': 'route',
- },
- },
- ],
- [
- 'https://example.com/some-page',
- '/some-page',
- {},
- {
- pageProps: {
- _sentryTraceData: 'c82b8554881b4d28ad977de04a4fb40a-a755953cd3394d5f-1',
- _sentryBaggage: 'other=vendor,foo=bar,third=party,last=item,sentry-release=2.1.0,sentry-environment=myEnv',
- },
- },
- true,
- {
- name: '/some-page',
- attributes: {
- 'sentry.op': 'pageload',
- 'sentry.origin': 'auto.pageload.nextjs.pages_router_instrumentation',
- 'sentry.source': 'route',
- },
- },
- ],
- [
- 'https://example.com/',
- '/',
- {},
- {},
- true,
- {
- name: '/',
- attributes: {
- 'sentry.op': 'pageload',
- 'sentry.origin': 'auto.pageload.nextjs.pages_router_instrumentation',
- 'sentry.source': 'route',
- },
- },
- ],
- [
- 'https://example.com/lforst/posts/1337?q=42',
- '/',
- {},
- {},
- false, // no __NEXT_DATA__ tag
- {
- name: '/lforst/posts/1337',
- attributes: {
- 'sentry.op': 'pageload',
- 'sentry.origin': 'auto.pageload.nextjs.pages_router_instrumentation',
- 'sentry.source': 'url',
- },
- },
- ],
- ])(
- 'creates a pageload transaction (#%#)',
- (url, route, query, props, hasNextData, expectedStartTransactionArgument) => {
- setUpNextPage({ url, route, query, props, hasNextData });
-
- const emit = jest.fn();
- const client = {
- emit,
- getOptions: () => ({}),
- } as unknown as Client;
-
- pagesRouterInstrumentPageLoad(client);
-
- const sentryTrace = (props as any).pageProps?._sentryTraceData;
- const baggage = (props as any).pageProps?._sentryBaggage;
-
- expect(emit).toHaveBeenCalledTimes(1);
- expect(emit).toHaveBeenCalledWith(
- 'startPageLoadSpan',
- expect.objectContaining(expectedStartTransactionArgument),
- {
- sentryTrace,
- baggage,
- },
- );
- },
- );
-});
-
-describe('pagesRouterInstrumentNavigation', () => {
- const originalGlobalDocument = WINDOW.document;
- const originalGlobalLocation = WINDOW.location;
-
- function setUpNextPage(pageProperties: {
- url: string;
- route: string;
- query?: any;
- props?: any;
- navigatableRoutes?: string[];
- hasNextData: boolean;
- }) {
- const nextDataContent: NEXT_DATA = {
- props: pageProperties.props,
- page: pageProperties.route,
- query: pageProperties.query,
- buildId: 'y76hvndNJBAithejdVGLW',
- isFallback: false,
- gssp: true,
- appGip: true,
- scriptLoader: [],
- };
-
- const dom = new JSDOM(
- // Just an example of what a __NEXT_DATA__ tag might look like
- pageProperties.hasNextData
- ? ``
- : 'No next data :(
',
- { url: pageProperties.url },
- );
-
- // The Next.js routing instrumentations requires a few things to be present on pageload:
- // 1. Access to window.document API for `window.document.getElementById`
- // 2. Access to window.location API for `window.location.pathname`
- Object.defineProperty(WINDOW, 'document', { value: dom.window.document, writable: true });
- Object.defineProperty(WINDOW, 'location', { value: dom.window.document.location, writable: true });
-
- // Define Next.js clientside build manifest with navigatable routes
- globalObject.__BUILD_MANIFEST = {
- ...globalObject.__BUILD_MANIFEST,
- sortedPages: pageProperties.navigatableRoutes as string[],
- };
- }
-
- afterEach(() => {
- // Clean up JSDom
- Object.defineProperty(WINDOW, 'document', { value: originalGlobalDocument });
- Object.defineProperty(WINDOW, 'location', { value: originalGlobalLocation });
-
- // Reset Next.js' __BUILD_MANIFEST
- globalObject.__BUILD_MANIFEST = originalBuildManifest;
- if (globalObject.__BUILD_MANIFEST) {
- globalObject.__BUILD_MANIFEST.sortedPages = originalBuildManifestRoutes as string[];
- }
-
- // Clear all event handlers
- eventHandlers = {};
-
- // Necessary to clear all Router.events.off() mock call numbers
- jest.clearAllMocks();
- });
-
- it.each([
- ['/news', '/news', 'route'],
- ['/news/', '/news', 'route'],
- ['/some-route-that-is-not-defined-12332', '/some-route-that-is-not-defined-12332', 'url'], // unknown route
- ['/some-route-that-is-not-defined-12332?q=42', '/some-route-that-is-not-defined-12332', 'url'], // unknown route w/ query param
- ['/posts/42', '/posts/[id]', 'route'],
- ['/posts/42/', '/posts/[id]', 'route'],
- ['/posts/42?someParam=1', '/posts/[id]', 'route'], // query params are ignored
- ['/posts/42/details', '/posts/[id]/details', 'route'],
- ['/users/1337/friends/closeby/good', '/users/[id]/friends/[...filters]', 'route'],
- ['/users/1337/friends', '/users/1337/friends', 'url'],
- ['/statistics/page-visits', '/statistics/[[...parameters]]', 'route'],
- ['/statistics', '/statistics/[[...parameters]]', 'route'],
- ['/a/b/c/d', '/[a]/b/[c]/[...d]', 'route'],
- ['/a/b/c/d/e', '/[a]/b/[c]/[...d]', 'route'],
- ['/a/b/c', '/a/b/c', 'url'],
- ['/e/f/g/h', '/e/[f]/[g]/[[...h]]', 'route'],
- ['/e/f/g/h/i', '/e/[f]/[g]/[[...h]]', 'route'],
- ['/e/f/g', '/e/[f]/[g]/[[...h]]', 'route'],
- ])(
- 'should create a parameterized transaction on route change (%s)',
- (targetLocation, expectedTransactionName, expectedTransactionSource) => {
- setUpNextPage({
- url: 'https://example.com/home',
- route: '/home',
- hasNextData: true,
- navigatableRoutes: [
- '/home',
- '/news',
- '/posts/[id]',
- '/posts/[id]/details',
- '/users/[id]/friends/[...filters]',
- '/statistics/[[...parameters]]',
- // just some complicated routes to see if we get the matching right
- '/[a]/b/[c]/[...d]',
- '/e/[f]/[g]/[[...h]]',
- ],
- });
-
- const emit = jest.fn();
- const client = {
- emit,
- getOptions: () => ({}),
- } as unknown as Client;
-
- pagesRouterInstrumentNavigation(client);
-
- Router.events.emit('routeChangeStart', targetLocation);
-
- expect(emit).toHaveBeenCalledTimes(1);
- expect(emit).toHaveBeenCalledWith(
- 'startNavigationSpan',
- expect.objectContaining({
- name: expectedTransactionName,
- attributes: {
- 'sentry.op': 'navigation',
- 'sentry.origin': 'auto.navigation.nextjs.pages_router_instrumentation',
- 'sentry.source': expectedTransactionSource,
- },
- }),
- );
- },
- );
-});
diff --git a/packages/nextjs/test/serverSdk.test.ts b/packages/nextjs/test/serverSdk.test.ts
deleted file mode 100644
index 27230874d457..000000000000
--- a/packages/nextjs/test/serverSdk.test.ts
+++ /dev/null
@@ -1,119 +0,0 @@
-import { getCurrentScope } from '@sentry/node';
-import * as SentryNode from '@sentry/node';
-import type { Integration } from '@sentry/types';
-import { GLOBAL_OBJ } from '@sentry/utils';
-
-import { init } from '../src/server';
-
-// normally this is set as part of the build process, so mock it here
-(GLOBAL_OBJ as typeof GLOBAL_OBJ & { __rewriteFramesDistDir__: string }).__rewriteFramesDistDir__ = '.next';
-
-const nodeInit = jest.spyOn(SentryNode, 'init');
-
-function findIntegrationByName(integrations: Integration[] = [], name: string): Integration | undefined {
- return integrations.find(integration => integration.name === name);
-}
-
-describe('Server init()', () => {
- afterEach(() => {
- jest.clearAllMocks();
-
- SentryNode.getGlobalScope().clear();
- SentryNode.getIsolationScope().clear();
- SentryNode.getCurrentScope().clear();
- SentryNode.getCurrentScope().setClient(undefined);
-
- delete process.env.VERCEL;
- });
-
- it('inits the Node SDK', () => {
- expect(nodeInit).toHaveBeenCalledTimes(0);
- init({});
- expect(nodeInit).toHaveBeenCalledTimes(1);
- expect(nodeInit).toHaveBeenLastCalledWith(
- expect.objectContaining({
- _metadata: {
- sdk: {
- name: 'sentry.javascript.nextjs',
- version: expect.any(String),
- packages: [
- {
- name: 'npm:@sentry/nextjs',
- version: expect.any(String),
- },
- {
- name: 'npm:@sentry/node',
- version: expect.any(String),
- },
- ],
- },
- },
- autoSessionTracking: false,
- environment: 'test',
-
- // Integrations are tested separately, and we can't be more specific here without depending on the order in
- // which integrations appear in the array, which we can't guarantee.
- //
- // TODO: If we upgrde to Jest 28+, we can follow Jest's example matcher and create an
- // `expect.ArrayContainingInAnyOrder`. See
- // https://github.com/facebook/jest/blob/main/examples/expect-extend/toBeWithinRange.ts.
- defaultIntegrations: expect.any(Array),
- }),
- );
- });
-
- it("doesn't reinitialize the node SDK if already initialized", () => {
- expect(nodeInit).toHaveBeenCalledTimes(0);
- init({});
- expect(nodeInit).toHaveBeenCalledTimes(1);
- init({});
- expect(nodeInit).toHaveBeenCalledTimes(1);
- });
-
- // TODO: test `vercel` tag when running on Vercel
- // Can't just add the test and set env variables, since the value in `index.server.ts`
- // is resolved when importing.
-
- it('does not apply `vercel` tag when not running on vercel', () => {
- const currentScope = getCurrentScope();
-
- expect(process.env.VERCEL).toBeUndefined();
-
- init({});
-
- // @ts-expect-error need access to protected _tags attribute
- expect(currentScope._tags.vercel).toBeUndefined();
- });
-
- describe('integrations', () => {
- // Options passed by `@sentry/nextjs`'s `init` to `@sentry/node`'s `init` after modifying them
- type ModifiedInitOptions = { integrations: Integration[]; defaultIntegrations: Integration[] };
-
- it('adds default integrations', () => {
- init({});
-
- const nodeInitOptions = nodeInit.mock.calls[0]?.[0] as ModifiedInitOptions;
- const integrationNames = nodeInitOptions.defaultIntegrations.map(integration => integration.name);
- const onUncaughtExceptionIntegration = findIntegrationByName(
- nodeInitOptions.defaultIntegrations,
- 'OnUncaughtException',
- );
-
- expect(integrationNames).toContain('DistDirRewriteFrames');
- expect(onUncaughtExceptionIntegration).toBeDefined();
- });
-
- it('supports passing unrelated integrations through options', () => {
- init({ integrations: [SentryNode.consoleIntegration()] });
-
- const nodeInitOptions = nodeInit.mock.calls[0]?.[0] as ModifiedInitOptions;
- const consoleIntegration = findIntegrationByName(nodeInitOptions.integrations, 'Console');
-
- expect(consoleIntegration).toBeDefined();
- });
- });
-
- it('returns client from init', () => {
- expect(init({})).not.toBeUndefined();
- });
-});
diff --git a/packages/nextjs/test/tsconfig.json b/packages/nextjs/test/tsconfig.json
deleted file mode 100644
index 5c0c9dfa01bb..000000000000
--- a/packages/nextjs/test/tsconfig.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "extends": "../tsconfig.test.json",
-
- "include": ["./**/*", "../playwright.config.ts"]
-}
diff --git a/packages/nextjs/test/types/.gitignore b/packages/nextjs/test/types/.gitignore
deleted file mode 100644
index 23d67fc10447..000000000000
--- a/packages/nextjs/test/types/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-node_modules/
-yarn.lock
diff --git a/packages/nextjs/test/types/next.config.ts b/packages/nextjs/test/types/next.config.ts
deleted file mode 100644
index 74ea16a946db..000000000000
--- a/packages/nextjs/test/types/next.config.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import type { NextConfig } from 'next';
-
-import { withSentryConfig } from '../../src/config/withSentryConfig';
-
-const config: NextConfig = {
- hideSourceMaps: true,
- webpack: config => ({
- ...config,
- module: {
- ...config.module,
- rules: [...config.module.rules],
- },
- }),
-};
-
-module.exports = withSentryConfig(config);
diff --git a/packages/nextjs/test/types/package.json b/packages/nextjs/test/types/package.json
deleted file mode 100644
index 86f74bfe060a..000000000000
--- a/packages/nextjs/test/types/package.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "description": "This is used to install the nextjs v12 so we can test against those types",
- "scripts": {
- "test": "ts-node test.ts"
- },
- "dependencies": {
- "next": "13.2.0"
- }
-}
diff --git a/packages/nextjs/test/types/test.ts b/packages/nextjs/test/types/test.ts
deleted file mode 100644
index d9b5f059958c..000000000000
--- a/packages/nextjs/test/types/test.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { execSync } from 'child_process';
-/* eslint-disable no-console */
-import { parseSemver } from '@sentry/utils';
-
-const NODE_VERSION = parseSemver(process.versions.node);
-
-if (NODE_VERSION.major && NODE_VERSION.major >= 12) {
- console.log('Installing next@v12...');
- execSync('yarn install', { stdio: 'inherit' });
- console.log('Testing some types...');
- execSync('tsc --noEmit --project tsconfig.json', { stdio: 'inherit' });
-}
diff --git a/packages/nextjs/test/types/tsconfig.json b/packages/nextjs/test/types/tsconfig.json
deleted file mode 100644
index adedc2fafa6c..000000000000
--- a/packages/nextjs/test/types/tsconfig.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "include": [
- "./**/*"
- ]
-}
diff --git a/packages/nextjs/test/utils/tunnelRoute.test.ts b/packages/nextjs/test/utils/tunnelRoute.test.ts
deleted file mode 100644
index 576898c061b2..000000000000
--- a/packages/nextjs/test/utils/tunnelRoute.test.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import type { BrowserOptions } from '@sentry/react';
-
-import { applyTunnelRouteOption } from '../../src/client/tunnelRoute';
-
-const globalWithInjectedValues = global as typeof global & {
- __sentryRewritesTunnelPath__?: string;
-};
-
-beforeEach(() => {
- globalWithInjectedValues.__sentryRewritesTunnelPath__ = undefined;
-});
-
-describe('applyTunnelRouteOption()', () => {
- it('Correctly applies `tunnelRoute` option when conditions are met', () => {
- globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
- const options: any = {
- dsn: 'https://11111111111111111111111111111111@o2222222.ingest.sentry.io/3333333',
- } as BrowserOptions;
-
- applyTunnelRouteOption(options);
-
- expect(options.tunnel).toBe('/my-error-monitoring-route?o=2222222&p=3333333');
- });
-
- it("Doesn't apply `tunnelRoute` when DSN is missing", () => {
- globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
- const options: any = {
- // no dsn
- } as BrowserOptions;
-
- applyTunnelRouteOption(options);
-
- expect(options.tunnel).toBeUndefined();
- });
-
- it("Doesn't apply `tunnelRoute` when DSN is invalid", () => {
- globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
- const options: any = {
- dsn: 'invalidDsn',
- } as BrowserOptions;
-
- applyTunnelRouteOption(options);
-
- expect(options.tunnel).toBeUndefined();
- });
-
- it("Doesn't apply `tunnelRoute` option when `tunnelRoute` option wasn't injected", () => {
- const options: any = {
- dsn: 'https://11111111111111111111111111111111@o2222222.ingest.sentry.io/3333333',
- } as BrowserOptions;
-
- applyTunnelRouteOption(options);
-
- expect(options.tunnel).toBeUndefined();
- });
-
- it("Doesn't `tunnelRoute` option when DSN is not a SaaS DSN", () => {
- globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
- const options: any = {
- dsn: 'https://11111111111111111111111111111111@example.com/3333333',
- } as BrowserOptions;
-
- applyTunnelRouteOption(options);
-
- expect(options.tunnel).toBeUndefined();
- });
-
- it('Correctly applies `tunnelRoute` option to region DSNs', () => {
- globalWithInjectedValues.__sentryRewritesTunnelPath__ = '/my-error-monitoring-route';
- const options: any = {
- dsn: 'https://11111111111111111111111111111111@o2222222.ingest.us.sentry.io/3333333',
- } as BrowserOptions;
-
- applyTunnelRouteOption(options);
-
- expect(options.tunnel).toBe('/my-error-monitoring-route?o=2222222&p=3333333&r=us');
- });
-});
diff --git a/packages/nextjs/tsconfig.json b/packages/nextjs/tsconfig.json
deleted file mode 100644
index bf45a09f2d71..000000000000
--- a/packages/nextjs/tsconfig.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "extends": "../../tsconfig.json",
-
- "include": ["src/**/*"],
-
- "compilerOptions": {
- // package-specific options
- }
-}
diff --git a/packages/nextjs/tsconfig.test.json b/packages/nextjs/tsconfig.test.json
deleted file mode 100644
index f72f7d93a39e..000000000000
--- a/packages/nextjs/tsconfig.test.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": "./tsconfig.json",
-
- "include": ["test/**/*"],
-
- "compilerOptions": {
- // should include all types from `./tsconfig.json` plus types for all test frameworks used
- "types": ["node", "jest"],
-
- // other package-specific, test-specific options
- "lib": ["DOM", "ESNext"]
- }
-}
diff --git a/packages/nextjs/tsconfig.types.json b/packages/nextjs/tsconfig.types.json
deleted file mode 100644
index 978b51b8e126..000000000000
--- a/packages/nextjs/tsconfig.types.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "extends": "./tsconfig.json",
-
- // Some of the templates for code we inject into a user's app include an import from `@sentry/nextjs`. This makes
- // creating types for these template files a circular exercise, which causes `tsc` to crash. Fortunately, since the
- // templates aren't consumed as modules (they're essentially just text files which happen to contain code), we don't
- // actually need to create types for them.
- "exclude": ["src/config/templates/*"],
-
- "compilerOptions": {
- "declaration": true,
- "declarationMap": true,
- "emitDeclarationOnly": true,
- "outDir": "build/types"
- }
-}