Skip to content
Draft
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
2 changes: 1 addition & 1 deletion src/integrations/default.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { breadcrumbsIntegration, browserApiErrorsIntegration, browserSessionIntegration, globalHandlersIntegration } from '@sentry/browser';
import { type Integration,dedupeIntegration, functionToStringIntegration, inboundFiltersIntegration, linkedErrorsIntegration } from '@sentry/core';
import { type Integration, dedupeIntegration, functionToStringIntegration, inboundFiltersIntegration, linkedErrorsIntegration } from '@sentry/core';
import type { CapacitorOptions } from '../options';
import { deviceContextIntegration } from './devicecontext';
import { eventOriginIntegration } from './eventorigin';
Expand Down
71 changes: 35 additions & 36 deletions src/sdk.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import type { BrowserOptions } from '@sentry/browser';
import { init as browserInit } from '@sentry/browser';
import type { Integration } from '@sentry/core';
import { debug, getClient, getGlobalScope, getIntegrationsToSetup, getIsolationScope } from '@sentry/core';
import { debug, getClient, getGlobalScope, getIsolationScope } from '@sentry/core';
import { sdkInit } from './client';
import { getDefaultIntegrations } from './integrations/default';
import type { CapacitorClientOptions, CapacitorOptions } from './options';
Expand All @@ -19,48 +18,38 @@ import { NATIVE } from './wrapper';
*/
export function init(
passedOptions: CapacitorOptions,
originalInit: (passedOptions:BrowserOptions) => void = browserInit,
originalInit: (passedOptions: BrowserOptions) => void = browserInit,
): void {

const finalOptions = {
/**
* Shared options are the options that are shared between the browser and native SDKs.
*/
const sharedOptions = {
enableAutoSessionTracking: true,
enableWatchdogTerminationTracking: true,
enableCaptureFailedRequests: false,
...passedOptions,
};
finalOptions.siblingOptions && delete finalOptions.siblingOptions;
sharedOptions.siblingOptions && delete sharedOptions.siblingOptions;

if (finalOptions.enabled === false || NATIVE.platform === 'web') {
finalOptions.enableNative = false;
finalOptions.enableNativeNagger = false;
if (sharedOptions.enabled === false || NATIVE.platform === 'web') {
sharedOptions.enableNative = false;
sharedOptions.enableNativeNagger = false;
} else {
// keep the original value if user defined it.
finalOptions.enableNativeNagger ??= true;
finalOptions.enableNative ??= true;
sharedOptions.enableNativeNagger ??= true;
sharedOptions.enableNative ??= true;
}
// const capacitorHub = new Hub(undefined, new CapacitorScope());
// makeMain(capacitorHub);
const defaultIntegrations: false | Integration[] =
passedOptions.defaultIntegrations === undefined
? getDefaultIntegrations(finalOptions)
: passedOptions.defaultIntegrations;

finalOptions.integrations = getIntegrationsToSetup({
integrations: safeFactory(passedOptions.integrations, {
loggerMessage: 'The integrations threw an error',
}),
defaultIntegrations,
});

if (
finalOptions.enableNative &&
!passedOptions.transport &&
sharedOptions.enableNative &&
!passedOptions.transport &&
NATIVE.platform !== 'web'
) {
finalOptions.transport = passedOptions.transport || makeNativeTransport;
sharedOptions.transport = passedOptions.transport || makeNativeTransport;

finalOptions.transportOptions = {
...(passedOptions.transportOptions ?? {}),
sharedOptions.transportOptions = {
...(passedOptions.transportOptions ?? {}),
bufferSize: DEFAULT_BUFFER_SIZE,
};
}
Expand All @@ -69,25 +58,35 @@ finalOptions.transport = passedOptions.transport || makeNativeTransport;
useEncodePolyfill();
}

if (finalOptions.enableNative) {
if (sharedOptions.enableNative) {
enableSyncToNative(getGlobalScope());
enableSyncToNative(getIsolationScope());
}

/**
* Browser options are the options that are only used by the browser SDK.
*/
const browserOptions = {
...passedOptions.siblingOptions?.vueOptions,
...passedOptions.siblingOptions?.nuxtClientOptions,
...finalOptions,
autoSessionTracking:
NATIVE.platform === 'web' && finalOptions.enableAutoSessionTracking,
enableMetrics: finalOptions._experiments?.enableMetrics,
beforeSendMetric: finalOptions._experiments?.beforeSendMetric,
...sharedOptions,
integrations: safeFactory(passedOptions.integrations, { loggerMessage: 'The integrations threw an error' }),
enableMetrics: sharedOptions._experiments?.enableMetrics,
beforeSendMetric: sharedOptions._experiments?.beforeSendMetric,
} as BrowserOptions;


browserOptions.defaultIntegrations = passedOptions.defaultIntegrations === undefined
? getDefaultIntegrations(sharedOptions)
: passedOptions.defaultIntegrations;

/**
* Mobile options are the options that are only used by the native SDK.
*/
const mobileOptions = {
...finalOptions,
...sharedOptions,
enableAutoSessionTracking:
NATIVE.platform !== 'web' && finalOptions.enableAutoSessionTracking,
NATIVE.platform !== 'web' && sharedOptions.enableAutoSessionTracking,
} as CapacitorClientOptions;

sdkInit(browserOptions, mobileOptions, originalInit, passedOptions.transport);
Expand Down
131 changes: 88 additions & 43 deletions test/sdk.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { type EventHint, type Exception, type StackFrame } from '@sentry/browser';
import type { Client, Event } from '@sentry/core';
import type { CapacitorOptions } from '../src';
import type { Client, Event, Integration } from '@sentry/core';
import { init } from '../src/sdk';
import { NATIVE } from '../src/wrapper';

Expand Down Expand Up @@ -95,51 +94,87 @@ describe('SDK Init', () => {

test('Does not Enable Native SDK if enabled is false', async () => {
NATIVE.platform = 'ios';
const mockOriginalInit = jest.fn();

init({
enabled: false
}, (capacitorOptions: CapacitorOptions) => {
expect(capacitorOptions.enableNative).toBe(false);
expect(capacitorOptions.enableNativeNagger).toBe(false);
}, mockOriginalInit);

// Wait for async operations
return new Promise<void>((resolve) => {
setTimeout(() => {
expect(NATIVE.initNativeSdk).toHaveBeenCalled();
const nativeOptions = (NATIVE.initNativeSdk as jest.Mock).mock.calls[0][0];
expect(nativeOptions.enableNative).toBe(false);
expect(nativeOptions.enableNativeNagger).toBe(false);
resolve();
}, 0);
});
});

test('Keep enableNative if set on mobile', async () => {
NATIVE.platform = 'ios';
const mockOriginalInit = jest.fn();

init({
enabled: true,
enableNative: false
}, (capacitorOptions: CapacitorOptions) => {
expect(capacitorOptions.enableNative).toBe(false);
expect(capacitorOptions.enableNativeNagger).toBe(true);
expect(capacitorOptions.enabled).toBe(true);
}, mockOriginalInit);

// Wait for async operations
return new Promise<void>((resolve) => {
setTimeout(() => {
expect(NATIVE.initNativeSdk).toHaveBeenCalled();
const nativeOptions = (NATIVE.initNativeSdk as jest.Mock).mock.calls[0][0];
expect(nativeOptions.enableNative).toBe(false);
expect(nativeOptions.enableNativeNagger).toBe(true);
expect(nativeOptions.enabled).toBe(true);
resolve();
}, 0);
});
});

test('Keep enableNativeNagger if set on mobile', async () => {
NATIVE.platform = 'ios';
const mockOriginalInit = jest.fn();

init({
enabled: true,
enableNative: false,
enableNativeNagger: false
}, (capacitorOptions: CapacitorOptions) => {
expect(capacitorOptions.enableNative).toBe(false);
expect(capacitorOptions.enableNativeNagger).toBe(false);
expect(capacitorOptions.enabled).toBe(true);
}, mockOriginalInit);

// Wait for async operations
return new Promise<void>((resolve) => {
setTimeout(() => {
expect(NATIVE.initNativeSdk).toHaveBeenCalled();
const nativeOptions = (NATIVE.initNativeSdk as jest.Mock).mock.calls[0][0];
expect(nativeOptions.enableNative).toBe(false);
expect(nativeOptions.enableNativeNagger).toBe(false);
expect(nativeOptions.enabled).toBe(true);
resolve();
}, 0);
});
});

test('WEB has enableNative false by default', async () => {
NATIVE.platform = 'web';
const mockOriginalInit = jest.fn();

init({
enabled: true,
}, (capacitorOptions: CapacitorOptions) => {
expect(capacitorOptions.enableNative).toBe(false);
expect(capacitorOptions.enableNativeNagger).toBe(false);
expect(capacitorOptions.enabled).toBe(true);
}, mockOriginalInit);

// Wait for async operations
return new Promise<void>((resolve) => {
setTimeout(() => {
expect(NATIVE.initNativeSdk).toHaveBeenCalled();
const nativeOptions = (NATIVE.initNativeSdk as jest.Mock).mock.calls[0][0];
expect(nativeOptions.enableNative).toBe(false);
expect(nativeOptions.enableNativeNagger).toBe(false);
expect(nativeOptions.enabled).toBe(true);
resolve();
}, 0);
});
});

Expand Down Expand Up @@ -167,39 +202,49 @@ describe('SDK Init', () => {
expect(browserOptions.beforeSendMetric).toBe(beforeSendMetric);

resolve();
}, 10);
}, 0);
});
});

test('RewriteFrames to be added by default', async () => {
NATIVE.platform = 'web';
init({ enabled: true }, async (capacitorOptions: CapacitorOptions) => {
const rewriteFramesIntegration =
Array.isArray(capacitorOptions.integrations) &&
capacitorOptions.integrations.find(
(integration) => integration.name === 'RewriteFrames');
const mockOriginalInit = jest.fn();

init({ enabled: true }, mockOriginalInit);

// Wait for async operations
return new Promise<void>((resolve) => {
setTimeout(async () => {
expect(mockOriginalInit).toHaveBeenCalled();
const browserOptions = mockOriginalInit.mock.calls[0][0];

// Capacitor specific frame.
const error = { values: [{ stacktrace: { frames: [{ filename: 'capacitor://localhost:8080/file.js' }] } }] };
const expectedError =
{
filename: 'app:///file.js',
in_app: true
}
const rewriteFramesIntegration =
Array.isArray(browserOptions.defaultIntegrations) &&
browserOptions.defaultIntegrations.find(
(integration: Integration) => integration.name === 'RewriteFrames');

expect(rewriteFramesIntegration).toBeDefined();
// Capacitor specific frame.
const error = { values: [{ stacktrace: { frames: [{ filename: 'capacitor://localhost:8080/file.js' }] } }] };
const expectedError =
{
filename: 'app:///file.js',
in_app: true
}

if (!rewriteFramesIntegration) {
throw new Error('rewriteFrames should be defined, but it is false');
}
expect(rewriteFramesIntegration).toBeDefined();

// @ts-expect-error
const event = await rewriteFramesIntegration.processEvent(({ exception: error }) as Event, {} as EventHint, {} as Client);
if (!rewriteFramesIntegration) {
throw new Error('rewriteFrames should be defined, but it is false');
}

const [firstException] = event?.exception?.values as Exception[];
const [firstFrame] = firstException?.stacktrace?.frames as StackFrame[];
const event = await rewriteFramesIntegration.processEvent(({ exception: error }) as Event, {} as EventHint, {} as Client);

expect(firstFrame).toStrictEqual(expectedError);
const [firstException] = event?.exception?.values as Exception[];
const [firstFrame] = firstException?.stacktrace?.frames as StackFrame[];

expect(firstFrame).toStrictEqual(expectedError);
resolve();
}, 0);
});
});

Expand Down Expand Up @@ -236,7 +281,7 @@ describe('SDK Init', () => {
expect(browserOptions.siblingOptions).toBeUndefined();

resolve();
}, 10);
}, 0);
});
});

Expand Down Expand Up @@ -269,7 +314,7 @@ describe('SDK Init', () => {
expect(browserOptions.siblingOptions).toBeUndefined();

resolve();
}, 10);
}, 0);
});
});

Expand Down Expand Up @@ -308,7 +353,7 @@ describe('SDK Init', () => {
expect(browserOptions.attachErrorHandler).toBe(false);

resolve();
}, 10);
}, 0);
});
});

Expand Down Expand Up @@ -339,7 +384,7 @@ describe('SDK Init', () => {
expect(nativeOptions.nuxtClientOptions).toBeUndefined();

resolve();
}, 10);
}, 0);
});
});
});
Expand Down
Loading