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
14 changes: 7 additions & 7 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -286,12 +286,12 @@ PODS:
- React
- RNGestureHandler (2.3.0):
- React-Core
- segment-analytics-react-native (2.2.0):
- segment-analytics-react-native (2.4.0):
- React-Core
- sovran-react-native
- segment-analytics-react-native-plugin-idfa (0.2.1):
- segment-analytics-react-native-plugin-idfa (0.4.0):
- React-Core
- sovran-react-native (0.2.8):
- sovran-react-native (0.4.0):
- React-Core
- Yoga (1.14.0)

Expand Down Expand Up @@ -459,11 +459,11 @@ SPEC CHECKSUMS:
RNCAsyncStorage: b49b4e38a1548d03b74b30e558a1d18465b94be7
RNCMaskedView: 0e1bc4bfa8365eba5fbbb71e07fbdc0555249489
RNGestureHandler: 77d59828d40838c9fabb76a12d2d0a80c006906f
segment-analytics-react-native: d0b24d7b7e6e6968a3558a2c41f61e94420b6797
segment-analytics-react-native-plugin-idfa: 80e5d610f537156833eabea12a1804523355de95
sovran-react-native: e4064b633fd8232055d003460d5816dff87ba8cc
segment-analytics-react-native: b8ef30c1fc4feae75d98e03226cfc64716f6e0e3
segment-analytics-react-native-plugin-idfa: 9befc7ff398e8a1fc49d353b7d74dab62a23e519
sovran-react-native: 81407f2b9d1171f7106e7c9a8ef34de7a412caae
Yoga: 90dcd029e45d8a7c1ff059e8b3c6612ff409061a

PODFILE CHECKSUM: 0c7eb82d495ca56953c50916b7b49e7512632eb6

COCOAPODS: 1.11.2
COCOAPODS: 1.11.3
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"@react-native-community/masked-view": "^0.1.11",
"@react-navigation/native": "^6.0.2",
"@react-navigation/stack": "^6.0.7",
"@segment/sovran-react-native": "^0.2.8",
"@segment/sovran-react-native": "^0.4.0",
"react": "17.0.2",
"react-native": "0.67.3",
"react-native-bootsplash": "^3.2.4",
Expand Down
8 changes: 4 additions & 4 deletions example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1575,10 +1575,10 @@
color "^3.1.3"
warn-once "^0.1.0"

"@segment/sovran-react-native@^0.2.8":
version "0.2.8"
resolved "https://registry.yarnpkg.com/@segment/sovran-react-native/-/sovran-react-native-0.2.8.tgz#76a3c29011f9726a0fa2ac3942fb1d7715816d7e"
integrity sha512-b3a2vfEj2+jb8w/o+rNrJESWUhHEtrRZgydRNg1PEmMDlLeh42T3mDAap4mtGFICRDHU57w2zPeuw+wfs/sk7g==
"@segment/sovran-react-native@^0.4.0":
version "0.4.0"
resolved "https://registry.yarnpkg.com/@segment/sovran-react-native/-/sovran-react-native-0.4.0.tgz#cdac0902657dbb0c16103601d7e3885f67979a69"
integrity sha512-dba3sEQteoZLZf3STAgl5xKjKQbeupvQxsCtp/9UcEDkhnfYEq9AIXXZ7iNVSFK+uAxfdlumZU4kfMBcsHzVUw==
dependencies:
"@react-native-async-storage/async-storage" "^1.15.15"
ansi-regex "5.0.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"homepage": "https://github.com/segmentio/analytics-react-native#readme",
"dependencies": {
"@react-native-async-storage/async-storage": "^1.15.17",
"@segment/sovran-react-native": "^0.2.8",
"@segment/sovran-react-native": "^0.4.0",
"deepmerge": "^4.2.2",
"js-base64": "^3.7.2",
"nanoid": "^3.1.25",
Expand Down
32 changes: 32 additions & 0 deletions packages/core/src/__tests__/__helpers__/mockEventStore.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { SegmentEvent } from '../../types';
import { createMockStoreGetter } from './mockSegmentStore';
// import { createMockStoreGetter } from './mockSegmentStore';
import { createCallbackManager } from './utils';

export class MockEventStore {
private initialData: SegmentEvent[] = [];
private events: SegmentEvent[] = [];

private callbackManager = createCallbackManager<{ events: SegmentEvent[] }>();

constructor(initialData?: SegmentEvent[]) {
this.events = [...(initialData ?? [])];
this.initialData = JSON.parse(JSON.stringify(initialData ?? []));
}

reset = () => {
this.events = JSON.parse(JSON.stringify(this.initialData));
};

getState = createMockStoreGetter(() => ({ events: this.events }));

subscribe = (callback: (value: { events: SegmentEvent[] }) => void) =>
this.callbackManager.register(callback);

dispatch = (
callback: (value: { events: SegmentEvent[] }) => { events: SegmentEvent[] }
) => {
this.events = callback({ events: this.events }).events;
this.callbackManager.run({ events: this.events });
};
}
92 changes: 45 additions & 47 deletions packages/core/src/__tests__/__helpers__/mockSegmentStore.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { SEGMENT_DESTINATION_KEY } from '../../plugins/SegmentDestination';
import type { DeepLinkData, Storage } from '../../storage';
import type {
DeepLinkData,
Dictionary,
Settable,
Storage,
Watchable,
} from '../../storage';
import type {
Context,
DeepPartial,
IntegrationSettings,
SegmentAPIIntegrations,
SegmentEvent,
UserInfoState,
} from '../../types';
import { createCallbackManager } from './utils';
import { createGetter } from '../../storage/helpers';

type Data = {
isReady: boolean;
Expand All @@ -35,32 +41,12 @@ const INITIAL_VALUES: Data = {
},
};

export class MockEventStore {
private initialData: SegmentEvent[] = [];
private events: SegmentEvent[] = [];

private callbackManager = createCallbackManager<{ events: SegmentEvent[] }>();

constructor(initialData?: SegmentEvent[]) {
this.events = [...(initialData ?? [])];
this.initialData = JSON.parse(JSON.stringify(initialData ?? []));
}

reset = () => {
this.events = JSON.parse(JSON.stringify(this.initialData));
};

getState = () => this.events;

subscribe = (callback: (value: { events: SegmentEvent[] }) => void) =>
this.callbackManager.register(callback);

dispatch = (
callback: (value: { events: SegmentEvent[] }) => { events: SegmentEvent[] }
) => {
this.events = callback({ events: this.events }).events;
this.callbackManager.run({ events: this.events });
};
export function createMockStoreGetter<T>(fn: () => T) {
return createGetter(fn, () => {
return new Promise((resolve) => {
resolve(fn());
});
});
}

export class MockSegmentStore implements Storage {
Expand All @@ -86,34 +72,43 @@ export class MockSegmentStore implements Storage {
};

readonly isReady = {
get: () => {
get: createMockStoreGetter(() => {
return this.data.isReady;
},
}),
onChange: (_callback: (value: boolean) => void) => {
// Not doing anything cause this mock store is always ready, this is just legacy from the redux persistor
return () => {};
},
};

readonly context = {
get: () => ({ ...this.data.context }),
readonly context: Watchable<DeepPartial<Context> | undefined> &
Settable<DeepPartial<Context>> = {
get: createMockStoreGetter(() => ({ ...this.data.context })),
onChange: (callback: (value?: DeepPartial<Context>) => void) =>
this.callbacks.context.register(callback),
set: (value: DeepPartial<Context>) => {
this.data.context = { ...value };
this.callbacks.context.run(value);
set: (value) => {
this.data.context =
value instanceof Function
? value(this.data.context ?? {})
: { ...value };
this.callbacks.context.run(this.data.context);
return this.data.context;
},
};

readonly settings = {
get: () => this.data.settings,
readonly settings: Watchable<SegmentAPIIntegrations | undefined> &
Settable<SegmentAPIIntegrations> &
Dictionary<string, IntegrationSettings> = {
get: createMockStoreGetter(() => this.data.settings),
onChange: (
callback: (value?: SegmentAPIIntegrations | undefined) => void
) => this.callbacks.settings.register(callback),
set: (value: SegmentAPIIntegrations) => {
this.data.settings = value;
this.callbacks.settings.run(value);
set: (value) => {
this.data.settings =
value instanceof Function
? value(this.data.settings ?? {})
: { ...value };
this.callbacks.settings.run(this.data.settings);
return this.data.settings;
},
add: (key: string, value: IntegrationSettings) => {
Expand All @@ -122,21 +117,24 @@ export class MockSegmentStore implements Storage {
},
};

readonly userInfo = {
get: () => this.data.userInfo,
readonly userInfo: Watchable<UserInfoState> & Settable<UserInfoState> = {
get: createMockStoreGetter(() => this.data.userInfo),
onChange: (callback: (value: UserInfoState) => void) =>
this.callbacks.userInfo.register(callback),
set: (value: UserInfoState) => {
this.data.userInfo = value;
this.callbacks.userInfo.run(value);
set: (value) => {
this.data.userInfo =
value instanceof Function
? value(this.data.userInfo ?? {})
: { ...value };
this.callbacks.userInfo.run(this.data.userInfo);
return this.data.userInfo;
},
};

readonly deepLinkData = {
get: () => {
get: createMockStoreGetter(() => {
return this.data.deepLinkData;
},
}),
set: (value: DeepLinkData) => {
this.data.deepLinkData = value;
this.callbacks.deepLinkData.run(value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ describe('SegmentClient #handleAppStateChange', () => {
event: 'Application Opened',
properties: {
from_background: true,
build: store.context.get().app?.build,
version: store.context.get().app?.version,
build: store.context.get()?.app?.build,
version: store.context.get()?.app?.version,
},
type: EventType.TrackEvent,
});
Expand All @@ -104,8 +104,8 @@ describe('SegmentClient #handleAppStateChange', () => {
event: 'Application Opened',
properties: {
from_background: true,
build: store.context.get().app?.build,
version: store.context.get().app?.version,
build: store.context.get()?.app?.build,
version: store.context.get()?.app?.version,
},
type: EventType.TrackEvent,
});
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/__tests__/internal/trackDeepLinks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import { SegmentClient } from '../../analytics';
import { getMockLogger } from '../__helpers__/mockLogger';
import * as ReactNative from 'react-native';
import { EventType } from '../../types';
import { MockSegmentStore } from '../__helpers__/mockSegmentStore';
import {
createMockStoreGetter,
MockSegmentStore,
} from '../__helpers__/mockSegmentStore';

jest
.spyOn(Date.prototype, 'toISOString')
Expand Down Expand Up @@ -40,7 +43,9 @@ describe('#trackDeepLinks', () => {
url: 'myapp://open',
referring_application: 'Safari',
};
jest.spyOn(store.deepLinkData, 'get').mockReturnValue(deepLinkData);
jest
.spyOn(store.deepLinkData, 'get')
.mockImplementation(createMockStoreGetter(() => deepLinkData));
const client = new SegmentClient(clientArgs);
jest.spyOn(client, 'process');

Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/__tests__/methods/alias.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ describe('methods #alias', () => {
jest.clearAllMocks();
});

it('adds the alias event correctly', () => {
it('adds the alias event correctly', async () => {
const client = new SegmentClient(clientArgs);

jest.spyOn(client, 'process');

client.alias('new-user-id');
await client.alias('new-user-id');

const expectedEvent = {
previousId: 'current-user-id',
Expand All @@ -52,7 +52,7 @@ describe('methods #alias', () => {
});
});

it('uses anonymousId in event if no userId in store', () => {
it('uses anonymousId in event if no userId in store', async () => {
const client = new SegmentClient({
...clientArgs,
store: new MockSegmentStore({
Expand All @@ -64,7 +64,7 @@ describe('methods #alias', () => {
});
jest.spyOn(client, 'process');

client.alias('new-user-id');
await client.alias('new-user-id');

const expectedEvent = {
previousId: 'anonymousId',
Expand Down
21 changes: 12 additions & 9 deletions packages/core/src/__tests__/methods/identify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ describe('methods #identify', () => {
jest.clearAllMocks();
});

it('adds the identify event correctly', () => {
it('adds the identify event correctly', async () => {
const client = new SegmentClient(clientArgs);
jest.spyOn(client, 'process');

client.identify('new-user-id', { name: 'Mary', age: 30 });
await client.identify('new-user-id', { name: 'Mary', age: 30 });

const expectedEvent = {
traits: {
Expand All @@ -59,11 +59,11 @@ describe('methods #identify', () => {
});
});

it('does not update user traits when there are no new ones provided', () => {
it('does not update user traits when there are no new ones provided', async () => {
const client = new SegmentClient(clientArgs);
jest.spyOn(client, 'process');

client.identify('new-user-id');
await client.identify('new-user-id');

const expectedEvent = {
traits: initialUserInfo.traits,
Expand All @@ -80,15 +80,15 @@ describe('methods #identify', () => {
});
});

it('does not update userId when userId is undefined', () => {
it('does not update userId when userId is undefined', async () => {
const client = new SegmentClient(clientArgs);
jest.spyOn(client, 'process');

client.identify(undefined, { name: 'Mary' });
await client.identify(undefined, { name: 'Mary' });

const expectedEvent = {
traits: { name: 'Mary', age: 30 },
userId: undefined,
userId: 'current-user-id',
type: 'identify',
};

Expand All @@ -103,13 +103,13 @@ describe('methods #identify', () => {
});
});

it('does not persist identity traits accross events', () => {
it('does not persist identity traits accross events', async () => {
const client = new SegmentClient(clientArgs);
jest.spyOn(client, 'process');
// @ts-ignore accessing the internal timeline to check the processed events
jest.spyOn(client.timeline, 'process');

client.identify('new-user-id', { name: 'Mary', age: 30 });
await client.identify('new-user-id', { name: 'Mary', age: 30 });

const expectedEvent = {
traits: {
Expand All @@ -131,6 +131,9 @@ describe('methods #identify', () => {

client.track('track event');

// Await all promises
await new Promise(process.nextTick);

// @ts-ignore
expect(client.timeline.process).toHaveBeenLastCalledWith({
anonymousId: 'very-anonymous',
Expand Down
Loading