Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion packages/nextjs/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function init(options: BrowserOptions): Client | undefined {
}

const opts = {
environment: getVercelEnv(true) || process.env.NODE_ENV,
environment: options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv(true) || process.env.NODE_ENV,
defaultIntegrations: getDefaultIntegrations(options),
release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease,
...options,
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/src/edge/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export function init(options: VercelEdgeOptions = {}): void {

const opts: VercelEdgeOptions = {
defaultIntegrations: customDefaultIntegrations,
environment: options.environment || process.env.SENTRY_ENVIRONMENT,
release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease,
...options,
// Override runtime to 'cloudflare' when running on OpenNext/Cloudflare
Expand Down
2 changes: 1 addition & 1 deletion packages/nextjs/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function init(options: NodeOptions): NodeClient | undefined {
const cloudflareConfig = getCloudflareRuntimeConfig();

const opts: NodeOptions = {
environment: process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV,
environment: options.environment || process.env.SENTRY_ENVIRONMENT || getVercelEnv(false) || process.env.NODE_ENV,
release: process.env._sentryRelease || globalWithInjectedValues._sentryRelease,
defaultIntegrations: customDefaultIntegrations,
...options,
Expand Down
85 changes: 85 additions & 0 deletions packages/nextjs/test/clientSdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,89 @@ describe('Client init()', () => {
it('returns client from init', () => {
expect(init({})).not.toBeUndefined();
});

describe('environment option', () => {
const originalEnv = process.env.SENTRY_ENVIRONMENT;
const originalNodeEnv = process.env.NODE_ENV;

afterEach(() => {
if (originalEnv !== undefined) {
process.env.SENTRY_ENVIRONMENT = originalEnv;
} else {
delete process.env.SENTRY_ENVIRONMENT;
}
if (originalNodeEnv !== undefined) {
process.env.NODE_ENV = originalNodeEnv;
} else {
delete process.env.NODE_ENV;
}
});

it('uses environment from options when provided', () => {
delete process.env.SENTRY_ENVIRONMENT;
process.env.NODE_ENV = 'development';

init({
dsn: TEST_DSN,
environment: 'custom-env',
});

expect(reactInit).toHaveBeenCalledTimes(1);
const callArgs = reactInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('custom-env');
});

it('uses SENTRY_ENVIRONMENT env var when options.environment is not provided', () => {
process.env.SENTRY_ENVIRONMENT = 'env-from-variable';
process.env.NODE_ENV = 'development';

init({
dsn: TEST_DSN,
});

expect(reactInit).toHaveBeenCalledTimes(1);
const callArgs = reactInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('env-from-variable');
});

it('uses NODE_ENV as fallback when neither options.environment nor SENTRY_ENVIRONMENT is provided', () => {
delete process.env.SENTRY_ENVIRONMENT;
process.env.NODE_ENV = 'production';

init({
dsn: TEST_DSN,
});

expect(reactInit).toHaveBeenCalledTimes(1);
const callArgs = reactInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('production');
});

it('prioritizes options.environment over SENTRY_ENVIRONMENT env var', () => {
process.env.SENTRY_ENVIRONMENT = 'env-from-variable';
process.env.NODE_ENV = 'development';

init({
dsn: TEST_DSN,
environment: 'options-env',
});

expect(reactInit).toHaveBeenCalledTimes(1);
const callArgs = reactInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('options-env');
});

it('prioritizes SENTRY_ENVIRONMENT over NODE_ENV', () => {
process.env.SENTRY_ENVIRONMENT = 'sentry-env';
process.env.NODE_ENV = 'development';

init({
dsn: TEST_DSN,
});

expect(reactInit).toHaveBeenCalledTimes(1);
const callArgs = reactInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('sentry-env');
});
});
});
140 changes: 140 additions & 0 deletions packages/nextjs/test/edgeSdk.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import type { Integration } from '@sentry/core';
import { GLOBAL_OBJ } from '@sentry/core';
import * as SentryVercelEdge from '@sentry/vercel-edge';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { init } from '../src/edge';

// normally this is set as part of the build process, so mock it here
(GLOBAL_OBJ as typeof GLOBAL_OBJ & { _sentryRewriteFramesDistDir: string })._sentryRewriteFramesDistDir = '.next';

const vercelEdgeInit = vi.spyOn(SentryVercelEdge, 'init');

function findIntegrationByName(integrations: Integration[] = [], name: string): Integration | undefined {
return integrations.find(integration => integration.name === name);
}

describe('Edge init()', () => {
beforeEach(() => {
vi.clearAllMocks();
});

afterEach(() => {
vi.clearAllMocks();
});

it('inits the Vercel Edge SDK', () => {
expect(vercelEdgeInit).toHaveBeenCalledTimes(0);
init({});
expect(vercelEdgeInit).toHaveBeenCalledTimes(1);
expect(vercelEdgeInit).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/vercel-edge',
version: expect.any(String),
},
],
},
},
// 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.
defaultIntegrations: expect.any(Array),
}),
);
});

describe('integrations', () => {
// Options passed by `@sentry/nextjs`'s `init` to `@sentry/vercel-edge`'s `init` after modifying them
type ModifiedInitOptions = { integrations?: Integration[]; defaultIntegrations: Integration[] };

it('adds default integrations', () => {
init({});

const vercelEdgeInitOptions = vercelEdgeInit.mock.calls[0]?.[0] as ModifiedInitOptions;
const integrationNames = vercelEdgeInitOptions.defaultIntegrations.map(integration => integration.name);

expect(integrationNames).toContain('DistDirRewriteFrames');
});

it('supports passing unrelated integrations through options', () => {
init({ integrations: [SentryVercelEdge.dedupeIntegration()] });

const vercelEdgeInitOptions = vercelEdgeInit.mock.calls[0]?.[0] as ModifiedInitOptions;
const dedupeIntegration = findIntegrationByName(vercelEdgeInitOptions.integrations, 'Dedupe');

expect(dedupeIntegration).toBeDefined();
});
});

describe('environment option', () => {
const originalEnv = process.env.SENTRY_ENVIRONMENT;

afterEach(() => {
if (originalEnv !== undefined) {
process.env.SENTRY_ENVIRONMENT = originalEnv;
} else {
delete process.env.SENTRY_ENVIRONMENT;
}
});

it('uses environment from options when provided', () => {
delete process.env.SENTRY_ENVIRONMENT;

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
environment: 'custom-env',
});

expect(vercelEdgeInit).toHaveBeenCalledTimes(1);
const callArgs = vercelEdgeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('custom-env');
});

it('uses SENTRY_ENVIRONMENT env var when options.environment is not provided', () => {
process.env.SENTRY_ENVIRONMENT = 'env-from-variable';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});

expect(vercelEdgeInit).toHaveBeenCalledTimes(1);
const callArgs = vercelEdgeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('env-from-variable');
});

it('uses fallback environment when neither options.environment nor SENTRY_ENVIRONMENT is provided', () => {
delete process.env.SENTRY_ENVIRONMENT;

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});

expect(vercelEdgeInit).toHaveBeenCalledTimes(1);
const callArgs = vercelEdgeInit.mock.calls[0]?.[0];
// In test environment, it should be undefined since we only set it explicitly when SENTRY_ENVIRONMENT is present
// Note: The underlying vercel-edge SDK may set its own default
expect(callArgs?.environment).toBeDefined();
});

it('prioritizes options.environment over SENTRY_ENVIRONMENT env var', () => {
process.env.SENTRY_ENVIRONMENT = 'env-from-variable';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
environment: 'options-env',
});

expect(vercelEdgeInit).toHaveBeenCalledTimes(1);
const callArgs = vercelEdgeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('options-env');
});
});
});
93 changes: 93 additions & 0 deletions packages/nextjs/test/serverSdk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,97 @@ describe('Server init()', () => {
);
});
});

describe('environment option', () => {
const originalEnv = process.env.SENTRY_ENVIRONMENT;
const originalNodeEnv = process.env.NODE_ENV;

beforeEach(() => {
// Reset the global scope to allow re-initialization
SentryNode.getGlobalScope().clear();
SentryNode.getIsolationScope().clear();
SentryNode.getCurrentScope().clear();
SentryNode.getCurrentScope().setClient(undefined);
});

afterEach(() => {
if (originalEnv !== undefined) {
process.env.SENTRY_ENVIRONMENT = originalEnv;
} else {
delete process.env.SENTRY_ENVIRONMENT;
}
if (originalNodeEnv !== undefined) {
process.env.NODE_ENV = originalNodeEnv;
} else {
delete process.env.NODE_ENV;
}
});

it('uses environment from options when provided', () => {
delete process.env.SENTRY_ENVIRONMENT;
process.env.NODE_ENV = 'development';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
environment: 'custom-env',
});

expect(nodeInit).toHaveBeenCalledTimes(1);
const callArgs = nodeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('custom-env');
});

it('uses SENTRY_ENVIRONMENT env var when options.environment is not provided', () => {
process.env.SENTRY_ENVIRONMENT = 'env-from-variable';
process.env.NODE_ENV = 'development';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});

expect(nodeInit).toHaveBeenCalledTimes(1);
const callArgs = nodeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('env-from-variable');
});

it('uses NODE_ENV as fallback when neither options.environment nor SENTRY_ENVIRONMENT is provided', () => {
delete process.env.SENTRY_ENVIRONMENT;
process.env.NODE_ENV = 'production';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});

expect(nodeInit).toHaveBeenCalledTimes(1);
const callArgs = nodeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('production');
});

it('prioritizes options.environment over SENTRY_ENVIRONMENT env var', () => {
process.env.SENTRY_ENVIRONMENT = 'env-from-variable';
process.env.NODE_ENV = 'development';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
environment: 'options-env',
});

expect(nodeInit).toHaveBeenCalledTimes(1);
const callArgs = nodeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('options-env');
});

it('prioritizes SENTRY_ENVIRONMENT over NODE_ENV', () => {
process.env.SENTRY_ENVIRONMENT = 'sentry-env';
process.env.NODE_ENV = 'development';

init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
});

expect(nodeInit).toHaveBeenCalledTimes(1);
const callArgs = nodeInit.mock.calls[0]?.[0];
expect(callArgs?.environment).toBe('sentry-env');
});
});
});
Loading