Skip to content

Commit 5a2d8a3

Browse files
authored
Merge pull request #3 from Scode-Njnjas/feature/implement-test-case-and-fix-issue-initialize-twice-in-example-app
feat: implement test case and fix issue: initialize twice in the example app
2 parents 7cde103 + f5de6a9 commit 5a2d8a3

File tree

11 files changed

+361
-87
lines changed

11 files changed

+361
-87
lines changed

README.md

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -54,33 +54,34 @@ const AppContent = () => {
5454

5555
### `RNConvertInSDKProvider`
5656

57-
| Prop | Type | Required |
58-
|------|------|----------|
59-
| `pixelId` | string | Yes |
60-
| `storeUrl` | string | Yes |
57+
| Prop | Type | Required | Description |
58+
| ---------- | ------ | -------- | ------------------------- |
59+
| `pixelId` | string | Yes | Your ConvertedIn pixel ID |
60+
| `storeUrl` | string | Yes | Your store URL |
6161

6262
### `useConvertedIn`
6363

6464
Returns an object with the following methods:
6565

66-
| Method | Parameters | Return Type |
67-
|--------|------------|-------------|
68-
| `initializeSDK` | None | `Promise<void>` |
69-
| `identifyUser` | `email: string, countryCode: string, phone: string` | `void` |
70-
| `addEvent` | `name: string, currency: string, total: number, products: Product[]` | `void` |
71-
| `viewContentEvent` | `currency: string, total: number, products: Product[]` | `void` |
72-
| `addToCartEvent` | `currency: string, total: number, products: Product[]` | `void` |
73-
| `initiateCheckoutEvent` | `currency: string, total: number, products: Product[]` | `void` |
74-
| `purchaseEvent` | `currency: string, total: number, products: Product[]` | `void` |
75-
| `registerEvent` | None | `void` |
66+
| Method | Parameters | Return Type | Description |
67+
| ----------------------- | ------------------------------------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
68+
| `isInitialized` | None | `boolean` | Returns whether the SDK has been initialized. |
69+
| `initializeSDK` | `config: { pixelId: string, storeUrl: string }` | `Promise<void>` | Initializes the SDK with the provided configuration. Call this as early as possible in your app's lifecycle. |
70+
| `identifyUser` | `email: string, countryCode: string, phoneNumber: string` | `void` | Identifies a user with their email, country code, and phone number. This helps in tracking user-specific events and improving personalization. |
71+
| `addEvent` | `eventName: string, currency: string, total: number, products: Product[]` | `void` | Adds a custom event with the specified name, currency, total, and products. This allows tracking of specific actions or milestones in your app. |
72+
| `viewContentEvent` | `currency: string, total: number, products: Product[]` | `void` | Tracks a view content event when a user views a product or content page. Includes details like currency, total value, and viewed products. |
73+
| `addToCartEvent` | `currency: string, total: number, products: Product[]` | `void` | Tracks an add to cart event when a user adds items to their shopping cart. Includes details about the added products, total value, and currency. |
74+
| `initiateCheckoutEvent` | `currency: string, total: number, products: Product[]` | `void` | Tracks the initiation of the checkout process. This event should be called when a user starts the purchasing process. |
75+
| `purchaseEvent` | `currency: string, total: number, products: Product[]` | `void` | Tracks a completed purchase event. This should be called when a user successfully completes a transaction, including details of purchased products. |
76+
| `registerEvent` | None | `void` | Tracks a user registration event. This should be called when a new user creates an account in your app. |
7677

7778
### `Product` Interface
7879

79-
| Property | Type |
80-
|----------|------|
81-
| `id` | number |
82-
| `quantity` | number |
83-
| `name` | string |
80+
| Property | Type | Description |
81+
| ---------- | ------ | ------------------------------------- |
82+
| `id` | number | The unique identifier of the product. |
83+
| `quantity` | number | The quantity of the product. |
84+
| `name` | string | The name of the product. |
8485

8586
## Example
8687

@@ -127,6 +128,16 @@ Run tests with:
127128
yarn test
128129
```
129130

131+
### Error Handling
132+
133+
If any SDK method is called before initialization, an error will be thrown with the message:
134+
135+
```txt
136+
SDK must be initialized before calling this method.
137+
```
138+
139+
Make sure to initialize the SDK using the `initializeSDK` method before invoking any other SDK methods.
140+
130141
## Contributing
131142

132143
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.

example/ios/ConvertedInSdkExample.xcodeproj/project.pbxproj

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,10 @@
593593
"-DFOLLY_CFG_NO_COROUTINES=1",
594594
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
595595
);
596-
OTHER_LDFLAGS = "$(inherited) ";
596+
OTHER_LDFLAGS = (
597+
"$(inherited)",
598+
" ",
599+
);
597600
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
598601
SDKROOT = iphoneos;
599602
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -666,7 +669,10 @@
666669
"-DFOLLY_CFG_NO_COROUTINES=1",
667670
"-DFOLLY_HAVE_CLOCK_GETTIME=1",
668671
);
669-
OTHER_LDFLAGS = "$(inherited) ";
672+
OTHER_LDFLAGS = (
673+
"$(inherited)",
674+
" ",
675+
);
670676
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
671677
SDKROOT = iphoneos;
672678
USE_HERMES = true;

example/src/App.tsx

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { useEffect, useState } from 'react';
21
import { ActivityIndicator, StyleSheet, View } from 'react-native';
32
import {
43
RNConvertInSDKProvider,
@@ -9,35 +8,11 @@ import CustomEventOpenApp from './components/custom-event-open-app';
98
import IdentifyUser from './components/identify-user';
109
import InitiateCheckoutEvent from './components/initiate-checkout';
1110
import PurchaseEvent from './components/purchase';
12-
import ViewContent from './components/view-content';
1311
import Register from './components/register';
12+
import ViewContent from './components/view-content';
1413

15-
const AppContent = () => {
16-
const [isInitialized, setIsInitialized] = useState(false);
17-
const { initializeSDK } = useConvertedIn();
18-
19-
useEffect(() => {
20-
let isMounted = true;
21-
22-
const initialize = async () => {
23-
if (!isInitialized) {
24-
try {
25-
await initializeSDK();
26-
if (isMounted) {
27-
setIsInitialized(true);
28-
}
29-
} catch (error) {
30-
console.error('Failed to initialize SDK:', error);
31-
}
32-
}
33-
};
34-
35-
initialize();
36-
37-
return () => {
38-
isMounted = false;
39-
};
40-
}, [initializeSDK, isInitialized]);
14+
const AppContent: React.FC = () => {
15+
const { isInitialized } = useConvertedIn();
4116

4217
if (!isInitialized) {
4318
return <ActivityIndicator size="large" />;

package.json

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-native-converted-in-sdk",
3-
"version": "0.1.0",
3+
"version": "1.0.0",
44
"description": "Converted In SDK for React Native",
55
"source": "src/index.ts",
66
"main": "lib/commonjs/index.js",
@@ -43,7 +43,15 @@
4343
"keywords": [
4444
"react-native",
4545
"ios",
46-
"android"
46+
"android",
47+
"converted-in",
48+
"sdk",
49+
"tracking",
50+
"events",
51+
"react-native-converted-in-sdk",
52+
"com.github.convertedin",
53+
"android-pixel-sdk",
54+
"iOS_ConvertedinMobileSDK"
4755
],
4856
"repository": {
4957
"type": "git",
@@ -63,8 +71,11 @@
6371
"@evilmartians/lefthook": "^1.5.0",
6472
"@react-native/eslint-config": "^0.73.1",
6573
"@release-it/conventional-changelog": "^5.0.0",
74+
"@testing-library/react-hooks": "^8.0.1",
75+
"@testing-library/react-native": "^12.6.1",
6676
"@types/jest": "^29.5.5",
6777
"@types/react": "^18.2.44",
78+
"@types/testing-library__react-hooks": "^4.0.0",
6879
"commitlint": "^17.0.2",
6980
"del-cli": "^5.1.0",
7081
"eslint": "^8.51.0",
@@ -75,6 +86,7 @@
7586
"react": "18.3.1",
7687
"react-native": "0.75.2",
7788
"react-native-builder-bob": "^0.30.0",
89+
"react-test-renderer": "^18.3.1",
7890
"release-it": "^15.0.0",
7991
"turbo": "^1.10.7",
8092
"typescript": "^5.2.2"

src/RNConvertedInSdkModule.ts

Lines changed: 36 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,60 @@ interface Product {
1010

1111
let isInitialized = false;
1212

13-
interface InitializeSDKParams {
13+
export const __test__ = {
14+
resetInitialization: () => {
15+
isInitialized = false;
16+
},
17+
};
18+
19+
interface SDKConfig {
1420
pixelId: string;
1521
storeUrl: string;
1622
}
1723

18-
export const initializeSDK = async ({
19-
pixelId,
20-
storeUrl,
21-
}: InitializeSDKParams): Promise<void> => {
22-
try {
23-
await ConvertedInSDKModule.initializeSDK({ pixelId, storeUrl });
24-
isInitialized = true;
25-
console.log('SDK Initialized Successfully');
26-
} catch (error: any) {
27-
console.error('Failed to initialize SDK:', error);
28-
throw error;
29-
}
30-
};
31-
3224
const checkInitialization = () => {
3325
if (!isInitialized) {
3426
throw new Error('SDK must be initialized before calling this method.');
3527
}
3628
};
3729

38-
export const identifyUser = (
30+
export async function initializeSDK(config: SDKConfig): Promise<void> {
31+
if (isInitialized) {
32+
console.log('SDK already initialized');
33+
return;
34+
}
35+
await NativeModules.ConvertedInSDKModule.initializeSDK(config);
36+
console.log('SDK Initialized Successfully');
37+
isInitialized = true;
38+
}
39+
40+
export function identifyUser(
3941
email: string,
4042
countryCode: string,
41-
phone: string
42-
) => {
43+
phoneNumber: string
44+
): void {
4345
checkInitialization();
44-
ConvertedInSDKModule.identifyUser(email, countryCode, phone);
45-
};
46+
NativeModules.ConvertedInSDKModule.identifyUser(
47+
email,
48+
countryCode,
49+
phoneNumber
50+
);
51+
}
4652

47-
export const addEvent = (
48-
name: string,
53+
export function addEvent(
54+
eventName: string,
4955
currency: string,
5056
total: number,
5157
products: Product[]
52-
) => {
58+
): void {
5359
checkInitialization();
54-
ConvertedInSDKModule.addEvent(name, currency, total, products);
55-
};
60+
NativeModules.ConvertedInSDKModule.addEvent(
61+
eventName,
62+
currency,
63+
total,
64+
products
65+
);
66+
}
5667

5768
export const viewContentEvent = (
5869
currency: string,
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import * as RNConvertedInSdkModule from '../RNConvertedInSdkModule';
2+
3+
jest.mock('react-native', () => ({
4+
NativeModules: {
5+
ConvertedInSDKModule: {
6+
initializeSDK: jest.fn(),
7+
identifyUser: jest.fn(),
8+
addEvent: jest.fn(),
9+
viewContentEvent: jest.fn(),
10+
addToCartEvent: jest.fn(),
11+
initiateCheckoutEvent: jest.fn(),
12+
purchaseEvent: jest.fn(),
13+
registerEvent: jest.fn(),
14+
},
15+
},
16+
}));
17+
18+
describe('RNConvertedInSdkModule', () => {
19+
beforeEach(() => {
20+
jest.resetAllMocks();
21+
RNConvertedInSdkModule.__test__.resetInitialization();
22+
});
23+
24+
it('should throw an error when calling methods before initialization', () => {
25+
expect(() =>
26+
RNConvertedInSdkModule.identifyUser(
27+
'test@example.com',
28+
'+1',
29+
'1234567890'
30+
)
31+
).toThrow('SDK must be initialized before calling this method.');
32+
});
33+
34+
it('should throw an error when calling identifyUser before initialization', () => {
35+
expect(() =>
36+
RNConvertedInSdkModule.identifyUser(
37+
'test@example.com',
38+
'+1',
39+
'1234567890'
40+
)
41+
).toThrow('SDK must be initialized before calling this method.');
42+
});
43+
44+
it('should throw errors when calling other methods before initialization', () => {
45+
expect(() =>
46+
RNConvertedInSdkModule.addEvent('test_event', 'USD', 100, [])
47+
).toThrow('SDK must be initialized before calling this method.');
48+
expect(() =>
49+
RNConvertedInSdkModule.viewContentEvent('USD', 50, [])
50+
).toThrow('SDK must be initialized before calling this method.');
51+
expect(() => RNConvertedInSdkModule.addToCartEvent('USD', 75, [])).toThrow(
52+
'SDK must be initialized before calling this method.'
53+
);
54+
expect(() =>
55+
RNConvertedInSdkModule.initiateCheckoutEvent('USD', 150, [])
56+
).toThrow('SDK must be initialized before calling this method.');
57+
expect(() => RNConvertedInSdkModule.purchaseEvent('USD', 200, [])).toThrow(
58+
'SDK must be initialized before calling this method.'
59+
);
60+
expect(() => RNConvertedInSdkModule.registerEvent()).toThrow(
61+
'SDK must be initialized before calling this method.'
62+
);
63+
});
64+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// @ts-ignore
2+
import * as React from 'react';
3+
import { render } from '@testing-library/react-native';
4+
import { RNConvertInSDKProvider } from '../../src/context/RNConvertedInSdkProvider';
5+
import * as RNConvertedInSdkModule from '../../src/RNConvertedInSdkModule';
6+
7+
jest.mock('../../src/RNConvertedInSdkModule', () => ({
8+
initializeSDK: jest.fn(),
9+
}));
10+
11+
describe('RNConvertInSDKProvider', () => {
12+
it('should throw an error if pixelId or storeUrl is not provided', () => {
13+
console.error = jest.fn(); // Suppress console.error for this test
14+
expect(() =>
15+
render(
16+
<RNConvertInSDKProvider pixelId="" storeUrl="">
17+
<></>
18+
</RNConvertInSDKProvider>
19+
)
20+
).toThrow(
21+
'Both pixelId and storeUrl must be provided to initialize the SDK'
22+
);
23+
});
24+
25+
it('should initialize SDK on mount', () => {
26+
render(
27+
<RNConvertInSDKProvider
28+
pixelId="test-pixel-id"
29+
storeUrl="https://test-store.com"
30+
>
31+
<></>
32+
</RNConvertInSDKProvider>
33+
);
34+
35+
expect(RNConvertedInSdkModule.initializeSDK).toHaveBeenCalledWith({
36+
pixelId: 'test-pixel-id',
37+
storeUrl: 'https://test-store.com',
38+
});
39+
});
40+
});

0 commit comments

Comments
 (0)