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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/nextjs/src/index.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { RewriteFrames } from '@sentry/integrations';
import { configureScope, init as nodeInit } from '@sentry/node';
import { configureScope, init as nodeInit, Integrations } from '@sentry/node';

import { instrumentServer } from './utils/instrumentServer';
import { MetadataBuilder } from './utils/metadataBuilder';
Expand Down Expand Up @@ -37,12 +37,20 @@ const defaultRewriteFramesIntegration = new RewriteFrames({
},
});

const defaultHttpTracingIntegration = new Integrations.Http({ tracing: true });

function addServerIntegrations(options: NextjsOptions): void {
if (options.integrations) {
options.integrations = addIntegration(defaultRewriteFramesIntegration, options.integrations);
} else {
options.integrations = [defaultRewriteFramesIntegration];
}

if (options.tracesSampleRate !== undefined || options.tracesSampler !== undefined) {
options.integrations = addIntegration(defaultHttpTracingIntegration, options.integrations, {
Http: { keyPath: '_tracing', value: true },
});
}
}

export { withSentryConfig } from './utils/config';
Expand Down
136 changes: 136 additions & 0 deletions packages/nextjs/test/index.server.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { RewriteFrames } from '@sentry/integrations';
import { Integrations } from '@sentry/node';
import { Integration } from '@sentry/types';

import { init, Scope } from '../src/index.server';
import { NextjsOptions } from '../src/utils/nextjsOptions';

const mockInit = jest.fn();
let configureScopeCallback: (scope: Scope) => void = () => undefined;

jest.mock('@sentry/node', () => {
const actual = jest.requireActual('@sentry/node');
return {
...actual,
init: (options: NextjsOptions) => {
mockInit(options);
},
configureScope: (callback: (scope: Scope) => void) => {
configureScopeCallback = callback;
},
};
});

describe('Server init()', () => {
afterEach(() => {
mockInit.mockClear();
configureScopeCallback = () => undefined;
});

it('inits the Node SDK', () => {
expect(mockInit).toHaveBeenCalledTimes(0);
init({});
expect(mockInit).toHaveBeenCalledTimes(1);
expect(mockInit).toHaveBeenLastCalledWith({
_metadata: {
sdk: {
name: 'sentry.javascript.nextjs',
version: expect.any(String),
packages: expect.any(Array),
},
},
autoSessionTracking: false,
environment: 'test',
integrations: [expect.any(RewriteFrames)],
});
});

it('sets runtime on scope', () => {
const mockScope = new Scope();
init({});
configureScopeCallback(mockScope);
// @ts-ignore need access to protected _tags attribute
expect(mockScope._tags).toEqual({ runtime: 'node' });
});

describe('integrations', () => {
it('adds RewriteFrames integration by default', () => {
init({});

const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(1);
const integrations = reactInitOptions.integrations as Integration[];
expect(integrations[0]).toEqual(expect.any(RewriteFrames));
});

it('adds Http integration by default if tracesSampleRate is set', () => {
init({ tracesSampleRate: 1.0 });

const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(2);
const integrations = reactInitOptions.integrations as Integration[];
expect(integrations[1]).toEqual(expect.any(Integrations.Http));
});

it('adds Http integration by default if tracesSampler is set', () => {
init({ tracesSampler: () => true });

const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(2);
const integrations = reactInitOptions.integrations as Integration[];
expect(integrations[1]).toEqual(expect.any(Integrations.Http));
});

it('adds Http integration with tracing true', () => {
init({ tracesSampleRate: 1.0 });
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(2);

const integrations = reactInitOptions.integrations as Integration[];
expect((integrations[1] as any)._tracing).toBe(true);
});

it('supports passing integration through options', () => {
init({ tracesSampleRate: 1.0, integrations: [new Integrations.Console()] });
const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(3);

const integrations = reactInitOptions.integrations as Integration[];
expect(integrations).toEqual([
expect.any(Integrations.Console),
expect.any(RewriteFrames),
expect.any(Integrations.Http),
]);
});

describe('custom Http integration', () => {
it('sets tracing to true if tracesSampleRate is set', () => {
init({
tracesSampleRate: 1.0,
integrations: [new Integrations.Http({ tracing: false })],
});

const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(2);
const integrations = reactInitOptions.integrations as Integration[];
expect(integrations[0] as InstanceType<typeof Integrations.Http>).toEqual(
expect.objectContaining({ _breadcrumbs: true, _tracing: true, name: 'Http' }),
);
});

it('sets tracing to true if tracesSampler is set', () => {
init({
tracesSampler: () => true,
integrations: [new Integrations.Http({ tracing: false })],
});

const reactInitOptions: NextjsOptions = mockInit.mock.calls[0][0];
expect(reactInitOptions.integrations).toHaveLength(2);
const integrations = reactInitOptions.integrations as Integration[];
expect(integrations[0] as InstanceType<typeof Integrations.Http>).toEqual(
expect.objectContaining({ _breadcrumbs: true, _tracing: true, name: 'Http' }),
);
});
});
});
});