From b52ffcc8e334d8f6d8f06383269ab8e8ed4c25fd Mon Sep 17 00:00:00 2001 From: Valerio Belli Date: Tue, 25 Mar 2025 17:47:04 +0100 Subject: [PATCH 1/5] Move @react-native-async-storage/async-storage to peer dependencies --- trackers/react-native-tracker/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/trackers/react-native-tracker/package.json b/trackers/react-native-tracker/package.json index bbe1bd521..20cf3282b 100644 --- a/trackers/react-native-tracker/package.json +++ b/trackers/react-native-tracker/package.json @@ -43,6 +43,7 @@ "test": "jest" }, "peerDependencies": { + "@react-native-async-storage/async-storage": "~2.0.0", "react": "*", "react-native": "*", "react-native-get-random-values": "^1.11.0" @@ -51,11 +52,11 @@ "@snowplow/tracker-core": "workspace:*", "@snowplow/browser-tracker-core": "workspace:*", "@snowplow/browser-plugin-screen-tracking": "workspace:*", - "@react-native-async-storage/async-storage": "~2.0.0", "tslib": "^2.3.1", "uuid": "^10.0.0" }, "devDependencies": { + "@react-native-async-storage/async-storage": "~2.0.0", "@snowplow/browser-plugin-snowplow-ecommerce": "workspace:*", "@typescript-eslint/eslint-plugin": "~5.15.0", "@typescript-eslint/parser": "~5.15.0", From 2425bd717a7acfc67a3b89ebeee950cebd3ab703 Mon Sep 17 00:00:00 2001 From: Valerio Belli Date: Tue, 25 Mar 2025 17:47:55 +0100 Subject: [PATCH 2/5] Allow to specify a custom AsyncStorage implementation --- .../react-native-tracker/src/event_store.ts | 9 ++-- .../src/plugins/app_install/index.ts | 17 +++--- .../src/plugins/session/index.ts | 31 ++++++----- trackers/react-native-tracker/src/types.ts | 17 +++++- .../test/event_store.test.ts | 31 +++++++++++ .../test/plugins/session.test.ts | 54 ++++++++++++++++--- 6 files changed, 128 insertions(+), 31 deletions(-) diff --git a/trackers/react-native-tracker/src/event_store.ts b/trackers/react-native-tracker/src/event_store.ts index c98259302..9a6de4a0f 100644 --- a/trackers/react-native-tracker/src/event_store.ts +++ b/trackers/react-native-tracker/src/event_store.ts @@ -1,17 +1,18 @@ -import { EventStore, newInMemoryEventStore, EventStorePayload } from '@snowplow/tracker-core'; +import { EventStore, EventStorePayload, newInMemoryEventStore } from '@snowplow/tracker-core'; import { EventStoreConfiguration, TrackerConfiguration } from './types'; -import AsyncStorage from '@react-native-async-storage/async-storage'; +import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; export async function newReactNativeEventStore({ namespace, maxEventStoreSize = 1000, useAsyncStorageForEventStore: useAsyncStorage = true, + asyncStorage = DefaultAsyncStorage, }: EventStoreConfiguration & TrackerConfiguration): Promise { const queueName = `snowplow_${namespace}`; async function newInMemoryEventStoreForReactNative() { if (useAsyncStorage) { - const data = await AsyncStorage.getItem(queueName); + const data = await asyncStorage.getItem(queueName); const events: EventStorePayload[] = data ? JSON.parse(data) : []; return newInMemoryEventStore({ maxSize: maxEventStoreSize, events }); } else { @@ -26,7 +27,7 @@ export async function newReactNativeEventStore({ async function sync() { if (useAsyncStorage) { const events = await getAll(); - await AsyncStorage.setItem(queueName, JSON.stringify(events)); + await asyncStorage.setItem(queueName, JSON.stringify(events)); } } diff --git a/trackers/react-native-tracker/src/plugins/app_install/index.ts b/trackers/react-native-tracker/src/plugins/app_install/index.ts index 4e9c8ac58..e119488d8 100644 --- a/trackers/react-native-tracker/src/plugins/app_install/index.ts +++ b/trackers/react-native-tracker/src/plugins/app_install/index.ts @@ -1,7 +1,8 @@ -import { buildSelfDescribingEvent, CorePluginConfiguration, TrackerCore } from '@snowplow/tracker-core'; -import { AppLifecycleConfiguration, TrackerConfiguration } from '../../types'; +import type { CorePluginConfiguration, TrackerCore } from '@snowplow/tracker-core'; +import { buildSelfDescribingEvent } from '@snowplow/tracker-core'; import { APPLICATION_INSTALL_EVENT_SCHEMA } from '../../constants'; -import AsyncStorage from '@react-native-async-storage/async-storage'; +import type { AppLifecycleConfiguration, AsyncStorage, TrackerConfiguration } from '../../types'; +import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; /** * Tracks an application install event on the first run of the app. @@ -10,14 +11,18 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; * Event schema: `iglu:com.snowplowanalytics.mobile/application_install/jsonschema/1-0-0` */ export function newAppInstallPlugin( - { namespace, installAutotracking = false }: TrackerConfiguration & AppLifecycleConfiguration, + { + asyncStorage = DefaultAsyncStorage, + namespace, + installAutotracking = false, + }: TrackerConfiguration & AppLifecycleConfiguration & { asyncStorage?: AsyncStorage }, core: TrackerCore ): CorePluginConfiguration { if (installAutotracking) { // Track install event on first run const key = `snowplow_${namespace}_install`; setTimeout(async () => { - const installEvent = await AsyncStorage.getItem(key); + const installEvent = await asyncStorage.getItem(key); if (!installEvent) { core.track( buildSelfDescribingEvent({ @@ -27,7 +32,7 @@ export function newAppInstallPlugin( }, }) ); - await AsyncStorage.setItem(key, new Date().toISOString()); + await asyncStorage.setItem(key, new Date().toISOString()); } }, 0); } diff --git a/trackers/react-native-tracker/src/plugins/session/index.ts b/trackers/react-native-tracker/src/plugins/session/index.ts index c276cf8dd..339ca2327 100644 --- a/trackers/react-native-tracker/src/plugins/session/index.ts +++ b/trackers/react-native-tracker/src/plugins/session/index.ts @@ -1,9 +1,15 @@ import { CorePluginConfiguration, PayloadBuilder } from '@snowplow/tracker-core'; -import { SessionConfiguration, SessionState, TrackerConfiguration } from '../../types'; -import AsyncStorage from '@react-native-async-storage/async-storage'; import { v4 as uuidv4 } from 'uuid'; import { BACKGROUND_EVENT_SCHEMA, CLIENT_SESSION_ENTITY_SCHEMA, FOREGROUND_EVENT_SCHEMA } from '../../constants'; +import { + AsyncStorage, + EventStoreConfiguration, + SessionConfiguration, + SessionState, + TrackerConfiguration, +} from '../../types'; import { getUsefulSchema } from '../../utils'; +import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; interface StoredSessionState { userId: string; @@ -19,13 +25,13 @@ interface SessionPlugin extends CorePluginConfiguration { startNewSession: () => Promise; } -async function storeSessionState(namespace: string, state: StoredSessionState) { +async function storeSessionState(namespace: string, state: StoredSessionState, asyncStorage: AsyncStorage) { const { userId, sessionId, sessionIndex } = state; - await AsyncStorage.setItem(`snowplow_${namespace}_session`, JSON.stringify({ userId, sessionId, sessionIndex })); + await asyncStorage.setItem(`snowplow_${namespace}_session`, JSON.stringify({ userId, sessionId, sessionIndex })); } -async function resumeStoredSession(namespace: string): Promise { - const storedState = await AsyncStorage.getItem(`snowplow_${namespace}_session`); +async function resumeStoredSession(namespace: string, asyncStorage: AsyncStorage): Promise { + const storedState = await asyncStorage.getItem(`snowplow_${namespace}_session`); if (storedState) { const state = JSON.parse(storedState) as StoredSessionState; return { @@ -48,18 +54,19 @@ async function resumeStoredSession(namespace: string): Promise { /** * Creates a new session plugin for tracking the session information. * The plugin will add the session context to all events and start a new session if the current one has timed out. - * - * The session state is stored in AsyncStorage. + * + * The session state is stored in the defined application storage. * Each restart of the app or creation of a new tracker instance will trigger a new session with reference to the previous session. */ export async function newSessionPlugin({ + asyncStorage = DefaultAsyncStorage, namespace, sessionContext = true, foregroundSessionTimeout, backgroundSessionTimeout, -}: TrackerConfiguration & SessionConfiguration): Promise { - let sessionState = await resumeStoredSession(namespace); - await storeSessionState(namespace, sessionState); +}: EventStoreConfiguration & TrackerConfiguration & SessionConfiguration): Promise { + let sessionState = await resumeStoredSession(namespace, asyncStorage); + await storeSessionState(namespace, sessionState, asyncStorage); let inBackground = false; let lastUpdateTs = new Date().getTime(); @@ -84,7 +91,7 @@ export async function newSessionPlugin({ const timeDiff = now.getTime() - lastUpdateTs; if (timeDiff > getTimeoutMs()) { startNewSession(); - storeSessionState(namespace, sessionState); + storeSessionState(namespace, sessionState, asyncStorage); } lastUpdateTs = now.getTime(); diff --git a/trackers/react-native-tracker/src/types.ts b/trackers/react-native-tracker/src/types.ts index 6e2d8aaa2..66d4734a7 100755 --- a/trackers/react-native-tracker/src/types.ts +++ b/trackers/react-native-tracker/src/types.ts @@ -7,6 +7,11 @@ import { StructuredEvent, } from '@snowplow/tracker-core'; +export interface AsyncStorage { + getItem: (key: string) => Promise; + setItem: (key: string, value: string) => Promise; +} + /** * Configuration for the event store */ @@ -23,7 +28,16 @@ export interface EventStoreConfiguration { * Whether to use the AsyncStorage library as the persistent event store for the event store * @defaultValue true */ - useAsyncStorageForEventStore?: boolean; + useAsyncStorageForEventStore?: true; + + /** + * The Async storage implementation. + * In environments where AsyncStorage is not available or where another kind of storage is used, + * you can provide a custom implementation. + * + * @defaultValue AsyncStorage from {@link https://react-native-async-storage.github.io/async-storage/ @react-native-async-storage/async-storage} + * */ + asyncStorage?: AsyncStorage; } /** @@ -918,7 +932,6 @@ export { ContextGenerator, ContextFilter, EventPayloadAndContext, - EventStore, EventStoreIterator, EventStorePayload, TrackerCore, diff --git a/trackers/react-native-tracker/test/event_store.test.ts b/trackers/react-native-tracker/test/event_store.test.ts index 47d5030b8..712eafb22 100644 --- a/trackers/react-native-tracker/test/event_store.test.ts +++ b/trackers/react-native-tracker/test/event_store.test.ts @@ -1,5 +1,18 @@ import { newReactNativeEventStore } from '../src/event_store'; +function createAppStorageMock() { + const storageState: Record = {}; + + return { + getItem: (key: string) => Promise.resolve(storageState[key] ?? null), + setItem: (key: string, value: string) => { + storageState[key] = value; + + return Promise.resolve(); + }, + }; +} + describe('React Native event store', () => { it('keeps track of added events', async () => { const eventStore = await newReactNativeEventStore({ @@ -51,4 +64,22 @@ describe('React Native event store', () => { expect(await eventStore2.count()).toBe(2); }); + + it('syncs with the custom async storage implementation', async () => { + const asyncStorage = createAppStorageMock(); + const eventStore1 = await newReactNativeEventStore({ + asyncStorage, + namespace: 'testA', + }); + + await eventStore1.add({ payload: { e: 'pv' } }); + await eventStore1.add({ payload: { e: 'pp' } }); + + const eventStore2 = await newReactNativeEventStore({ + asyncStorage, + namespace: 'testA', + }); + + expect(await eventStore2.count()).toBe(2); + }); }); diff --git a/trackers/react-native-tracker/test/plugins/session.test.ts b/trackers/react-native-tracker/test/plugins/session.test.ts index 993fdf995..36c56f85b 100644 --- a/trackers/react-native-tracker/test/plugins/session.test.ts +++ b/trackers/react-native-tracker/test/plugins/session.test.ts @@ -1,7 +1,20 @@ -import { newSessionPlugin } from '../../src/plugins/session'; import { buildPageView, buildSelfDescribingEvent, Payload, trackerCore } from '@snowplow/tracker-core'; +import { newSessionPlugin } from '../../src/plugins/session'; import AsyncStorage from '@react-native-async-storage/async-storage'; +function createAppStorageMock() { + const storageState: Record = {}; + + return { + getItem: (key: string) => Promise.resolve(storageState[key] ?? null), + setItem: (key: string, value: string) => { + storageState[key] = value; + + return Promise.resolve(); + }, + }; +} + describe('Session plugin', () => { beforeAll(() => { jest.useFakeTimers(); @@ -119,12 +132,14 @@ describe('Session plugin', () => { }); const tracker = trackerCore({ corePlugins: [sessionPlugin.plugin] }); - tracker.track(buildSelfDescribingEvent({ - event: { - schema: 'iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0', - data: {}, - } - })); + tracker.track( + buildSelfDescribingEvent({ + event: { + schema: 'iglu:com.snowplowanalytics.snowplow/application_background/jsonschema/1-0-0', + data: {}, + }, + }) + ); let sessionState = await sessionPlugin.getSessionState(); expect(sessionState.sessionIndex).toBe(1); @@ -218,4 +233,29 @@ describe('Session plugin', () => { expect(sessionState.sessionId).toEqual(sessionId); expect(sessionState.sessionIndex).toEqual(sessionIndex); }); + + it('retrieves the correct information from the session plugin with a custom async storage', async () => { + jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); + const sessionPlugin = await newSessionPlugin({ + asyncStorage: createAppStorageMock(), + namespace: 'test', + foregroundSessionTimeout: 5, + backgroundSessionTimeout: 5, + }); + + const tracker = trackerCore({ corePlugins: [sessionPlugin.plugin] }); + tracker.track(buildPageView({ pageUrl: 'http://localhost' })); + + const userId = await sessionPlugin.getSessionUserId(); + const sessionId = await sessionPlugin.getSessionId(); + const sessionIndex = await sessionPlugin.getSessionIndex(); + const sessionState = await sessionPlugin.getSessionState(); + + expect(userId).toBeDefined(); + expect(sessionId).toBeDefined(); + expect(sessionIndex).toBe(1); + expect(sessionState.userId).toEqual(userId); + expect(sessionState.sessionId).toEqual(sessionId); + expect(sessionState.sessionIndex).toEqual(sessionIndex); + }); }); From 836b80fe232b98befaf5fd0c0d83501e92f6abb7 Mon Sep 17 00:00:00 2001 From: Valerio Belli Date: Wed, 26 Mar 2025 10:51:28 +0100 Subject: [PATCH 3/5] Remove optionality of AsyncStorage in plugins --- .../react-native-tracker/src/event_store.ts | 10 ++- .../src/plugins/app_install/index.ts | 5 +- .../src/plugins/session/index.ts | 13 +--- trackers/react-native-tracker/src/tracker.ts | 78 ++++++++++++------- .../test/event_store.test.ts | 6 ++ .../test/plugins/app_install.test.ts | 4 + .../test/plugins/session.test.ts | 9 +++ 7 files changed, 82 insertions(+), 43 deletions(-) diff --git a/trackers/react-native-tracker/src/event_store.ts b/trackers/react-native-tracker/src/event_store.ts index 9a6de4a0f..4fd3c6a27 100644 --- a/trackers/react-native-tracker/src/event_store.ts +++ b/trackers/react-native-tracker/src/event_store.ts @@ -1,13 +1,15 @@ import { EventStore, EventStorePayload, newInMemoryEventStore } from '@snowplow/tracker-core'; -import { EventStoreConfiguration, TrackerConfiguration } from './types'; -import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; +import { AsyncStorage, EventStoreConfiguration, TrackerConfiguration } from './types'; + +type Configuration = Omit & + TrackerConfiguration & { asyncStorage: AsyncStorage }; export async function newReactNativeEventStore({ namespace, maxEventStoreSize = 1000, useAsyncStorageForEventStore: useAsyncStorage = true, - asyncStorage = DefaultAsyncStorage, -}: EventStoreConfiguration & TrackerConfiguration): Promise { + asyncStorage, +}: Configuration): Promise { const queueName = `snowplow_${namespace}`; async function newInMemoryEventStoreForReactNative() { diff --git a/trackers/react-native-tracker/src/plugins/app_install/index.ts b/trackers/react-native-tracker/src/plugins/app_install/index.ts index e119488d8..954f41296 100644 --- a/trackers/react-native-tracker/src/plugins/app_install/index.ts +++ b/trackers/react-native-tracker/src/plugins/app_install/index.ts @@ -2,7 +2,6 @@ import type { CorePluginConfiguration, TrackerCore } from '@snowplow/tracker-cor import { buildSelfDescribingEvent } from '@snowplow/tracker-core'; import { APPLICATION_INSTALL_EVENT_SCHEMA } from '../../constants'; import type { AppLifecycleConfiguration, AsyncStorage, TrackerConfiguration } from '../../types'; -import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; /** * Tracks an application install event on the first run of the app. @@ -12,10 +11,10 @@ import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; */ export function newAppInstallPlugin( { - asyncStorage = DefaultAsyncStorage, + asyncStorage, namespace, installAutotracking = false, - }: TrackerConfiguration & AppLifecycleConfiguration & { asyncStorage?: AsyncStorage }, + }: TrackerConfiguration & AppLifecycleConfiguration & { asyncStorage: AsyncStorage }, core: TrackerCore ): CorePluginConfiguration { if (installAutotracking) { diff --git a/trackers/react-native-tracker/src/plugins/session/index.ts b/trackers/react-native-tracker/src/plugins/session/index.ts index 339ca2327..41dfdd5e6 100644 --- a/trackers/react-native-tracker/src/plugins/session/index.ts +++ b/trackers/react-native-tracker/src/plugins/session/index.ts @@ -1,15 +1,8 @@ import { CorePluginConfiguration, PayloadBuilder } from '@snowplow/tracker-core'; import { v4 as uuidv4 } from 'uuid'; import { BACKGROUND_EVENT_SCHEMA, CLIENT_SESSION_ENTITY_SCHEMA, FOREGROUND_EVENT_SCHEMA } from '../../constants'; -import { - AsyncStorage, - EventStoreConfiguration, - SessionConfiguration, - SessionState, - TrackerConfiguration, -} from '../../types'; +import { AsyncStorage, SessionConfiguration, SessionState, TrackerConfiguration } from '../../types'; import { getUsefulSchema } from '../../utils'; -import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; interface StoredSessionState { userId: string; @@ -59,12 +52,12 @@ async function resumeStoredSession(namespace: string, asyncStorage: AsyncStorage * Each restart of the app or creation of a new tracker instance will trigger a new session with reference to the previous session. */ export async function newSessionPlugin({ - asyncStorage = DefaultAsyncStorage, + asyncStorage, namespace, sessionContext = true, foregroundSessionTimeout, backgroundSessionTimeout, -}: EventStoreConfiguration & TrackerConfiguration & SessionConfiguration): Promise { +}: TrackerConfiguration & SessionConfiguration & { asyncStorage: AsyncStorage }): Promise { let sessionState = await resumeStoredSession(namespace, asyncStorage); await storeSessionState(namespace, sessionState, asyncStorage); diff --git a/trackers/react-native-tracker/src/tracker.ts b/trackers/react-native-tracker/src/tracker.ts index 5abdbc501..6a0738d01 100644 --- a/trackers/react-native-tracker/src/tracker.ts +++ b/trackers/react-native-tracker/src/tracker.ts @@ -33,38 +33,64 @@ import { newPlatformContextPlugin } from './plugins/platform_context'; import { newAppLifecyclePlugin } from './plugins/app_lifecycle'; import { newAppInstallPlugin } from './plugins/app_install'; import { newAppContextPlugin } from './plugins/app_context'; +import DefaultAsyncStorage from '@react-native-async-storage/async-storage'; const initializedTrackers: Record = {}; +type SetPropertiesAsNonNullable = Omit & { + [K in Properties]-?: NonNullable; +}; + +type Configuration = TrackerConfiguration & + EmitterConfiguration & + SessionConfiguration & + SubjectConfiguration & + EventStoreConfiguration & + ScreenTrackingConfiguration & + PlatformContextConfiguration & + DeepLinkConfiguration & + AppLifecycleConfiguration; + +type NormalizedConfiguration = SetPropertiesAsNonNullable< + Configuration, + 'asyncStorage' | 'devicePlatform' | 'encodeBase64' | 'eventStore' | 'plugins' +>; + +const normalizeTrackerConfiguration = async (configuration: Configuration): Promise => { + const eventStore = configuration.eventStore ?? (await newReactNativeEventStore(configuration)); + const asyncStorage = configuration.asyncStorage ?? DefaultAsyncStorage; + const plugins = configuration.plugins ?? []; + const devicePlatform = configuration.devicePlatform ?? 'mob'; + const encodeBase64 = configuration.encodeBase64 ?? false; + + return { + ...configuration, + devicePlatform, + encodeBase64, + asyncStorage, + eventStore, + plugins, + }; +}; + /** * Creates a new tracker instance with the given configuration * @param configuration - Configuration for the tracker * @returns Tracker instance */ -export async function newTracker( - configuration: TrackerConfiguration & - EmitterConfiguration & - SessionConfiguration & - SubjectConfiguration & - EventStoreConfiguration & - ScreenTrackingConfiguration & - PlatformContextConfiguration & - DeepLinkConfiguration & - AppLifecycleConfiguration -): Promise { - const { namespace, appId, encodeBase64 = false } = configuration; - if (configuration.eventStore === undefined) { - configuration.eventStore = await newReactNativeEventStore(configuration); - } +export async function newTracker(configuration: Configuration): Promise { + const normalizedConfiguration = await normalizeTrackerConfiguration(configuration); + + const { namespace, appId, encodeBase64 } = normalizedConfiguration; - const emitter = newEmitter(configuration); + const emitter = newEmitter(normalizedConfiguration); const callback = (payload: PayloadBuilder): void => { emitter.input(payload.build()); }; const core = trackerCore({ base64: encodeBase64, callback }); - core.setPlatform(configuration.devicePlatform ?? 'mob'); + core.setPlatform(normalizedConfiguration.devicePlatform); core.setTrackerVersion('rn-' + version); core.setTrackerNamespace(namespace); if (appId) { @@ -73,31 +99,31 @@ export async function newTracker( const { addPlugin } = newPlugins(namespace, core); - const sessionPlugin = await newSessionPlugin(configuration); + const sessionPlugin = await newSessionPlugin(normalizedConfiguration); addPlugin(sessionPlugin); - const deepLinksPlugin = await newDeepLinksPlugin(configuration, core); + const deepLinksPlugin = newDeepLinksPlugin(normalizedConfiguration, core); addPlugin(deepLinksPlugin); - const subject = newSubject(core, configuration); + const subject = newSubject(core, normalizedConfiguration); addPlugin(subject.subjectPlugin); - const screenPlugin = ScreenTrackingPlugin(configuration); + const screenPlugin = ScreenTrackingPlugin(normalizedConfiguration); addPlugin({ plugin: screenPlugin }); - const platformContextPlugin = await newPlatformContextPlugin(configuration); + const platformContextPlugin = await newPlatformContextPlugin(normalizedConfiguration); addPlugin(platformContextPlugin); - const lifecyclePlugin = await newAppLifecyclePlugin(configuration, core); + const lifecyclePlugin = await newAppLifecyclePlugin(normalizedConfiguration, core); addPlugin(lifecyclePlugin); - const installPlugin = newAppInstallPlugin(configuration, core); + const installPlugin = newAppInstallPlugin(normalizedConfiguration, core); addPlugin(installPlugin); - const appContextPlugin = newAppContextPlugin(configuration); + const appContextPlugin = newAppContextPlugin(normalizedConfiguration); addPlugin(appContextPlugin); - (configuration.plugins ?? []).forEach((plugin) => addPlugin({ plugin })); + normalizedConfiguration.plugins.forEach((plugin) => addPlugin({ plugin })); const tracker: ReactNativeTracker = { ...newTrackEventFunctions(core), diff --git a/trackers/react-native-tracker/test/event_store.test.ts b/trackers/react-native-tracker/test/event_store.test.ts index 712eafb22..410c63b2d 100644 --- a/trackers/react-native-tracker/test/event_store.test.ts +++ b/trackers/react-native-tracker/test/event_store.test.ts @@ -1,3 +1,4 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; import { newReactNativeEventStore } from '../src/event_store'; function createAppStorageMock() { @@ -16,6 +17,7 @@ function createAppStorageMock() { describe('React Native event store', () => { it('keeps track of added events', async () => { const eventStore = await newReactNativeEventStore({ + asyncStorage: AsyncStorage, namespace: 'test', }); @@ -34,9 +36,11 @@ describe('React Native event store', () => { it('separates event stores by namespace', async () => { const eventStore1 = await newReactNativeEventStore({ + asyncStorage: AsyncStorage, namespace: 'test1', }); const eventStore2 = await newReactNativeEventStore({ + asyncStorage: AsyncStorage, namespace: 'test2', }); @@ -52,6 +56,7 @@ describe('React Native event store', () => { it('syncs with AsyncStorage', async () => { const eventStore1 = await newReactNativeEventStore({ + asyncStorage: AsyncStorage, namespace: 'testA', }); @@ -59,6 +64,7 @@ describe('React Native event store', () => { await eventStore1.add({ payload: { e: 'pp' } }); const eventStore2 = await newReactNativeEventStore({ + asyncStorage: AsyncStorage, namespace: 'testA', }); diff --git a/trackers/react-native-tracker/test/plugins/app_install.test.ts b/trackers/react-native-tracker/test/plugins/app_install.test.ts index 88b9409f5..9a99287d4 100644 --- a/trackers/react-native-tracker/test/plugins/app_install.test.ts +++ b/trackers/react-native-tracker/test/plugins/app_install.test.ts @@ -15,6 +15,7 @@ describe('Application install plugin', () => { }); const appInstallPlugin = newAppInstallPlugin( { + asyncStorage: AsyncStorage, namespace: 'test', installAutotracking: true, }, @@ -39,6 +40,7 @@ describe('Application install plugin', () => { tracker1.addPlugin( newAppInstallPlugin( { + asyncStorage: AsyncStorage, namespace: 'test', installAutotracking: true, }, @@ -57,6 +59,7 @@ describe('Application install plugin', () => { tracker2.addPlugin( newAppInstallPlugin( { + asyncStorage: AsyncStorage, namespace: 'test', installAutotracking: true, }, @@ -75,6 +78,7 @@ describe('Application install plugin', () => { }); const appInstallPlugin = newAppInstallPlugin( { + asyncStorage: AsyncStorage, namespace: 'test', }, tracker diff --git a/trackers/react-native-tracker/test/plugins/session.test.ts b/trackers/react-native-tracker/test/plugins/session.test.ts index 36c56f85b..43e9823ea 100644 --- a/trackers/react-native-tracker/test/plugins/session.test.ts +++ b/trackers/react-native-tracker/test/plugins/session.test.ts @@ -31,6 +31,7 @@ describe('Session plugin', () => { it('starts a new session when necessary', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, @@ -52,6 +53,7 @@ describe('Session plugin', () => { it('attaches session context to events with the correct properties', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, @@ -101,6 +103,7 @@ describe('Session plugin', () => { it('creates a new session when new tracker is created', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, @@ -113,6 +116,7 @@ describe('Session plugin', () => { expect(sessionState.sessionIndex).toBe(1); const sessionPlugin2 = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, @@ -126,6 +130,7 @@ describe('Session plugin', () => { it('uses a background timeout when in background', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 1000, backgroundSessionTimeout: 5, @@ -161,6 +166,7 @@ describe('Session plugin', () => { it('uses a foreground timeout when in foreground', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 1, @@ -186,12 +192,14 @@ describe('Session plugin', () => { it('has separate session state for different namespaces', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin1 = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test1', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, }); const sessionPlugin2 = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test2', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, @@ -213,6 +221,7 @@ describe('Session plugin', () => { it('retrieves the correct information from the session plugin', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ + asyncStorage: AsyncStorage, namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, From 2e829e914ae171feeb34c35f215ad1db4e230292 Mon Sep 17 00:00:00 2001 From: Valerio Belli Date: Wed, 26 Mar 2025 10:54:32 +0100 Subject: [PATCH 4/5] Rename AsyncStorage-related mocks in React Native tracker tests --- trackers/react-native-tracker/test/event_store.test.ts | 4 ++-- trackers/react-native-tracker/test/plugins/session.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/trackers/react-native-tracker/test/event_store.test.ts b/trackers/react-native-tracker/test/event_store.test.ts index 410c63b2d..8c95e51ed 100644 --- a/trackers/react-native-tracker/test/event_store.test.ts +++ b/trackers/react-native-tracker/test/event_store.test.ts @@ -1,7 +1,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import { newReactNativeEventStore } from '../src/event_store'; -function createAppStorageMock() { +function createAsyncStorageMock() { const storageState: Record = {}; return { @@ -72,7 +72,7 @@ describe('React Native event store', () => { }); it('syncs with the custom async storage implementation', async () => { - const asyncStorage = createAppStorageMock(); + const asyncStorage = createAsyncStorageMock(); const eventStore1 = await newReactNativeEventStore({ asyncStorage, namespace: 'testA', diff --git a/trackers/react-native-tracker/test/plugins/session.test.ts b/trackers/react-native-tracker/test/plugins/session.test.ts index 43e9823ea..bd8fd3563 100644 --- a/trackers/react-native-tracker/test/plugins/session.test.ts +++ b/trackers/react-native-tracker/test/plugins/session.test.ts @@ -2,7 +2,7 @@ import { buildPageView, buildSelfDescribingEvent, Payload, trackerCore } from '@ import { newSessionPlugin } from '../../src/plugins/session'; import AsyncStorage from '@react-native-async-storage/async-storage'; -function createAppStorageMock() { +function createAsyncStorageMock() { const storageState: Record = {}; return { @@ -246,7 +246,7 @@ describe('Session plugin', () => { it('retrieves the correct information from the session plugin with a custom async storage', async () => { jest.setSystemTime(new Date('2022-04-17T00:00:00.000Z')); const sessionPlugin = await newSessionPlugin({ - asyncStorage: createAppStorageMock(), + asyncStorage: createAsyncStorageMock(), namespace: 'test', foregroundSessionTimeout: 5, backgroundSessionTimeout: 5, From 5c768211c0d32cc8123930956672e06b32bf7f52 Mon Sep 17 00:00:00 2001 From: Valerio Belli Date: Wed, 2 Apr 2025 01:05:04 +0200 Subject: [PATCH 5/5] Revert types of EventStoreConfiguration --- trackers/react-native-tracker/src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trackers/react-native-tracker/src/types.ts b/trackers/react-native-tracker/src/types.ts index 66d4734a7..f12fa2d07 100755 --- a/trackers/react-native-tracker/src/types.ts +++ b/trackers/react-native-tracker/src/types.ts @@ -28,7 +28,7 @@ export interface EventStoreConfiguration { * Whether to use the AsyncStorage library as the persistent event store for the event store * @defaultValue true */ - useAsyncStorageForEventStore?: true; + useAsyncStorageForEventStore?: boolean; /** * The Async storage implementation.