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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@

## Unreleased

### Fixes

- Skip idle span creation when app is in background ([#4995](https://github.com/getsentry/sentry-react-native/pull/4995))

### Dependencies

- Bump JavaScript SDK from v8.54.0 to v8.55.0 ([#4981](https://github.com/getsentry/sentry-react-native/pull/4981))
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/js/tracing/span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
spanToJSON,
startIdleSpan as coreStartIdleSpan,
} from '@sentry/core';
import { AppState } from 'react-native';

import { isRootSpan } from '../utils/span';
import { adjustTransactionDuration, cancelInBackground } from './onSpanEndUtils';
Expand Down Expand Up @@ -104,6 +105,12 @@ export const startIdleSpan = (
return new SentryNonRecordingSpan();
}

const currentAppState = AppState.currentState;
if (currentAppState === 'background') {
logger.debug(`[startIdleSpan] App is already in background, not starting span for ${startSpanOption.name}`);
return new SentryNonRecordingSpan();
}

getCurrentScope().setPropagationContext(generatePropagationContext());

const span = coreStartIdleSpan(startSpanOption, { finalTimeout, idleTimeout });
Expand Down
32 changes: 32 additions & 0 deletions packages/core/test/tracing/idleNavigationSpan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('startIdleNavigationSpan', () => {
jest.useFakeTimers();
NATIVE.enableNative = true;
mockedAppState.isAvailable = true;
mockedAppState.currentState = 'active';
mockedAppState.addEventListener = (_, listener) => {
mockedAppState.listener = listener;
return {
Expand Down Expand Up @@ -82,6 +83,37 @@ describe('startIdleNavigationSpan', () => {
expect(spanToJSON(transaction!).timestamp).toBeDefined();
});

it('Returns non-recording span when app is already in background', () => {
mockedAppState.currentState = 'background';

const span = startIdleNavigationSpan({
name: 'test',
});

// Non-recording spans don't become active
expect(getActiveSpan()).toBeUndefined();

// Verify it's a non-recording span
expect(span).toBeDefined();
expect(span.constructor.name).toBe('SentryNonRecordingSpan');

// No AppState listener should be set up for non-recording spans
expect(mockedAppState.removeSubscription).not.toHaveBeenCalled();
});

it('Does not set up AppState listener for background spans', () => {
mockedAppState.currentState = 'background';

startIdleNavigationSpan({
name: 'test',
});

mockedAppState.setState('active');

// No subscription removal should happen since no listener was set up
expect(mockedAppState.removeSubscription).not.toHaveBeenCalled();
});

describe('Start a new active root span (without parent)', () => {
it('Starts a new span when there is no active span', () => {
const span = startIdleNavigationSpan({
Expand Down
13 changes: 13 additions & 0 deletions packages/core/test/tracing/integrations/userInteraction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ describe('User Interaction Tracing', () => {
jest.useFakeTimers();
NATIVE.enableNative = true;
mockedAppState.isAvailable = true;
mockedAppState.currentState = 'active';
mockedAppState.addEventListener = (_, listener) => {
mockedAppState.listener = listener;
return {
Expand Down Expand Up @@ -291,5 +292,17 @@ describe('User Interaction Tracing', () => {
);
expect(interactionTransactionContext!.timestamp).toBeLessThanOrEqual(routingTransactionContext!.start_timestamp!);
});

test('does not start UI span when app is in background', () => {
mockedAppState.currentState = 'background';

startUserInteractionSpan(mockedUserInteractionId);

// No active span should be created
expect(getActiveSpan()).toBeUndefined();

// No events should be queued
expect(client.eventQueue).toHaveLength(0);
});
});
});
Loading