@bytem/bytem-tracker-app 0.0.3 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +50 -25
  2. package/dist/src/BytemTracker.d.ts +58 -0
  3. package/dist/src/BytemTracker.js +474 -0
  4. package/dist/src/business/index.d.ts +4 -0
  5. package/dist/src/business/index.js +20 -0
  6. package/dist/src/business/trackCheckOutOrder.d.ts +2 -0
  7. package/dist/src/business/trackCheckOutOrder.js +20 -0
  8. package/dist/src/business/trackPayOrder.d.ts +2 -0
  9. package/dist/src/business/trackPayOrder.js +21 -0
  10. package/dist/src/business/trackUser.d.ts +2 -0
  11. package/dist/src/business/trackUser.js +13 -0
  12. package/dist/src/business/trackViewProduct.d.ts +2 -0
  13. package/dist/src/business/trackViewProduct.js +16 -0
  14. package/dist/src/core/base.d.ts +2 -0
  15. package/dist/src/core/base.js +24 -0
  16. package/dist/src/core/device.d.ts +3 -0
  17. package/dist/src/core/device.js +45 -0
  18. package/dist/src/core/request.d.ts +2 -0
  19. package/dist/src/core/request.js +21 -0
  20. package/dist/src/core/session.d.ts +17 -0
  21. package/dist/src/core/session.js +59 -0
  22. package/dist/src/core/storage.d.ts +9 -0
  23. package/dist/src/core/storage.js +41 -0
  24. package/dist/src/index.d.ts +3 -0
  25. package/dist/src/index.js +22 -0
  26. package/dist/src/types.d.ts +67 -0
  27. package/dist/src/types.js +12 -0
  28. package/dist/test/BytemTracker.test.d.ts +1 -0
  29. package/dist/test/BytemTracker.test.js +248 -0
  30. package/dist/test/debug.test.d.ts +1 -0
  31. package/dist/test/debug.test.js +90 -0
  32. package/dist/test/setup.d.ts +1 -0
  33. package/dist/test/setup.js +57 -0
  34. package/package.json +30 -11
  35. package/dist/env.d.ts +0 -1
  36. package/dist/env.js +0 -6
  37. package/dist/index.d.ts +0 -4
  38. package/dist/index.js +0 -6
  39. package/dist/tracker.d.ts +0 -26
  40. package/dist/tracker.js +0 -120
  41. package/dist/types.d.ts +0 -24
  42. package/dist/types.js +0 -2
  43. package/dist/uuid.d.ts +0 -1
  44. package/dist/uuid.js +0 -19
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackPayOrder = void 0;
4
+ const trackPayOrder = (order) => {
5
+ return {
6
+ event: 'pay_order',
7
+ params: {
8
+ order_id: order.orderId,
9
+ total: order.total,
10
+ currency: order.currency,
11
+ products: order.products.map((p) => ({
12
+ product_id: p.productId,
13
+ name: p.name,
14
+ price: p.price,
15
+ quantity: p.quantity,
16
+ })),
17
+ status: 'success',
18
+ },
19
+ };
20
+ };
21
+ exports.trackPayOrder = trackPayOrder;
@@ -0,0 +1,2 @@
1
+ import { EventPayload, UserTraits } from '../types';
2
+ export declare const trackUser: (userId: string, traits?: UserTraits) => EventPayload;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackUser = void 0;
4
+ const trackUser = (userId, traits = {}) => {
5
+ return {
6
+ event: 'identify',
7
+ params: {
8
+ user_id: userId,
9
+ traits: traits,
10
+ },
11
+ };
12
+ };
13
+ exports.trackUser = trackUser;
@@ -0,0 +1,2 @@
1
+ import { EventPayload, Product } from '../types';
2
+ export declare const trackViewProduct: (product: Product) => EventPayload;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackViewProduct = void 0;
4
+ const trackViewProduct = (product) => {
5
+ return {
6
+ event: 'view_product',
7
+ params: {
8
+ product_id: product.productId,
9
+ name: product.name,
10
+ price: product.price,
11
+ currency: product.currency,
12
+ category: product.category,
13
+ },
14
+ };
15
+ };
16
+ exports.trackViewProduct = trackViewProduct;
@@ -0,0 +1,2 @@
1
+ import { BaseParams } from '../types';
2
+ export declare const buildBaseRequestParams: (appId: string, visitorId: string) => Promise<BaseParams>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildBaseRequestParams = void 0;
4
+ const device_1 = require("./device");
5
+ const session_1 = require("./session");
6
+ const buildBaseRequestParams = async (appId, visitorId) => {
7
+ const deviceInfo = await (0, device_1.getDeviceInfo)();
8
+ const sessionInfo = (0, session_1.getSessionInfo)();
9
+ return {
10
+ app_id: appId,
11
+ device_id: visitorId,
12
+ session_id: sessionInfo ? sessionInfo.sessionId : '',
13
+ timestamp: Date.now(),
14
+ platform: deviceInfo.platform,
15
+ os: deviceInfo.os,
16
+ os_version: deviceInfo.osVersion,
17
+ model: deviceInfo.model,
18
+ language: deviceInfo.language,
19
+ screen_width: deviceInfo.screenWidth,
20
+ screen_height: deviceInfo.screenHeight,
21
+ user_agent: deviceInfo.userAgent,
22
+ };
23
+ };
24
+ exports.buildBaseRequestParams = buildBaseRequestParams;
@@ -0,0 +1,3 @@
1
+ import { DeviceInfo } from '../types';
2
+ export declare const getDeviceInfo: () => Promise<DeviceInfo>;
3
+ export declare const generateUUID: () => string;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateUUID = exports.getDeviceInfo = void 0;
7
+ const react_native_1 = require("react-native");
8
+ const react_native_device_info_1 = __importDefault(require("react-native-device-info"));
9
+ const getDeviceInfo = async () => {
10
+ var _a, _b, _c;
11
+ const os = react_native_device_info_1.default.getSystemName();
12
+ const osVersion = react_native_device_info_1.default.getSystemVersion();
13
+ const model = react_native_device_info_1.default.getModel();
14
+ const userAgent = await react_native_device_info_1.default.getUserAgent();
15
+ // Try to get language
16
+ let language = 'en';
17
+ if (react_native_1.Platform.OS === 'ios') {
18
+ const settings = (_a = react_native_1.NativeModules.SettingsManager) === null || _a === void 0 ? void 0 : _a.settings;
19
+ language = (settings === null || settings === void 0 ? void 0 : settings.AppleLocale) || ((_b = settings === null || settings === void 0 ? void 0 : settings.AppleLanguages) === null || _b === void 0 ? void 0 : _b[0]) || 'en';
20
+ }
21
+ else if (react_native_1.Platform.OS === 'android') {
22
+ language = ((_c = react_native_1.NativeModules.I18nManager) === null || _c === void 0 ? void 0 : _c.localeIdentifier) || 'en';
23
+ }
24
+ const { width, height } = react_native_1.Dimensions.get('window');
25
+ return {
26
+ platform: react_native_1.Platform.OS,
27
+ os,
28
+ osVersion,
29
+ model,
30
+ screenWidth: width,
31
+ screenHeight: height,
32
+ language,
33
+ userAgent,
34
+ };
35
+ };
36
+ exports.getDeviceInfo = getDeviceInfo;
37
+ const generateUUID = () => {
38
+ // Simple UUID v4 generator that doesn't require crypto polyfills
39
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
40
+ const r = (Math.random() * 16) | 0;
41
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
42
+ return v.toString(16);
43
+ });
44
+ };
45
+ exports.generateUUID = generateUUID;
@@ -0,0 +1,2 @@
1
+ import { EventPayload } from '../types';
2
+ export declare const sendRequest: (endpoint: string, data: EventPayload) => Promise<void>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sendRequest = void 0;
4
+ const sendRequest = async (endpoint, data) => {
5
+ try {
6
+ const response = await fetch(endpoint, {
7
+ method: 'POST',
8
+ headers: {
9
+ 'Content-Type': 'application/json',
10
+ },
11
+ body: JSON.stringify(data),
12
+ });
13
+ if (!response.ok) {
14
+ console.error(`[BytemTracker] Request failed: ${response.statusText}`);
15
+ }
16
+ }
17
+ catch (error) {
18
+ console.error('[BytemTracker] Network error:', error);
19
+ }
20
+ };
21
+ exports.sendRequest = sendRequest;
@@ -0,0 +1,17 @@
1
+ import { SessionInfo } from '../types';
2
+ /**
3
+ * Initialize session. Must be called before tracking events.
4
+ * 1. Checks memory (fastest)
5
+ * 2. Checks storage
6
+ * 3. Creates new if none exists
7
+ */
8
+ export declare const initSession: () => Promise<SessionInfo>;
9
+ /**
10
+ * Force start a new session
11
+ */
12
+ export declare const refreshSession: () => Promise<SessionInfo>;
13
+ /**
14
+ * Get current session info.
15
+ * WARNING: Returns null if initSession() hasn't been called/awaited.
16
+ */
17
+ export declare const getSessionInfo: () => SessionInfo | null;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSessionInfo = exports.refreshSession = exports.initSession = void 0;
4
+ const device_1 = require("./device");
5
+ const storage_1 = require("./storage");
6
+ let currentSession = null;
7
+ /**
8
+ * Initialize session. Must be called before tracking events.
9
+ * 1. Checks memory (fastest)
10
+ * 2. Checks storage
11
+ * 3. Creates new if none exists
12
+ */
13
+ const initSession = async () => {
14
+ if (currentSession) {
15
+ return currentSession;
16
+ }
17
+ try {
18
+ const storedId = await (0, storage_1.getItem)(storage_1.StorageKeys.SESSION_ID);
19
+ const storedStart = await (0, storage_1.getItem)(storage_1.StorageKeys.SESSION_START);
20
+ if (storedId && storedStart) {
21
+ currentSession = {
22
+ sessionId: storedId,
23
+ startTime: parseInt(storedStart, 10),
24
+ };
25
+ }
26
+ else {
27
+ await (0, exports.refreshSession)();
28
+ }
29
+ }
30
+ catch (e) {
31
+ // If error, generate new
32
+ await (0, exports.refreshSession)();
33
+ }
34
+ return currentSession;
35
+ };
36
+ exports.initSession = initSession;
37
+ /**
38
+ * Force start a new session
39
+ */
40
+ const refreshSession = async () => {
41
+ const newId = (0, device_1.generateUUID)();
42
+ const newStart = Date.now();
43
+ currentSession = {
44
+ sessionId: newId,
45
+ startTime: newStart,
46
+ };
47
+ await (0, storage_1.setItem)(storage_1.StorageKeys.SESSION_ID, newId);
48
+ await (0, storage_1.setItem)(storage_1.StorageKeys.SESSION_START, newStart.toString());
49
+ return currentSession;
50
+ };
51
+ exports.refreshSession = refreshSession;
52
+ /**
53
+ * Get current session info.
54
+ * WARNING: Returns null if initSession() hasn't been called/awaited.
55
+ */
56
+ const getSessionInfo = () => {
57
+ return currentSession;
58
+ };
59
+ exports.getSessionInfo = getSessionInfo;
@@ -0,0 +1,9 @@
1
+ export declare const StorageKeys: {
2
+ VISITOR_ID: string;
3
+ DEVICE_ID: string;
4
+ SESSION_ID: string;
5
+ SESSION_START: string;
6
+ };
7
+ export declare const getItem: (key: string) => Promise<string | null>;
8
+ export declare const setItem: (key: string, value: string) => Promise<void>;
9
+ export declare const removeItem: (key: string) => Promise<void>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.removeItem = exports.setItem = exports.getItem = exports.StorageKeys = void 0;
7
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
8
+ exports.StorageKeys = {
9
+ VISITOR_ID: 'bytem_visitor_id',
10
+ DEVICE_ID: 'bytem_device_id',
11
+ SESSION_ID: 'bytem_session_id',
12
+ SESSION_START: 'bytem_session_start',
13
+ };
14
+ const getItem = async (key) => {
15
+ try {
16
+ return await async_storage_1.default.getItem(key);
17
+ }
18
+ catch (e) {
19
+ console.warn('[BytemTracker] Storage get error:', e);
20
+ return null;
21
+ }
22
+ };
23
+ exports.getItem = getItem;
24
+ const setItem = async (key, value) => {
25
+ try {
26
+ await async_storage_1.default.setItem(key, value);
27
+ }
28
+ catch (e) {
29
+ console.warn('[BytemTracker] Storage set error:', e);
30
+ }
31
+ };
32
+ exports.setItem = setItem;
33
+ const removeItem = async (key) => {
34
+ try {
35
+ await async_storage_1.default.removeItem(key);
36
+ }
37
+ catch (e) {
38
+ console.warn('[BytemTracker] Storage remove error:', e);
39
+ }
40
+ };
41
+ exports.removeItem = removeItem;
@@ -0,0 +1,3 @@
1
+ import BytemTracker from './BytemTracker';
2
+ export * from './types';
3
+ export default BytemTracker;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ const BytemTracker_1 = __importDefault(require("./BytemTracker"));
21
+ __exportStar(require("./types"), exports);
22
+ exports.default = BytemTracker_1.default;
@@ -0,0 +1,67 @@
1
+ export interface TrackerConfig {
2
+ appId: string;
3
+ endpoint?: string;
4
+ path?: string;
5
+ debug?: boolean;
6
+ visitorId?: string;
7
+ deviceId?: string;
8
+ appScheme?: string;
9
+ }
10
+ export declare const BytemEventKeys: {
11
+ nps: string;
12
+ survey: string;
13
+ starRating: string;
14
+ view: string;
15
+ orientation: string;
16
+ pushAction: string;
17
+ action: string;
18
+ };
19
+ export interface DeviceInfo {
20
+ platform: string;
21
+ os: string;
22
+ osVersion: string;
23
+ model: string;
24
+ screenWidth: number;
25
+ screenHeight: number;
26
+ language: string;
27
+ userAgent: string;
28
+ }
29
+ export interface SessionInfo {
30
+ sessionId: string;
31
+ startTime: number;
32
+ }
33
+ export interface BaseParams {
34
+ app_id: string;
35
+ device_id: string;
36
+ session_id: string;
37
+ timestamp: number;
38
+ platform: string;
39
+ os: string;
40
+ os_version: string;
41
+ model: string;
42
+ language: string;
43
+ screen_width: number;
44
+ screen_height: number;
45
+ user_agent: string;
46
+ }
47
+ export interface UserTraits {
48
+ [key: string]: any;
49
+ }
50
+ export interface Product {
51
+ productId: string;
52
+ name?: string;
53
+ price?: number;
54
+ currency?: string;
55
+ category?: string;
56
+ quantity?: number;
57
+ }
58
+ export interface Order {
59
+ orderId: string;
60
+ total: number;
61
+ currency: string;
62
+ products: Product[];
63
+ }
64
+ export interface EventPayload {
65
+ event: string;
66
+ params: Record<string, any>;
67
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BytemEventKeys = void 0;
4
+ exports.BytemEventKeys = {
5
+ nps: "[CLY]_nps",
6
+ survey: "[CLY]_survey",
7
+ starRating: "[CLY]_star_rating",
8
+ view: "[CLY]_view",
9
+ orientation: "[CLY]_orientation",
10
+ pushAction: "[CLY]_push_action",
11
+ action: "[CLY]_action",
12
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,248 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const BytemTracker_1 = __importDefault(require("../src/BytemTracker"));
7
+ const storage_1 = require("../src/core/storage");
8
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
9
+ const types_1 = require("../src/types");
10
+ describe('BytemTracker SDK', () => {
11
+ const mockConfig = {
12
+ appId: 'test-app-id',
13
+ endpoint: 'https://api.example.com/track',
14
+ debug: true,
15
+ };
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+ // Reset singleton instance state manually
19
+ // @ts-ignore
20
+ BytemTracker_1.default.isInitialized = false;
21
+ // @ts-ignore
22
+ BytemTracker_1.default.appKey = null;
23
+ // @ts-ignore
24
+ BytemTracker_1.default.baseUrl = 'https://tracking.server.bytecon.com';
25
+ // @ts-ignore
26
+ BytemTracker_1.default.visitorId = null;
27
+ // @ts-ignore
28
+ BytemTracker_1.default.sessionStarted = false;
29
+ // @ts-ignore
30
+ BytemTracker_1.default.lastBeat = null;
31
+ // @ts-ignore
32
+ BytemTracker_1.default.trackTime = true;
33
+ // @ts-ignore
34
+ BytemTracker_1.default.storedDuration = 0;
35
+ // @ts-ignore
36
+ BytemTracker_1.default.lastViewTime = 0;
37
+ // @ts-ignore
38
+ BytemTracker_1.default.lastViewStoredDuration = 0;
39
+ // Clear storage mocks
40
+ // @ts-ignore
41
+ async_storage_1.default.clear();
42
+ });
43
+ describe('Initialization', () => {
44
+ it('should initialize correctly', async () => {
45
+ await BytemTracker_1.default.init(mockConfig);
46
+ // Verify Storage was checked for Visitor ID
47
+ expect(async_storage_1.default.getItem).toHaveBeenCalledWith(storage_1.StorageKeys.VISITOR_ID);
48
+ // Verify Session was initialized (checks storage)
49
+ expect(async_storage_1.default.getItem).toHaveBeenCalledWith(`${mockConfig.appId}/cly_session`);
50
+ });
51
+ it('should use default endpoint when not provided', async () => {
52
+ await BytemTracker_1.default.init({
53
+ appId: 'test-app-id',
54
+ // endpoint is omitted
55
+ debug: true,
56
+ });
57
+ await BytemTracker_1.default.trackEvent('test_event', {});
58
+ expect(global.fetch).toHaveBeenCalled();
59
+ const calls = global.fetch.mock.calls;
60
+ // Check any call, they should all use the default endpoint
61
+ const url = calls[0][0];
62
+ expect(url).toBe('https://tracking.server.bytecon.com/i');
63
+ });
64
+ it('should override endpoint path when path is configured', async () => {
65
+ await BytemTracker_1.default.init({
66
+ appId: 'test-app-id-2',
67
+ endpoint: 'https://api.example.com/track',
68
+ path: '/collect',
69
+ debug: true,
70
+ });
71
+ await BytemTracker_1.default.trackEvent('test_event', {});
72
+ expect(global.fetch).toHaveBeenCalled();
73
+ const calls = global.fetch.mock.calls;
74
+ const url = calls[0][0];
75
+ expect(url).toBe('https://api.example.com/track/collect');
76
+ });
77
+ });
78
+ describe('Event Tracking', () => {
79
+ beforeEach(async () => {
80
+ await BytemTracker_1.default.init(mockConfig);
81
+ });
82
+ it('should track a simple event', async () => {
83
+ const eventName = 'test_event';
84
+ const segmentation = { foo: 'bar' };
85
+ await BytemTracker_1.default.trackEvent(eventName, segmentation);
86
+ expect(global.fetch).toHaveBeenCalled();
87
+ const calls = global.fetch.mock.calls;
88
+ const eventCall = calls.find(call => call[1].body && call[1].body.includes('events='));
89
+ expect(eventCall).toBeDefined();
90
+ const options = eventCall[1];
91
+ const body = options.body;
92
+ expect(body).toContain(`app_key=${mockConfig.appId}`);
93
+ expect(body).toContain('events=%5B%7B%22key%22%3A%22test_event%22');
94
+ });
95
+ it('should track event with count and sum', async () => {
96
+ const eventName = 'purchase';
97
+ const count = 3;
98
+ const sum = 99.99;
99
+ const segmentation = { item: 'apple' };
100
+ await BytemTracker_1.default.trackEvent(eventName, segmentation, count, sum);
101
+ const calls = global.fetch.mock.calls;
102
+ const eventCall = calls.find(call => call[1].body && call[1].body.includes('events='));
103
+ const body = decodeURIComponent(eventCall[1].body);
104
+ const events = JSON.parse(new URLSearchParams(eventCall[1].body).get('events') || '[]');
105
+ expect(events[0]).toMatchObject({
106
+ key: eventName,
107
+ count: count,
108
+ sum: sum,
109
+ segmentation: segmentation
110
+ });
111
+ });
112
+ it('should track event with duration', async () => {
113
+ const eventName = 'video_watched';
114
+ const duration = 120; // seconds
115
+ await BytemTracker_1.default.trackEvent(eventName, {}, 1, undefined, duration);
116
+ const calls = global.fetch.mock.calls;
117
+ const eventCall = calls.find(call => call[1].body && call[1].body.includes('events='));
118
+ const events = JSON.parse(new URLSearchParams(eventCall[1].body).get('events') || '[]');
119
+ expect(events[0].dur).toBe(duration);
120
+ });
121
+ });
122
+ describe('Page View Tracking', () => {
123
+ beforeEach(async () => {
124
+ await BytemTracker_1.default.init(mockConfig);
125
+ });
126
+ it('should track page view', async () => {
127
+ const pageName = '/home';
128
+ await BytemTracker_1.default.trackPageview(pageName);
129
+ const calls = global.fetch.mock.calls;
130
+ const eventCall = calls.find(call => call[1].body && call[1].body.includes(`%22key%22%3A%22${encodeURIComponent(types_1.BytemEventKeys.view)}%22`));
131
+ expect(eventCall).toBeDefined();
132
+ const events = JSON.parse(new URLSearchParams(eventCall[1].body).get('events') || '[]');
133
+ expect(events[0].segmentation).toMatchObject({
134
+ name: pageName,
135
+ current: pageName,
136
+ referrer: ''
137
+ });
138
+ });
139
+ it('should track page view with referrer', async () => {
140
+ const pageName = '/product/123';
141
+ const referrer = '/home';
142
+ await BytemTracker_1.default.trackPageview(pageName, referrer);
143
+ const calls = global.fetch.mock.calls;
144
+ // Get the last call which should be the page view
145
+ const eventCall = calls[calls.length - 1];
146
+ const events = JSON.parse(new URLSearchParams(eventCall[1].body).get('events') || '[]');
147
+ expect(events[0].segmentation).toMatchObject({
148
+ name: pageName,
149
+ current: pageName,
150
+ referrer: referrer
151
+ });
152
+ });
153
+ });
154
+ describe('User Tracking', () => {
155
+ beforeEach(async () => {
156
+ await BytemTracker_1.default.init(mockConfig);
157
+ });
158
+ it('should track user details', async () => {
159
+ const userId = 'user_123';
160
+ const userTraits = {
161
+ name: 'John Doe',
162
+ email: 'john@example.com',
163
+ custom: {
164
+ plan: 'premium'
165
+ }
166
+ };
167
+ await BytemTracker_1.default.trackUser(userId, userTraits);
168
+ const calls = global.fetch.mock.calls;
169
+ const userCall = calls.find(call => call[1].body && call[1].body.includes('user_details='));
170
+ expect(userCall).toBeDefined();
171
+ const userDetails = JSON.parse(new URLSearchParams(userCall[1].body).get('user_details') || '{}');
172
+ expect(userDetails).toMatchObject({
173
+ name: 'John Doe',
174
+ email: 'john@example.com',
175
+ custom: {
176
+ plan: 'premium',
177
+ user_id: userId
178
+ }
179
+ });
180
+ });
181
+ });
182
+ describe('Session Management', () => {
183
+ beforeEach(async () => {
184
+ await BytemTracker_1.default.init(mockConfig);
185
+ });
186
+ it('should track session start', async () => {
187
+ await BytemTracker_1.default.trackSessions();
188
+ const calls = global.fetch.mock.calls;
189
+ const sessionCall = calls.find(call => call[1].body && call[1].body.includes('begin_session=1'));
190
+ expect(sessionCall).toBeDefined();
191
+ });
192
+ it('should track session end', async () => {
193
+ // First start session
194
+ await BytemTracker_1.default.trackSessions();
195
+ // Then end session
196
+ await BytemTracker_1.default.endSession(undefined, true); // force=true to bypass cookie check for test
197
+ const calls = global.fetch.mock.calls;
198
+ const endSessionCall = calls.find(call => call[1].body && call[1].body.includes('end_session=1'));
199
+ expect(endSessionCall).toBeDefined();
200
+ const body = new URLSearchParams(endSessionCall[1].body);
201
+ expect(body.has('session_duration')).toBe(true);
202
+ });
203
+ });
204
+ describe('E-commerce Tracking', () => {
205
+ beforeEach(async () => {
206
+ await BytemTracker_1.default.init(mockConfig);
207
+ });
208
+ it('should track checkout order', async () => {
209
+ const order = {
210
+ orderId: 'order_123',
211
+ total: 99.99,
212
+ currency: 'USD',
213
+ products: [
214
+ { productId: 'prod_1', price: 50, quantity: 1 },
215
+ { productId: 'prod_2', price: 49.99, quantity: 1 }
216
+ ]
217
+ };
218
+ await BytemTracker_1.default.trackCheckOutOrder(order);
219
+ const calls = global.fetch.mock.calls;
220
+ const eventCall = calls.find(call => call[1].body && call[1].body.includes('events='));
221
+ const events = JSON.parse(new URLSearchParams(eventCall[1].body).get('events') || '[]');
222
+ expect(events[0].key).toBe('check_out_order');
223
+ expect(events[0].segmentation).toMatchObject({
224
+ order_id: 'order_123',
225
+ total: 99.99,
226
+ currency: 'USD'
227
+ });
228
+ });
229
+ it('should track pay order', async () => {
230
+ const order = {
231
+ orderId: 'order_123',
232
+ total: 99.99,
233
+ currency: 'USD',
234
+ products: []
235
+ };
236
+ await BytemTracker_1.default.trackPayOrder(order);
237
+ const calls = global.fetch.mock.calls;
238
+ const eventCall = calls.find(call => call[1].body && call[1].body.includes('events='));
239
+ const events = JSON.parse(new URLSearchParams(eventCall[1].body).get('events') || '[]');
240
+ expect(events[0].key).toBe('pay_order');
241
+ expect(events[0].segmentation).toMatchObject({
242
+ order_id: 'order_123',
243
+ total: 99.99,
244
+ currency: 'USD'
245
+ });
246
+ });
247
+ });
248
+ });
@@ -0,0 +1 @@
1
+ export {};