diff --git a/packages/replay-internal/src/integration.ts b/packages/replay-internal/src/integration.ts index 527100272d05..2b1ef71287ae 100644 --- a/packages/replay-internal/src/integration.ts +++ b/packages/replay-internal/src/integration.ts @@ -1,5 +1,5 @@ -import { getClient, parseSampleRate } from '@sentry/core'; -import type { BrowserClientReplayOptions, Integration, IntegrationFn } from '@sentry/types'; +import { parseSampleRate } from '@sentry/core'; +import type { BrowserClientReplayOptions, Client, Integration, IntegrationFn } from '@sentry/types'; import { consoleSandbox, dropUndefinedKeys, isBrowser } from '@sentry/utils'; import { @@ -215,13 +215,13 @@ export class Replay implements Integration { /** * Setup and initialize replay container */ - public setupOnce(): void { - if (!isBrowser()) { + public afterAllSetup(client: Client): void { + if (!isBrowser() || this._replay) { return; } - this._setup(); - this._initialize(); + this._setup(client); + this._initialize(client); } /** @@ -292,24 +292,19 @@ export class Replay implements Integration { /** * Initializes replay. */ - protected _initialize(): void { + protected _initialize(client: Client): void { if (!this._replay) { return; } - // We have to run this in _initialize, because this runs in setTimeout - // So when this runs all integrations have been added - // Before this, we cannot access integrations on the client, - // so we need to mutate the options here - this._maybeLoadFromReplayCanvasIntegration(); - + this._maybeLoadFromReplayCanvasIntegration(client); this._replay.initializeSampling(); } /** Setup the integration. */ - private _setup(): void { + private _setup(client: Client): void { // Client is not available in constructor, so we need to wait until setupOnce - const finalOptions = loadReplayOptionsFromClient(this._initialOptions); + const finalOptions = loadReplayOptionsFromClient(this._initialOptions, client); this._replay = new ReplayContainer({ options: finalOptions, @@ -318,12 +313,11 @@ export class Replay implements Integration { } /** Get canvas options from ReplayCanvas integration, if it is also added. */ - private _maybeLoadFromReplayCanvasIntegration(): void { + private _maybeLoadFromReplayCanvasIntegration(client: Client): void { // To save bundle size, we skip checking for stuff here // and instead just try-catch everything - as generally this should all be defined /* eslint-disable @typescript-eslint/no-non-null-assertion */ try { - const client = getClient()!; const canvasIntegration = client.getIntegrationByName('ReplayCanvas') as Integration & { getOptions(): ReplayCanvasIntegrationOptions; }; @@ -340,9 +334,8 @@ export class Replay implements Integration { } /** Parse Replay-related options from SDK options */ -function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions): ReplayPluginOptions { - const client = getClient(); - const opt = client && (client.getOptions() as BrowserClientReplayOptions); +function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions, client: Client): ReplayPluginOptions { + const opt = client.getOptions() as BrowserClientReplayOptions; const finalOptions: ReplayPluginOptions = { sessionSampleRate: 0, @@ -350,14 +343,6 @@ function loadReplayOptionsFromClient(initialOptions: InitialReplayPluginOptions) ...dropUndefinedKeys(initialOptions), }; - if (!opt) { - consoleSandbox(() => { - // eslint-disable-next-line no-console - console.warn('SDK client is not available.'); - }); - return finalOptions; - } - const replaysSessionSampleRate = parseSampleRate(opt.replaysSessionSampleRate); const replaysOnErrorSampleRate = parseSampleRate(opt.replaysOnErrorSampleRate); diff --git a/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts b/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts index ebe43c25eb98..1526e7175f4f 100644 --- a/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts +++ b/packages/replay-internal/test/integration/coreHandlers/handleGlobalEvent.test.ts @@ -1,3 +1,4 @@ +import { getClient } from '@sentry/core'; import type { Event } from '@sentry/types'; import { REPLAY_EVENT_NAME, SESSION_IDLE_EXPIRE_DURATION } from '../../../src/constants'; @@ -133,8 +134,8 @@ describe('Integration | coreHandlers | handleGlobalEvent', () => { it('tags errors and transactions with replay id for session samples', async () => { const { replay, integration } = await resetSdkMock({}); - // @ts-expect-error protected but ok to use for testing - integration._initialize(); + integration['_initialize'](getClient()!); + const transaction = Transaction(); const error = Error(); expect(handleGlobalEventListener(replay)(transaction, {})).toEqual( diff --git a/packages/replay-internal/test/integration/errorSampleRate.test.ts b/packages/replay-internal/test/integration/errorSampleRate.test.ts index b3b605f28c12..ef6d80dfeac6 100644 --- a/packages/replay-internal/test/integration/errorSampleRate.test.ts +++ b/packages/replay-internal/test/integration/errorSampleRate.test.ts @@ -866,7 +866,7 @@ describe('Integration | errorSampleRate', () => { }, autoStart: false, }); - integration['_initialize'](); + integration['_initialize'](getClient()!); expect(replay.recordingMode).toBe('session'); const sessionId = replay.getSessionId(); @@ -899,7 +899,7 @@ describe('Integration | errorSampleRate', () => { }, autoStart: false, }); - integration['_initialize'](); + integration['_initialize'](getClient()!); vi.runAllTimers(); @@ -943,7 +943,7 @@ describe('Integration | errorSampleRate', () => { }, autoStart: false, }); - integration['_initialize'](); + integration['_initialize'](getClient()!); const optionsEvent = createOptionsEvent(replay); const TEST_EVENT = getTestEventIncremental({ timestamp: BASE_TIMESTAMP }); diff --git a/packages/replay-internal/test/integration/sampling.test.ts b/packages/replay-internal/test/integration/sampling.test.ts index 433a010b76aa..c0e5404056a1 100644 --- a/packages/replay-internal/test/integration/sampling.test.ts +++ b/packages/replay-internal/test/integration/sampling.test.ts @@ -1,3 +1,4 @@ +import { getClient } from '@sentry/core'; import { resetSdkMock } from '../mocks/resetSdkMock'; import { useFakeTimers } from '../utils/use-fake-timers'; @@ -57,8 +58,7 @@ describe('Integration | sampling', () => { // @ts-expect-error private API const spyAddListeners = vi.spyOn(replay, '_addListeners'); - // @ts-expect-error protected - integration._initialize(); + integration['_initialize'](getClient()!); vi.runAllTimers(); diff --git a/packages/replay-internal/test/mocks/mockSdk.ts b/packages/replay-internal/test/mocks/mockSdk.ts index d5c9a088b779..38adc7f9c476 100644 --- a/packages/replay-internal/test/mocks/mockSdk.ts +++ b/packages/replay-internal/test/mocks/mockSdk.ts @@ -61,12 +61,8 @@ export async function mockSdk({ replayOptions, sentryOptions, autoStart = true } _initialized = value; } - public setupOnce(): void { - // do nothing - } - - public initialize(): void { - return super._initialize(); + public afterAllSetup(): void { + // do nothing, we need to manually initialize this } } @@ -76,7 +72,7 @@ export async function mockSdk({ replayOptions, sentryOptions, autoStart = true } ...replayOptions, }); - init({ + const client = init({ ...getDefaultClientOptions(), dsn: 'https://dsn@ingest.f00.f00/1', autoSessionTracking: false, @@ -86,14 +82,14 @@ export async function mockSdk({ replayOptions, sentryOptions, autoStart = true } replaysOnErrorSampleRate: 0.0, ...sentryOptions, integrations: [replayIntegration], - }); + })!; - // Instead of `setupOnce`, which is tricky to test, we call this manually here - replayIntegration['_setup'](); + // Instead of `afterAllSetup`, which is tricky to test, we call this manually here + replayIntegration['_setup'](client); if (autoStart) { // Only exists in our mock - replayIntegration.initialize(); + replayIntegration['_initialize'](client); } const replay = replayIntegration['_replay']!; diff --git a/packages/replay-internal/test/utils/TestClient.ts b/packages/replay-internal/test/utils/TestClient.ts index f95eb5309bcb..2651dfe1c91a 100644 --- a/packages/replay-internal/test/utils/TestClient.ts +++ b/packages/replay-internal/test/utils/TestClient.ts @@ -1,6 +1,7 @@ import { BaseClient, createTransport, initAndBind } from '@sentry/core'; import type { BrowserClientReplayOptions, + Client, ClientOptions, Event, ParameterizedString, @@ -33,8 +34,8 @@ export class TestClient extends BaseClient { } } -export function init(options: TestClientOptions): void { - initAndBind(TestClient, options); +export function init(options: TestClientOptions): Client { + return initAndBind(TestClient, options); } export function getDefaultClientOptions(options: Partial = {}): ClientOptions {