@jmartins-tillster/analytics-react-native 2.21.3
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.
- package/.editorconfig +10 -0
- package/.eslintignore +11 -0
- package/.eslintrc.js +60 -0
- package/.gitattributes +4 -0
- package/.github/ISSUE_TEMPLATE/BUG_REPORT.md +23 -0
- package/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +18 -0
- package/.github/workflows/ci.yml +158 -0
- package/.github/workflows/create_jira.yml +41 -0
- package/.github/workflows/publish.yml +57 -0
- package/.husky/commit-msg +4 -0
- package/.husky/pre-commit +4 -0
- package/.prettierrc.js +7 -0
- package/.yarnrc.yml +2 -0
- package/CODE_OF_CONDUCT.md +84 -0
- package/CONTRIBUTING.md +141 -0
- package/LICENSE +21 -0
- package/MIGRATION_GUIDE.md +185 -0
- package/README.md +944 -0
- package/babel.config.js +3 -0
- package/commitlint.config.js +13 -0
- package/examples/AnalyticsReactNativeExample/.bundle/config +2 -0
- package/examples/AnalyticsReactNativeExample/.eslintrc.js +4 -0
- package/examples/AnalyticsReactNativeExample/.prettierrc.js +7 -0
- package/examples/AnalyticsReactNativeExample/.watchmanconfig +1 -0
- package/examples/AnalyticsReactNativeExample/App.tsx +164 -0
- package/examples/AnalyticsReactNativeExample/Gemfile +9 -0
- package/examples/AnalyticsReactNativeExample/Gemfile.lock +106 -0
- package/examples/AnalyticsReactNativeExample/Home.tsx +206 -0
- package/examples/AnalyticsReactNativeExample/Modal.tsx +25 -0
- package/examples/AnalyticsReactNativeExample/README.md +79 -0
- package/examples/AnalyticsReactNativeExample/SecondPage.tsx +25 -0
- package/examples/AnalyticsReactNativeExample/android/app/build.gradle +119 -0
- package/examples/AnalyticsReactNativeExample/android/app/debug.keystore +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/proguard-rules.pro +10 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/debug/AndroidManifest.xml +9 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/AndroidManifest.xml +26 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/java/com/analyticsreactnativeexample/MainActivity.kt +22 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/java/com/analyticsreactnativeexample/MainApplication.kt +44 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/drawable/rn_edit_text_material.xml +37 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/values/strings.xml +3 -0
- package/examples/AnalyticsReactNativeExample/android/app/src/main/res/values/styles.xml +9 -0
- package/examples/AnalyticsReactNativeExample/android/build.gradle +21 -0
- package/examples/AnalyticsReactNativeExample/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/AnalyticsReactNativeExample/android/gradle/wrapper/gradle-wrapper.properties +8 -0
- package/examples/AnalyticsReactNativeExample/android/gradle.properties +39 -0
- package/examples/AnalyticsReactNativeExample/android/gradlew +252 -0
- package/examples/AnalyticsReactNativeExample/android/gradlew.bat +94 -0
- package/examples/AnalyticsReactNativeExample/android/settings.gradle +6 -0
- package/examples/AnalyticsReactNativeExample/app.json +4 -0
- package/examples/AnalyticsReactNativeExample/babel.config.js +3 -0
- package/examples/AnalyticsReactNativeExample/index.js +9 -0
- package/examples/AnalyticsReactNativeExample/ios/.xcode.env +11 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/AppDelegate.h +6 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/AppDelegate.mm +31 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/Images.xcassets/Contents.json +6 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/Info.plist +52 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/LaunchScreen.storyboard +47 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/PrivacyInfo.xcprivacy +37 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample/main.m +10 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample.xcodeproj/project.pbxproj +705 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample.xcodeproj/xcshareddata/xcschemes/AnalyticsReactNativeExample.xcscheme +88 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample.xcworkspace/contents.xcworkspacedata +10 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExampleTests/AnalyticsReactNativeExampleTests.m +66 -0
- package/examples/AnalyticsReactNativeExample/ios/AnalyticsReactNativeExampleTests/Info.plist +24 -0
- package/examples/AnalyticsReactNativeExample/ios/Podfile +40 -0
- package/examples/AnalyticsReactNativeExample/ios/Podfile.lock +1961 -0
- package/examples/AnalyticsReactNativeExample/metro.config.js +11 -0
- package/examples/AnalyticsReactNativeExample/package.json +55 -0
- package/examples/AnalyticsReactNativeExample/plugins/ConsentManager.tsx +75 -0
- package/examples/AnalyticsReactNativeExample/plugins/Logger.ts +16 -0
- package/examples/AnalyticsReactNativeExample/react-native.config.js +9 -0
- package/examples/AnalyticsReactNativeExample/tsconfig.json +3 -0
- package/examples/AnalyticsReactNativeExample/workspace.js +5 -0
- package/examples/AnalyticsReactNativeExample/yarn.lock +11110 -0
- package/examples/E2E/.bundle/config +2 -0
- package/examples/E2E/.detoxrc.js +96 -0
- package/examples/E2E/.mise.toml +5 -0
- package/examples/E2E/.prettierrc.js +7 -0
- package/examples/E2E/.watchmanconfig +1 -0
- package/examples/E2E/App.tsx +176 -0
- package/examples/E2E/Gemfile +7 -0
- package/examples/E2E/Gemfile.lock +101 -0
- package/examples/E2E/Home.tsx +206 -0
- package/examples/E2E/Modal.tsx +25 -0
- package/examples/E2E/README.md +79 -0
- package/examples/E2E/SecondPage.tsx +25 -0
- package/examples/E2E/android/app/build.gradle +128 -0
- package/examples/E2E/android/app/debug.keystore +0 -0
- package/examples/E2E/android/app/proguard-rules.pro +10 -0
- package/examples/E2E/android/app/src/androidTest/java/com/segmentanalyticsreactnativeexample/DetoxTest.java +29 -0
- package/examples/E2E/android/app/src/debug/AndroidManifest.xml +13 -0
- package/examples/E2E/android/app/src/debug/java/com/analyticsreactnativeexample/ReactNativeFlipper.java +75 -0
- package/examples/E2E/android/app/src/main/AndroidManifest.xml +26 -0
- package/examples/E2E/android/app/src/main/java/com/analyticsreactnativeexample/MainActivity.java +32 -0
- package/examples/E2E/android/app/src/main/java/com/analyticsreactnativeexample/MainApplication.java +62 -0
- package/examples/E2E/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
- package/examples/E2E/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/examples/E2E/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/examples/E2E/android/app/src/main/res/values/strings.xml +3 -0
- package/examples/E2E/android/app/src/main/res/values/styles.xml +9 -0
- package/examples/E2E/android/app/src/main/res/xml/network_security_config.xml +7 -0
- package/examples/E2E/android/app/src/release/java/com/analyticsreactnativeexample/ReactNativeFlipper.java +20 -0
- package/examples/E2E/android/build.gradle +37 -0
- package/examples/E2E/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/E2E/android/gradle/wrapper/gradle-wrapper.properties +6 -0
- package/examples/E2E/android/gradle.properties +44 -0
- package/examples/E2E/android/gradlew +244 -0
- package/examples/E2E/android/gradlew.bat +92 -0
- package/examples/E2E/android/settings.gradle +4 -0
- package/examples/E2E/app.json +4 -0
- package/examples/E2E/babel.config.js +14 -0
- package/examples/E2E/e2e/jest.config.js +13 -0
- package/examples/E2E/e2e/main.e2e.js +192 -0
- package/examples/E2E/e2e/matchers.js +34 -0
- package/examples/E2E/e2e/mockServer.js +56 -0
- package/examples/E2E/index.js +11 -0
- package/examples/E2E/ios/.xcode.env +11 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/AppDelegate.h +6 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/AppDelegate.mm +26 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/Images.xcassets/Contents.json +6 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/Info.plist +55 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/LaunchScreen.storyboard +47 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E/main.m +10 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E.xcodeproj/project.pbxproj +721 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E.xcworkspace/contents.xcworkspacedata +10 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2E.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2ETests/AnalyticsReactNativeExampleTests.m +66 -0
- package/examples/E2E/ios/AnalyticsReactNativeE2ETests/Info.plist +24 -0
- package/examples/E2E/ios/Podfile +62 -0
- package/examples/E2E/ios/Podfile.lock +763 -0
- package/examples/E2E/jest.config.js +3 -0
- package/examples/E2E/metro.config.js +45 -0
- package/examples/E2E/package.json +65 -0
- package/examples/E2E/plugins/Logger.ts +16 -0
- package/examples/E2E/react-native.config.js +7 -0
- package/examples/E2E/workspace.js +5 -0
- package/examples/E2E/yarn.lock +10898 -0
- package/examples/E2E-73/.bundle/config +2 -0
- package/examples/E2E-73/.detoxrc.js +96 -0
- package/examples/E2E-73/.eslintrc.js +4 -0
- package/examples/E2E-73/.mise.toml +5 -0
- package/examples/E2E-73/.prettierrc.js +7 -0
- package/examples/E2E-73/.watchmanconfig +1 -0
- package/examples/E2E-73/App.tsx +175 -0
- package/examples/E2E-73/Gemfile +7 -0
- package/examples/E2E-73/Gemfile.lock +99 -0
- package/examples/E2E-73/Home.tsx +206 -0
- package/examples/E2E-73/Modal.tsx +25 -0
- package/examples/E2E-73/README.md +79 -0
- package/examples/E2E-73/SecondPage.tsx +25 -0
- package/examples/E2E-73/android/app/build.gradle +119 -0
- package/examples/E2E-73/android/app/debug.keystore +0 -0
- package/examples/E2E-73/android/app/proguard-rules.pro +10 -0
- package/examples/E2E-73/android/app/src/debug/AndroidManifest.xml +9 -0
- package/examples/E2E-73/android/app/src/main/AndroidManifest.xml +25 -0
- package/examples/E2E-73/android/app/src/main/java/com/analyticsreactnativee2e73/MainActivity.kt +22 -0
- package/examples/E2E-73/android/app/src/main/java/com/analyticsreactnativee2e73/MainApplication.kt +45 -0
- package/examples/E2E-73/android/app/src/main/res/drawable/rn_edit_text_material.xml +36 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
- package/examples/E2E-73/android/app/src/main/res/values/strings.xml +3 -0
- package/examples/E2E-73/android/app/src/main/res/values/styles.xml +9 -0
- package/examples/E2E-73/android/build.gradle +21 -0
- package/examples/E2E-73/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/examples/E2E-73/android/gradle/wrapper/gradle-wrapper.properties +7 -0
- package/examples/E2E-73/android/gradle.properties +41 -0
- package/examples/E2E-73/android/gradlew +249 -0
- package/examples/E2E-73/android/gradlew.bat +92 -0
- package/examples/E2E-73/android/settings.gradle +4 -0
- package/examples/E2E-73/app.json +4 -0
- package/examples/E2E-73/babel.config.js +14 -0
- package/examples/E2E-73/e2e/jest.config.js +13 -0
- package/examples/E2E-73/e2e/main.e2e.js +192 -0
- package/examples/E2E-73/e2e/matchers.js +34 -0
- package/examples/E2E-73/e2e/mockServer.js +56 -0
- package/examples/E2E-73/index.js +9 -0
- package/examples/E2E-73/ios/.xcode.env +11 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/AppDelegate.h +6 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/AppDelegate.mm +31 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/Images.xcassets/AppIcon.appiconset/Contents.json +53 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/Images.xcassets/Contents.json +6 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/Info.plist +51 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/LaunchScreen.storyboard +47 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73/main.m +10 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcodeproj/project.pbxproj +700 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcodeproj/xcshareddata/xcschemes/AnalyticsReactNativeE2E73.xcscheme +88 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73.xcworkspace/contents.xcworkspacedata +10 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73Tests/AnalyticsReactNativeE2E73Tests.m +66 -0
- package/examples/E2E-73/ios/AnalyticsReactNativeE2E73Tests/Info.plist +24 -0
- package/examples/E2E-73/ios/Podfile +55 -0
- package/examples/E2E-73/ios/Podfile.lock +1425 -0
- package/examples/E2E-73/jest.config.js +3 -0
- package/examples/E2E-73/metro.config.js +45 -0
- package/examples/E2E-73/package.json +62 -0
- package/examples/E2E-73/plugins/Logger.ts +16 -0
- package/examples/E2E-73/react-native.config.js +12 -0
- package/examples/E2E-73/tsconfig.json +3 -0
- package/examples/E2E-73/workspace.js +5 -0
- package/examples/E2E-73/yarn.lock +10789 -0
- package/examples/README.md +142 -0
- package/examples/linkHelper.js +45 -0
- package/jest.config.js +7 -0
- package/multi-release.config.js +7 -0
- package/package.json +79 -0
- package/packages/core/CHANGELOG.md +274 -0
- package/packages/core/LICENSE +21 -0
- package/packages/core/README.md +3 -0
- package/packages/core/android/build.gradle +96 -0
- package/packages/core/android/gradle.properties +5 -0
- package/packages/core/android/src/main/AndroidManifest.xml +4 -0
- package/packages/core/android/src/main/AndroidManifestNew.xml +3 -0
- package/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativeModule.kt +265 -0
- package/packages/core/android/src/main/java/com/segmentanalyticsreactnative/AnalyticsReactNativePackage.kt +43 -0
- package/packages/core/babel.config.js +3 -0
- package/packages/core/constants-generator.js +17 -0
- package/packages/core/ios/AnalyticsReactNative-Bridging-Header.h +1 -0
- package/packages/core/ios/AnalyticsReactNative.m +7 -0
- package/packages/core/ios/AnalyticsReactNative.swift +142 -0
- package/packages/core/ios/AnalyticsReactNative.xcodeproj/project.pbxproj +281 -0
- package/packages/core/ios/PrivacyInfo.xcprivacy +70 -0
- package/packages/core/jest.config.js +5 -0
- package/packages/core/package.json +85 -0
- package/packages/core/release.config.js +3 -0
- package/packages/core/segment-analytics-react-native.podspec +35 -0
- package/packages/core/src/__tests__/__snapshots__/mapTransform.test.ts.snap +20 -0
- package/packages/core/src/__tests__/analytics.test.ts +252 -0
- package/packages/core/src/__tests__/api.test.ts +91 -0
- package/packages/core/src/__tests__/client.test.ts +27 -0
- package/packages/core/src/__tests__/context.test.ts +122 -0
- package/packages/core/src/__tests__/events.test.ts +234 -0
- package/packages/core/src/__tests__/internal/checkInstalledVersion.test.ts +221 -0
- package/packages/core/src/__tests__/internal/fetchSettings.test.ts +438 -0
- package/packages/core/src/__tests__/internal/handleAppStateChange.test.ts +178 -0
- package/packages/core/src/__tests__/internal/trackDeepLinks.test.ts +121 -0
- package/packages/core/src/__tests__/logger.test.ts +83 -0
- package/packages/core/src/__tests__/mapTransform.test.ts +63 -0
- package/packages/core/src/__tests__/methods/alias.test.ts +89 -0
- package/packages/core/src/__tests__/methods/flush.test.ts +120 -0
- package/packages/core/src/__tests__/methods/group.test.ts +48 -0
- package/packages/core/src/__tests__/methods/identify.test.ts +132 -0
- package/packages/core/src/__tests__/methods/process.test.ts +162 -0
- package/packages/core/src/__tests__/methods/screen.test.ts +48 -0
- package/packages/core/src/__tests__/methods/track.test.ts +49 -0
- package/packages/core/src/__tests__/timeline.test.ts +130 -0
- package/packages/core/src/__tests__/util.test.ts +200 -0
- package/packages/core/src/analytics.ts +1030 -0
- package/packages/core/src/api.ts +23 -0
- package/packages/core/src/client.tsx +76 -0
- package/packages/core/src/constants.ts +18 -0
- package/packages/core/src/context.ts +107 -0
- package/packages/core/src/errors.ts +123 -0
- package/packages/core/src/events.ts +75 -0
- package/packages/core/src/flushPolicies/__tests__/background-flush-policy.test.ts +39 -0
- package/packages/core/src/flushPolicies/__tests__/count-flush-policy.test.ts +28 -0
- package/packages/core/src/flushPolicies/__tests__/flush-policy-executer.test.ts +85 -0
- package/packages/core/src/flushPolicies/__tests__/timer-flush-policy.test.ts +30 -0
- package/packages/core/src/flushPolicies/background-flush-policy.ts +21 -0
- package/packages/core/src/flushPolicies/count-flush-policy.ts +31 -0
- package/packages/core/src/flushPolicies/flush-policy-executer.ts +100 -0
- package/packages/core/src/flushPolicies/index.ts +5 -0
- package/packages/core/src/flushPolicies/startup-flush-policy.ts +20 -0
- package/packages/core/src/flushPolicies/timer-flush-policy.ts +45 -0
- package/packages/core/src/flushPolicies/types.ts +93 -0
- package/packages/core/src/index.ts +28 -0
- package/packages/core/src/logger.ts +37 -0
- package/packages/core/src/mapTransform.ts +48 -0
- package/packages/core/src/plugin.ts +212 -0
- package/packages/core/src/plugins/ConsentPlugin.ts +200 -0
- package/packages/core/src/plugins/DestinationMetadataEnrichment.ts +67 -0
- package/packages/core/src/plugins/QueueFlushingPlugin.ts +149 -0
- package/packages/core/src/plugins/SegmentDestination.ts +153 -0
- package/packages/core/src/plugins/__tests__/QueueFlushingPlugin.test.ts +143 -0
- package/packages/core/src/plugins/__tests__/SegmentDestination.test.ts +549 -0
- package/packages/core/src/plugins/__tests__/consent/consentNotEnabledAtSegment.test.ts +123 -0
- package/packages/core/src/plugins/__tests__/consent/destinationMultipleCategories.test.ts +147 -0
- package/packages/core/src/plugins/__tests__/consent/idfa.test.ts +101 -0
- package/packages/core/src/plugins/__tests__/consent/mockSettings/ConsentNotEnabledAtSegment.json +70 -0
- package/packages/core/src/plugins/__tests__/consent/mockSettings/DestinationsMultipleCategories.json +73 -0
- package/packages/core/src/plugins/__tests__/consent/mockSettings/NoUnmappedDestinations.json +105 -0
- package/packages/core/src/plugins/__tests__/consent/mockSettings/UnmappedDestinations.json +101 -0
- package/packages/core/src/plugins/__tests__/consent/noUnmapped.test.ts +234 -0
- package/packages/core/src/plugins/__tests__/consent/unmapped.test.ts +265 -0
- package/packages/core/src/plugins/__tests__/consent/utils.ts +75 -0
- package/packages/core/src/storage/__tests__/sovranStorage.test.ts +153 -0
- package/packages/core/src/storage/helpers.ts +25 -0
- package/packages/core/src/storage/index.ts +3 -0
- package/packages/core/src/storage/sovranStorage.ts +558 -0
- package/packages/core/src/storage/types.ts +106 -0
- package/packages/core/src/test-helpers/index.ts +7 -0
- package/packages/core/src/test-helpers/mockDestinationPlugin.ts +7 -0
- package/packages/core/src/test-helpers/mockEventStore.ts +35 -0
- package/packages/core/src/test-helpers/mockLogger.ts +10 -0
- package/packages/core/src/test-helpers/mockSegmentStore.ts +266 -0
- package/packages/core/src/test-helpers/mockTimeline.ts +9 -0
- package/packages/core/src/test-helpers/setupSegmentClient.ts +59 -0
- package/packages/core/src/test-helpers/utils.ts +23 -0
- package/packages/core/src/timeline.ts +136 -0
- package/packages/core/src/types.ts +392 -0
- package/packages/core/src/util.ts +288 -0
- package/packages/core/src/uuid.ts +7 -0
- package/packages/core/tsconfig.json +10 -0
- package/packages/core/tsconfig.linter.json +11 -0
- package/packages/plugins/plugin-adjust/CHANGELOG.md +60 -0
- package/packages/plugins/plugin-adjust/LICENSE +21 -0
- package/packages/plugins/plugin-adjust/README.md +72 -0
- package/packages/plugins/plugin-adjust/babel.config.js +3 -0
- package/packages/plugins/plugin-adjust/jest.config.js +5 -0
- package/packages/plugins/plugin-adjust/package.json +72 -0
- package/packages/plugins/plugin-adjust/release.config.js +3 -0
- package/packages/plugins/plugin-adjust/src/AdjustPlugin.tsx +92 -0
- package/packages/plugins/plugin-adjust/src/index.ts +1 -0
- package/packages/plugins/plugin-adjust/src/methods/__mocks__/react-native-adjust.ts +19 -0
- package/packages/plugins/plugin-adjust/src/methods/__tests__/identify.test.ts +34 -0
- package/packages/plugins/plugin-adjust/src/methods/__tests__/reset.test.ts +15 -0
- package/packages/plugins/plugin-adjust/src/methods/__tests__/track.test.ts +148 -0
- package/packages/plugins/plugin-adjust/src/methods/identify.ts +14 -0
- package/packages/plugins/plugin-adjust/src/methods/reset.ts +9 -0
- package/packages/plugins/plugin-adjust/src/methods/track.ts +47 -0
- package/packages/plugins/plugin-adjust/src/util.ts +29 -0
- package/packages/plugins/plugin-adjust/tsconfig.json +10 -0
- package/packages/plugins/plugin-advertising-id/CHANGELOG.md +101 -0
- package/packages/plugins/plugin-advertising-id/CONTRIBUTING.md +196 -0
- package/packages/plugins/plugin-advertising-id/LICENSE +21 -0
- package/packages/plugins/plugin-advertising-id/README.md +40 -0
- package/packages/plugins/plugin-advertising-id/android/build.gradle +96 -0
- package/packages/plugins/plugin-advertising-id/android/gradle.properties +5 -0
- package/packages/plugins/plugin-advertising-id/android/src/main/AndroidManifest.xml +4 -0
- package/packages/plugins/plugin-advertising-id/android/src/main/AndroidManifestNew.xml +3 -0
- package/packages/plugins/plugin-advertising-id/android/src/main/java/com/reactnativeanalyticsreactnativepluginadvertisingid/AnalyticsReactNativePluginAdvertisingIdModule.kt +64 -0
- package/packages/plugins/plugin-advertising-id/android/src/main/java/com/reactnativeanalyticsreactnativepluginadvertisingid/AnalyticsReactNativePluginAdvertisingIdPackage.kt +17 -0
- package/packages/plugins/plugin-advertising-id/babel.config.js +3 -0
- package/packages/plugins/plugin-advertising-id/jest.config.js +5 -0
- package/packages/plugins/plugin-advertising-id/package.json +70 -0
- package/packages/plugins/plugin-advertising-id/release.config.js +3 -0
- package/packages/plugins/plugin-advertising-id/src/AdvertisingIdPlugin.ts +172 -0
- package/packages/plugins/plugin-advertising-id/src/index.ts +1 -0
- package/packages/plugins/plugin-advertising-id/src/types.ts +3 -0
- package/packages/plugins/plugin-advertising-id/tsconfig.json +10 -0
- package/packages/plugins/plugin-amplitudeSession/CHANGELOG.md +46 -0
- package/packages/plugins/plugin-amplitudeSession/LICENSE +21 -0
- package/packages/plugins/plugin-amplitudeSession/README.md +66 -0
- package/packages/plugins/plugin-amplitudeSession/babel.config.js +3 -0
- package/packages/plugins/plugin-amplitudeSession/jest.config.js +5 -0
- package/packages/plugins/plugin-amplitudeSession/package.json +71 -0
- package/packages/plugins/plugin-amplitudeSession/release.config.js +3 -0
- package/packages/plugins/plugin-amplitudeSession/src/AmplitudeSessionPlugin.tsx +327 -0
- package/packages/plugins/plugin-amplitudeSession/src/__tests__/AmplitudeSessionPlugin.test.ts +769 -0
- package/packages/plugins/plugin-amplitudeSession/src/index.ts +1 -0
- package/packages/plugins/plugin-amplitudeSession/tsconfig.json +10 -0
- package/packages/plugins/plugin-appsflyer/CHANGELOG.md +64 -0
- package/packages/plugins/plugin-appsflyer/LICENSE +21 -0
- package/packages/plugins/plugin-appsflyer/README.md +90 -0
- package/packages/plugins/plugin-appsflyer/babel.config.js +3 -0
- package/packages/plugins/plugin-appsflyer/jest.config.js +5 -0
- package/packages/plugins/plugin-appsflyer/package.json +74 -0
- package/packages/plugins/plugin-appsflyer/release.config.js +3 -0
- package/packages/plugins/plugin-appsflyer/src/AppsflyerPlugin.tsx +218 -0
- package/packages/plugins/plugin-appsflyer/src/__tests__/AppsflyerPlugin.test.ts +20 -0
- package/packages/plugins/plugin-appsflyer/src/index.ts +1 -0
- package/packages/plugins/plugin-appsflyer/src/methods/__mocks__/react-native-appsflyer.ts +11 -0
- package/packages/plugins/plugin-appsflyer/src/methods/__tests__/identify.test.ts +57 -0
- package/packages/plugins/plugin-appsflyer/src/methods/__tests__/track.test.ts +99 -0
- package/packages/plugins/plugin-appsflyer/src/methods/identify.ts +42 -0
- package/packages/plugins/plugin-appsflyer/src/methods/track.ts +61 -0
- package/packages/plugins/plugin-appsflyer/src/types.ts +9 -0
- package/packages/plugins/plugin-appsflyer/tsconfig.json +10 -0
- package/packages/plugins/plugin-branch/CHANGELOG.md +51 -0
- package/packages/plugins/plugin-branch/LICENSE +21 -0
- package/packages/plugins/plugin-branch/README.md +71 -0
- package/packages/plugins/plugin-branch/babel.config.js +3 -0
- package/packages/plugins/plugin-branch/jest.config.js +5 -0
- package/packages/plugins/plugin-branch/package.json +74 -0
- package/packages/plugins/plugin-branch/release.config.js +3 -0
- package/packages/plugins/plugin-branch/src/BranchPlugin.ts +42 -0
- package/packages/plugins/plugin-branch/src/index.ts +1 -0
- package/packages/plugins/plugin-branch/src/methods/__mocks__/react-native-branch.ts +16 -0
- package/packages/plugins/plugin-branch/src/methods/__tests__/alias.test.ts +23 -0
- package/packages/plugins/plugin-branch/src/methods/__tests__/identify.test.ts +23 -0
- package/packages/plugins/plugin-branch/src/methods/__tests__/screen.test.ts +38 -0
- package/packages/plugins/plugin-branch/src/methods/__tests__/track.test.ts +73 -0
- package/packages/plugins/plugin-branch/src/methods/alias.ts +9 -0
- package/packages/plugins/plugin-branch/src/methods/identify.ts +9 -0
- package/packages/plugins/plugin-branch/src/methods/parameterMapping.ts +109 -0
- package/packages/plugins/plugin-branch/src/methods/reset.ts +5 -0
- package/packages/plugins/plugin-branch/src/methods/screen.ts +17 -0
- package/packages/plugins/plugin-branch/src/methods/track.ts +18 -0
- package/packages/plugins/plugin-branch/src/methods/util.ts +64 -0
- package/packages/plugins/plugin-branch/tsconfig.json +10 -0
- package/packages/plugins/plugin-braze/CHANGELOG.md +101 -0
- package/packages/plugins/plugin-braze/LICENSE +21 -0
- package/packages/plugins/plugin-braze/README.md +72 -0
- package/packages/plugins/plugin-braze/babel.config.js +3 -0
- package/packages/plugins/plugin-braze/jest.config.js +5 -0
- package/packages/plugins/plugin-braze/package.json +74 -0
- package/packages/plugins/plugin-braze/release.config.js +3 -0
- package/packages/plugins/plugin-braze/src/BrazePlugin.tsx +346 -0
- package/packages/plugins/plugin-braze/src/index.ts +1 -0
- package/packages/plugins/plugin-braze/src/methods/__mocks__/@braze/react-native-sdk.ts +31 -0
- package/packages/plugins/plugin-braze/src/methods/__tests__/flush.test.ts +15 -0
- package/packages/plugins/plugin-braze/src/methods/__tests__/identify.test.ts +114 -0
- package/packages/plugins/plugin-braze/src/methods/__tests__/track.test.ts +321 -0
- package/packages/plugins/plugin-braze/src/methods/flush.ts +5 -0
- package/packages/plugins/plugin-braze/tsconfig.json +10 -0
- package/packages/plugins/plugin-braze-middleware/CHANGELOG.md +32 -0
- package/packages/plugins/plugin-braze-middleware/LICENSE +21 -0
- package/packages/plugins/plugin-braze-middleware/README.md +67 -0
- package/packages/plugins/plugin-braze-middleware/babel.config.js +3 -0
- package/packages/plugins/plugin-braze-middleware/jest.config.js +5 -0
- package/packages/plugins/plugin-braze-middleware/package.json +72 -0
- package/packages/plugins/plugin-braze-middleware/release.config.js +3 -0
- package/packages/plugins/plugin-braze-middleware/src/BrazeMiddlewarePlugin.tsx +38 -0
- package/packages/plugins/plugin-braze-middleware/src/index.ts +1 -0
- package/packages/plugins/plugin-braze-middleware/tsconfig.json +10 -0
- package/packages/plugins/plugin-clevertap/CHANGELOG.md +32 -0
- package/packages/plugins/plugin-clevertap/LICENSE +21 -0
- package/packages/plugins/plugin-clevertap/README.md +71 -0
- package/packages/plugins/plugin-clevertap/babel.config.js +3 -0
- package/packages/plugins/plugin-clevertap/jest.config.js +5 -0
- package/packages/plugins/plugin-clevertap/package.json +72 -0
- package/packages/plugins/plugin-clevertap/release.config.js +3 -0
- package/packages/plugins/plugin-clevertap/src/ClevertapPlugin.ts +75 -0
- package/packages/plugins/plugin-clevertap/src/__mocks__/clevertap-react-native.ts +5 -0
- package/packages/plugins/plugin-clevertap/src/__tests__/ClevertapPlugin.test.ts +164 -0
- package/packages/plugins/plugin-clevertap/src/index.ts +1 -0
- package/packages/plugins/plugin-clevertap/src/parameterMapping.ts +19 -0
- package/packages/plugins/plugin-clevertap/tsconfig.json +10 -0
- package/packages/plugins/plugin-destination-filters/CHANGELOG.md +39 -0
- package/packages/plugins/plugin-destination-filters/LICENSE +21 -0
- package/packages/plugins/plugin-destination-filters/README.md +71 -0
- package/packages/plugins/plugin-destination-filters/babel.config.js +3 -0
- package/packages/plugins/plugin-destination-filters/jest.config.js +5 -0
- package/packages/plugins/plugin-destination-filters/package.json +77 -0
- package/packages/plugins/plugin-destination-filters/release.config.js +3 -0
- package/packages/plugins/plugin-destination-filters/src/DestinationFilters.ts +90 -0
- package/packages/plugins/plugin-destination-filters/src/__tests__/DestinationFilters.test.ts +102 -0
- package/packages/plugins/plugin-destination-filters/src/index.tsx +1 -0
- package/packages/plugins/plugin-destination-filters/tsconfig.json +10 -0
- package/packages/plugins/plugin-device-token/CHANGELOG.md +71 -0
- package/packages/plugins/plugin-device-token/LICENSE +21 -0
- package/packages/plugins/plugin-device-token/README.md +98 -0
- package/packages/plugins/plugin-device-token/babel.config.js +3 -0
- package/packages/plugins/plugin-device-token/jest.config.js +5 -0
- package/packages/plugins/plugin-device-token/package.json +76 -0
- package/packages/plugins/plugin-device-token/release.config.js +3 -0
- package/packages/plugins/plugin-device-token/src/DeviceTokenPlugin.tsx +94 -0
- package/packages/plugins/plugin-device-token/src/index.ts +1 -0
- package/packages/plugins/plugin-device-token/src/methods/___tests__/DeviceTokenPlugin.test.ts +94 -0
- package/packages/plugins/plugin-device-token/tsconfig.json +10 -0
- package/packages/plugins/plugin-facebook-app-events/CHANGELOG.md +78 -0
- package/packages/plugins/plugin-facebook-app-events/LICENSE +21 -0
- package/packages/plugins/plugin-facebook-app-events/README.md +73 -0
- package/packages/plugins/plugin-facebook-app-events/babel.config.js +3 -0
- package/packages/plugins/plugin-facebook-app-events/jest.config.js +5 -0
- package/packages/plugins/plugin-facebook-app-events/package.json +72 -0
- package/packages/plugins/plugin-facebook-app-events/release.config.js +3 -0
- package/packages/plugins/plugin-facebook-app-events/src/FacebookAppEventsPlugin.ts +192 -0
- package/packages/plugins/plugin-facebook-app-events/src/__tests__/FacebookAppEventsPlugin.test.ts +284 -0
- package/packages/plugins/plugin-facebook-app-events/src/index.ts +1 -0
- package/packages/plugins/plugin-facebook-app-events/src/methods/__tests__/screen.test.ts +33 -0
- package/packages/plugins/plugin-facebook-app-events/src/methods/screen.ts +35 -0
- package/packages/plugins/plugin-facebook-app-events/src/parameterMapping.ts +46 -0
- package/packages/plugins/plugin-facebook-app-events/src/utils.ts +12 -0
- package/packages/plugins/plugin-facebook-app-events/tsconfig.json +10 -0
- package/packages/plugins/plugin-firebase/CHANGELOG.md +102 -0
- package/packages/plugins/plugin-firebase/LICENSE +21 -0
- package/packages/plugins/plugin-firebase/README.md +72 -0
- package/packages/plugins/plugin-firebase/babel.config.js +3 -0
- package/packages/plugins/plugin-firebase/jest.config.js +5 -0
- package/packages/plugins/plugin-firebase/package.json +74 -0
- package/packages/plugins/plugin-firebase/release.config.js +3 -0
- package/packages/plugins/plugin-firebase/src/FirebasePlugin.tsx +96 -0
- package/packages/plugins/plugin-firebase/src/index.ts +1 -0
- package/packages/plugins/plugin-firebase/src/methods/___tests__/identify.test.ts +73 -0
- package/packages/plugins/plugin-firebase/src/methods/___tests__/reset.test.ts +20 -0
- package/packages/plugins/plugin-firebase/src/methods/___tests__/screen.test.ts +33 -0
- package/packages/plugins/plugin-firebase/src/methods/___tests__/track.test.ts +85 -0
- package/packages/plugins/plugin-firebase/src/methods/parameterMapping.ts +45 -0
- package/packages/plugins/plugin-firebase/src/methods/reset.ts +5 -0
- package/packages/plugins/plugin-firebase/src/methods/screen.ts +12 -0
- package/packages/plugins/plugin-firebase/src/methods/track.ts +26 -0
- package/packages/plugins/plugin-firebase/tsconfig.json +10 -0
- package/packages/plugins/plugin-idfa/CHANGELOG.md +62 -0
- package/packages/plugins/plugin-idfa/LICENSE +21 -0
- package/packages/plugins/plugin-idfa/README.md +105 -0
- package/packages/plugins/plugin-idfa/babel.config.js +3 -0
- package/packages/plugins/plugin-idfa/ios/AnalyticsReactNativePluginIdfa-Bridging-Header.h +2 -0
- package/packages/plugins/plugin-idfa/ios/AnalyticsReactNativePluginIdfa.m +11 -0
- package/packages/plugins/plugin-idfa/ios/AnalyticsReactNativePluginIdfa.swift +68 -0
- package/packages/plugins/plugin-idfa/ios/AnalyticsReactNativePluginIdfa.xcodeproj/project.pbxproj +293 -0
- package/packages/plugins/plugin-idfa/jest.config.js +5 -0
- package/packages/plugins/plugin-idfa/package.json +70 -0
- package/packages/plugins/plugin-idfa/release.config.js +3 -0
- package/packages/plugins/plugin-idfa/segment-analytics-react-native-plugin-idfa.podspec +19 -0
- package/packages/plugins/plugin-idfa/src/AnalyticsReactNativePluginIdfa.e2e.mock.ts +12 -0
- package/packages/plugins/plugin-idfa/src/AnalyticsReactNativePluginIdfa.ts +18 -0
- package/packages/plugins/plugin-idfa/src/IdfaPlugin.tsx +80 -0
- package/packages/plugins/plugin-idfa/src/index.ts +1 -0
- package/packages/plugins/plugin-idfa/src/types.ts +13 -0
- package/packages/plugins/plugin-idfa/tsconfig.json +10 -0
- package/packages/plugins/plugin-mixpanel/CHANGELOG.md +73 -0
- package/packages/plugins/plugin-mixpanel/LICENSE +21 -0
- package/packages/plugins/plugin-mixpanel/README.md +71 -0
- package/packages/plugins/plugin-mixpanel/babel.config.js +3 -0
- package/packages/plugins/plugin-mixpanel/jest.config.js +5 -0
- package/packages/plugins/plugin-mixpanel/package.json +72 -0
- package/packages/plugins/plugin-mixpanel/release.config.js +3 -0
- package/packages/plugins/plugin-mixpanel/src/MixpanelPlugin.ts +106 -0
- package/packages/plugins/plugin-mixpanel/src/index.ts +1 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__mocks__/mixpanel-react-native.ts +67 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/MixpanelPlugin.test.ts +57 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/__helpers__/constants.ts +18 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/alias.test.ts +47 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/group.test.ts +49 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/identify.test.ts +103 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/screen.test.ts +119 -0
- package/packages/plugins/plugin-mixpanel/src/methods/__tests__/track.test.ts +110 -0
- package/packages/plugins/plugin-mixpanel/src/methods/alias.ts +28 -0
- package/packages/plugins/plugin-mixpanel/src/methods/group.ts +26 -0
- package/packages/plugins/plugin-mixpanel/src/methods/identify.ts +72 -0
- package/packages/plugins/plugin-mixpanel/src/methods/screen.ts +43 -0
- package/packages/plugins/plugin-mixpanel/src/methods/track.ts +60 -0
- package/packages/plugins/plugin-mixpanel/src/types.ts +15 -0
- package/packages/plugins/plugin-mixpanel/tsconfig.json +10 -0
- package/packages/plugins/plugin-onetrust/CHANGELOG.md +16 -0
- package/packages/plugins/plugin-onetrust/LICENSE +21 -0
- package/packages/plugins/plugin-onetrust/README.md +155 -0
- package/packages/plugins/plugin-onetrust/babel.config.js +3 -0
- package/packages/plugins/plugin-onetrust/jest.config.js +6 -0
- package/packages/plugins/plugin-onetrust/package.json +75 -0
- package/packages/plugins/plugin-onetrust/release.config.js +3 -0
- package/packages/plugins/plugin-onetrust/src/OneTrustProvider.ts +71 -0
- package/packages/plugins/plugin-onetrust/src/index.tsx +1 -0
- package/packages/plugins/plugin-onetrust/tsconfig.json +10 -0
- package/packages/shared/__mocks__/context.ts +35 -0
- package/packages/shared/__mocks__/react-native-get-random-values.ts +1 -0
- package/packages/shared/__mocks__/react-native.ts +53 -0
- package/packages/shared/__mocks__/uuid.ts +3 -0
- package/packages/shared/jest.config.base.js +38 -0
- package/packages/shared/package.json +19 -0
- package/packages/shared/src/setup.ts +7 -0
- package/packages/shared/tsconfig.json +15 -0
- package/packages/sovran/CHANGELOG.md +42 -0
- package/packages/sovran/CONTRIBUTING.md +196 -0
- package/packages/sovran/LICENSE +21 -0
- package/packages/sovran/README.md +174 -0
- package/packages/sovran/android/build.gradle +94 -0
- package/packages/sovran/android/gradle.properties +5 -0
- package/packages/sovran/android/src/main/AndroidManifest.xml +4 -0
- package/packages/sovran/android/src/main/AndroidManifestNew.xml +3 -0
- package/packages/sovran/android/src/main/java/com/reactnativesovran/Sovran.kt +45 -0
- package/packages/sovran/android/src/main/java/com/reactnativesovran/SovranModule.kt +55 -0
- package/packages/sovran/babel.config.js +3 -0
- package/packages/sovran/ios/Sovran-Bridging-Header.h +3 -0
- package/packages/sovran/ios/Sovran.m +8 -0
- package/packages/sovran/ios/Sovran.swift +67 -0
- package/packages/sovran/ios/Sovran.xcodeproj/project.pbxproj +279 -0
- package/packages/sovran/jest.config.js +5 -0
- package/packages/sovran/package.json +89 -0
- package/packages/sovran/release.config.js +3 -0
- package/packages/sovran/sovran-react-native.podspec +33 -0
- package/packages/sovran/src/__tests__/bridge.test.ts +39 -0
- package/packages/sovran/src/__tests__/index.test.tsx +1 -0
- package/packages/sovran/src/__tests__/store.test.ts +397 -0
- package/packages/sovran/src/bridge.ts +48 -0
- package/packages/sovran/src/index.tsx +52 -0
- package/packages/sovran/src/persistor/async-storage-persistor.ts +63 -0
- package/packages/sovran/src/persistor/index.ts +2 -0
- package/packages/sovran/src/persistor/persistor.ts +31 -0
- package/packages/sovran/src/store.ts +232 -0
- package/packages/sovran/tsconfig.json +9 -0
- package/release.config.js +13 -0
- package/tsconfig.json +44 -0
- package/tsconfig.linter.json +36 -0
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import { AmplitudeSessionPlugin } from '../AmplitudeSessionPlugin';
|
|
4
|
+
// Import the constant for consistent timeout values
|
|
5
|
+
const MAX_SESSION_TIME_IN_MS = 300000;
|
|
6
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
7
|
+
import {
|
|
8
|
+
EventType,
|
|
9
|
+
TrackEventType,
|
|
10
|
+
IdentifyEventType,
|
|
11
|
+
ScreenEventType,
|
|
12
|
+
SegmentAPISettings,
|
|
13
|
+
UpdateType,
|
|
14
|
+
} from '@segment/analytics-react-native';
|
|
15
|
+
import { AppState } from 'react-native';
|
|
16
|
+
|
|
17
|
+
// AppState will be mocked by the base setup, we'll spy on it in the tests
|
|
18
|
+
|
|
19
|
+
describe('AmplitudeSessionPlugin', () => {
|
|
20
|
+
let plugin: AmplitudeSessionPlugin;
|
|
21
|
+
let mockAsyncStorage: jest.Mocked<typeof AsyncStorage>;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
jest.useFakeTimers();
|
|
26
|
+
|
|
27
|
+
plugin = new AmplitudeSessionPlugin();
|
|
28
|
+
|
|
29
|
+
mockAsyncStorage = AsyncStorage as jest.Mocked<typeof AsyncStorage>;
|
|
30
|
+
mockAsyncStorage.getItem.mockResolvedValue(null);
|
|
31
|
+
mockAsyncStorage.setItem.mockResolvedValue();
|
|
32
|
+
mockAsyncStorage.removeItem.mockResolvedValue();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
jest.useRealTimers();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const setupPluginWithClient = async () => {
|
|
40
|
+
const mockClient = {
|
|
41
|
+
track: jest.fn(),
|
|
42
|
+
} as any;
|
|
43
|
+
|
|
44
|
+
await plugin.configure(mockClient);
|
|
45
|
+
plugin.update(
|
|
46
|
+
{ integrations: { 'Actions Amplitude': {} } } as SegmentAPISettings,
|
|
47
|
+
UpdateType.initial
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return { client: mockClient };
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
describe('startNewSession scenarios', () => {
|
|
54
|
+
beforeEach(async () => {
|
|
55
|
+
await setupPluginWithClient();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should start a new session when sessionId is -1', async () => {
|
|
59
|
+
plugin.sessionId = -1;
|
|
60
|
+
plugin.lastEventTime = -1;
|
|
61
|
+
plugin.resetPending = false;
|
|
62
|
+
|
|
63
|
+
const mockEvent: TrackEventType = {
|
|
64
|
+
type: EventType.TrackEvent,
|
|
65
|
+
event: 'test_event',
|
|
66
|
+
properties: {},
|
|
67
|
+
messageId: 'msg-1',
|
|
68
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
69
|
+
anonymousId: 'anon-1',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
await plugin.execute(mockEvent);
|
|
73
|
+
|
|
74
|
+
expect(plugin.sessionId).toBeGreaterThan(0);
|
|
75
|
+
expect(plugin.analytics?.track).toHaveBeenCalledWith('session_start', {
|
|
76
|
+
integrations: {
|
|
77
|
+
'Actions Amplitude': { session_id: plugin.sessionId },
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should start a new session when session has expired (>MAX_SESSION_TIME_IN_MS)', async () => {
|
|
83
|
+
const baseTime = Date.now();
|
|
84
|
+
jest.setSystemTime(baseTime);
|
|
85
|
+
plugin.active = true;
|
|
86
|
+
plugin.sessionId = baseTime - 1000;
|
|
87
|
+
plugin.lastEventTime = baseTime - (MAX_SESSION_TIME_IN_MS + 1000); // 61 seconds ago
|
|
88
|
+
plugin.resetPending = false;
|
|
89
|
+
|
|
90
|
+
const mockEvent: TrackEventType = {
|
|
91
|
+
type: EventType.TrackEvent,
|
|
92
|
+
event: 'test_event',
|
|
93
|
+
properties: {},
|
|
94
|
+
messageId: 'msg-1',
|
|
95
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
96
|
+
anonymousId: 'anon-1',
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const oldSessionId = plugin.sessionId;
|
|
100
|
+
await plugin.execute(mockEvent);
|
|
101
|
+
|
|
102
|
+
expect(plugin.sessionId).not.toBe(oldSessionId);
|
|
103
|
+
expect(plugin.sessionId).toBeGreaterThan(oldSessionId);
|
|
104
|
+
expect(plugin.analytics?.track).toHaveBeenCalledWith('session_end', {
|
|
105
|
+
integrations: {
|
|
106
|
+
'Actions Amplitude': { session_id: oldSessionId },
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
expect(plugin.analytics?.track).toHaveBeenCalledWith('session_start', {
|
|
110
|
+
integrations: {
|
|
111
|
+
'Actions Amplitude': { session_id: plugin.sessionId },
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('should NOT start a new session when session is still active', async () => {
|
|
117
|
+
const baseTime = Date.now();
|
|
118
|
+
jest.setSystemTime(baseTime);
|
|
119
|
+
|
|
120
|
+
plugin.sessionId = baseTime - 1000;
|
|
121
|
+
plugin.lastEventTime = baseTime - 30000; // 30 seconds ago
|
|
122
|
+
plugin.resetPending = false;
|
|
123
|
+
|
|
124
|
+
const mockEvent: TrackEventType = {
|
|
125
|
+
type: EventType.TrackEvent,
|
|
126
|
+
event: 'test_event',
|
|
127
|
+
properties: {},
|
|
128
|
+
messageId: 'msg-1',
|
|
129
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
130
|
+
anonymousId: 'anon-1',
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const oldSessionId = plugin.sessionId;
|
|
134
|
+
await plugin.execute(mockEvent);
|
|
135
|
+
|
|
136
|
+
expect(plugin.sessionId).toBe(oldSessionId);
|
|
137
|
+
expect(plugin.analytics?.track).not.toHaveBeenCalledWith(
|
|
138
|
+
'session_start',
|
|
139
|
+
expect.any(Object)
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe('bug detection: multiple startNewSession calls', () => {
|
|
145
|
+
beforeEach(async () => {
|
|
146
|
+
await setupPluginWithClient();
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('BUG: should detect multiple session starts for rapid events (currently masked by 1000ms guard)', async () => {
|
|
150
|
+
const baseTime = Date.now();
|
|
151
|
+
jest.setSystemTime(baseTime);
|
|
152
|
+
|
|
153
|
+
plugin.sessionId = -1;
|
|
154
|
+
plugin.lastEventTime = -1;
|
|
155
|
+
plugin.resetPending = false;
|
|
156
|
+
|
|
157
|
+
const mockEvent: TrackEventType = {
|
|
158
|
+
type: EventType.TrackEvent,
|
|
159
|
+
event: 'test_event',
|
|
160
|
+
properties: {},
|
|
161
|
+
messageId: 'msg-1',
|
|
162
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
163
|
+
anonymousId: 'anon-1',
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// First call should start session
|
|
167
|
+
await plugin.execute(mockEvent);
|
|
168
|
+
const firstSessionId = plugin.sessionId;
|
|
169
|
+
|
|
170
|
+
expect(plugin.analytics?.track).toHaveBeenCalledWith('session_start', {
|
|
171
|
+
integrations: {
|
|
172
|
+
'Actions Amplitude': { session_id: firstSessionId },
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Advance time by only 500ms
|
|
177
|
+
jest.setSystemTime(baseTime + 500);
|
|
178
|
+
|
|
179
|
+
// Force expired condition artificially - this should be impossible in real scenarios
|
|
180
|
+
plugin.lastEventTime = baseTime - (MAX_SESSION_TIME_IN_MS + 10000); // MAX_SESSION_TIME_IN_MS + 10 seconds ago, definitely expired
|
|
181
|
+
|
|
182
|
+
// This scenario should NEVER happen in practice, but if it does, it's a bug
|
|
183
|
+
// The current implementation prevents this with a 1000ms guard, masking the bug
|
|
184
|
+
await plugin.execute(mockEvent);
|
|
185
|
+
|
|
186
|
+
// CURRENT BEHAVIOR (with guard): Only one session_start
|
|
187
|
+
// EXPECTED BEHAVIOR (without bugs): Should never reach this scenario
|
|
188
|
+
expect(plugin.analytics?.track).toHaveBeenCalledTimes(1);
|
|
189
|
+
|
|
190
|
+
// This test documents the current guard behavior but highlights it's a bug mask
|
|
191
|
+
console.warn(
|
|
192
|
+
'🐛 BUG MASKED: Multiple session start attempts should never occur'
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('BUG: should detect race conditions in parallel event execution', async () => {
|
|
197
|
+
const baseTime = Date.now();
|
|
198
|
+
jest.setSystemTime(baseTime);
|
|
199
|
+
|
|
200
|
+
plugin.sessionId = -1;
|
|
201
|
+
plugin.lastEventTime = -1;
|
|
202
|
+
plugin.resetPending = false;
|
|
203
|
+
|
|
204
|
+
const mockEvents = Array.from({ length: 5 }, (_, i) => ({
|
|
205
|
+
type: EventType.TrackEvent,
|
|
206
|
+
event: `test_event_${i}`,
|
|
207
|
+
properties: {},
|
|
208
|
+
messageId: `msg-${i}`,
|
|
209
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
210
|
+
anonymousId: 'anon-1',
|
|
211
|
+
})) as TrackEventType[];
|
|
212
|
+
|
|
213
|
+
// Execute multiple events in parallel - this could cause race conditions
|
|
214
|
+
const promises = mockEvents.map((event) => plugin.execute(event));
|
|
215
|
+
await Promise.all(promises);
|
|
216
|
+
|
|
217
|
+
// Count session_start calls
|
|
218
|
+
const trackMock = plugin.analytics?.track as jest.Mock;
|
|
219
|
+
const sessionStartCalls = trackMock.mock.calls.filter(
|
|
220
|
+
(call: any) => call[0] === 'session_start'
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
// IDEAL: Should only have 1 session_start call
|
|
224
|
+
// REALITY: May have multiple due to race conditions
|
|
225
|
+
if (sessionStartCalls.length > 1) {
|
|
226
|
+
console.error(
|
|
227
|
+
`🐛 BUG DETECTED: ${sessionStartCalls.length} session_start calls for parallel events`
|
|
228
|
+
);
|
|
229
|
+
// This test will fail if the bug exists, which is expected
|
|
230
|
+
expect(sessionStartCalls).toHaveLength(1);
|
|
231
|
+
} else {
|
|
232
|
+
// If this passes, the implementation handles parallel calls correctly
|
|
233
|
+
expect(sessionStartCalls).toHaveLength(1);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// it('BUG: should detect session restart loops from app state changes', async () => {
|
|
238
|
+
// const baseTime = Date.now();
|
|
239
|
+
// jest.setSystemTime(baseTime);
|
|
240
|
+
|
|
241
|
+
// // Start with an active session
|
|
242
|
+
// plugin.sessionId = baseTime;
|
|
243
|
+
// plugin.lastEventTime = baseTime;
|
|
244
|
+
|
|
245
|
+
// // Spy on startNewSessionIfNecessary to detect multiple calls
|
|
246
|
+
// const startNewSessionSpy = jest.spyOn(plugin as any, 'startNewSessionIfNecessary');
|
|
247
|
+
// const endSessionSpy = jest.spyOn(plugin as any, 'endSession');
|
|
248
|
+
// const startSessionSpy = jest.spyOn(plugin as any, 'startNewSession');
|
|
249
|
+
|
|
250
|
+
// // Simulate rapid app state changes
|
|
251
|
+
// const addEventListenerSpy = jest.spyOn(AppState, 'addEventListener');
|
|
252
|
+
// await setupPluginWithClient();
|
|
253
|
+
// const appStateChangeHandler = addEventListenerSpy.mock.calls[0][1];
|
|
254
|
+
|
|
255
|
+
// // Rapid background/foreground cycles
|
|
256
|
+
// appStateChangeHandler('background');
|
|
257
|
+
// appStateChangeHandler('active');
|
|
258
|
+
// appStateChangeHandler('background');
|
|
259
|
+
// appStateChangeHandler('active');
|
|
260
|
+
|
|
261
|
+
// // Wait for any async operations
|
|
262
|
+
// await new Promise(resolve => setTimeout(resolve, 0));
|
|
263
|
+
|
|
264
|
+
// // Should not cause multiple session operations for non-expired session
|
|
265
|
+
// const startNewSessionCalls = startNewSessionSpy.mock.calls.length;
|
|
266
|
+
// const endSessionCalls = endSessionSpy.mock.calls.length;
|
|
267
|
+
// const startSessionCalls = startSessionSpy.mock.calls.length;
|
|
268
|
+
|
|
269
|
+
// if (startNewSessionCalls > 2 || endSessionCalls > 0 || startSessionCalls > 0) {
|
|
270
|
+
// console.error(`🐛 BUG DETECTED: Unnecessary session operations - startNewSessionIfNecessary: ${startNewSessionCalls}, endSession: ${endSessionCalls}, startNewSession: ${startSessionCalls}`);
|
|
271
|
+
// }
|
|
272
|
+
|
|
273
|
+
// // For a non-expired session, we shouldn't have any actual session restarts
|
|
274
|
+
// expect(endSessionCalls).toBe(0);
|
|
275
|
+
// expect(startSessionCalls).toBe(0);
|
|
276
|
+
// });
|
|
277
|
+
|
|
278
|
+
it('BUG: should detect inconsistent session state', async () => {
|
|
279
|
+
const baseTime = Date.now();
|
|
280
|
+
jest.setSystemTime(baseTime);
|
|
281
|
+
|
|
282
|
+
// Set up inconsistent state that should never happen
|
|
283
|
+
plugin.sessionId = baseTime;
|
|
284
|
+
plugin.lastEventTime = -1; // Inconsistent: have sessionId but no lastEventTime
|
|
285
|
+
plugin.resetPending = false;
|
|
286
|
+
|
|
287
|
+
const mockEvent: TrackEventType = {
|
|
288
|
+
type: EventType.TrackEvent,
|
|
289
|
+
event: 'test_event',
|
|
290
|
+
properties: {},
|
|
291
|
+
messageId: 'msg-1',
|
|
292
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
293
|
+
anonymousId: 'anon-1',
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
// This inconsistent state might cause unexpected behavior
|
|
297
|
+
await plugin.execute(mockEvent);
|
|
298
|
+
|
|
299
|
+
// Check if the plugin handled inconsistent state correctly
|
|
300
|
+
const trackMock = plugin.analytics?.track as jest.Mock;
|
|
301
|
+
const sessionStartCalls = trackMock.mock.calls.filter(
|
|
302
|
+
(call: any) => call[0] === 'session_start'
|
|
303
|
+
);
|
|
304
|
+
const sessionEndCalls = trackMock.mock.calls.filter(
|
|
305
|
+
(call: any) => call[0] === 'session_end'
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
// Inconsistent state should be resolved without multiple session events
|
|
309
|
+
if (sessionStartCalls.length > 1 || sessionEndCalls.length > 1) {
|
|
310
|
+
console.error(
|
|
311
|
+
`🐛 BUG DETECTED: Inconsistent state caused multiple session events - starts: ${sessionStartCalls.length}, ends: ${sessionEndCalls.length}`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Should have resolved to a consistent state
|
|
316
|
+
expect(plugin.sessionId).toBeGreaterThan(0);
|
|
317
|
+
expect(plugin.lastEventTime).toBeGreaterThan(0);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('BUG: should detect async race conditions in startNewSessionIfNecessary', async () => {
|
|
321
|
+
const baseTime = Date.now();
|
|
322
|
+
jest.setSystemTime(baseTime);
|
|
323
|
+
|
|
324
|
+
plugin.sessionId = -1;
|
|
325
|
+
plugin.lastEventTime = -1;
|
|
326
|
+
|
|
327
|
+
const mockEvent: TrackEventType = {
|
|
328
|
+
type: EventType.TrackEvent,
|
|
329
|
+
event: 'test_event',
|
|
330
|
+
properties: {},
|
|
331
|
+
messageId: 'msg-1',
|
|
332
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
333
|
+
anonymousId: 'anon-1',
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Spy on the async methods to detect overlapping calls
|
|
337
|
+
const startNewSessionIfNecessarySpy = jest.spyOn(
|
|
338
|
+
plugin as any,
|
|
339
|
+
'startNewSessionIfNecessary'
|
|
340
|
+
);
|
|
341
|
+
const endSessionSpy = jest.spyOn(plugin as any, 'endSession');
|
|
342
|
+
const startNewSessionSpy = jest.spyOn(plugin as any, 'startNewSession');
|
|
343
|
+
|
|
344
|
+
// Call execute multiple times rapidly before any async operations complete
|
|
345
|
+
// This tests if the implementation properly handles concurrent calls to startNewSessionIfNecessary
|
|
346
|
+
const promises = [
|
|
347
|
+
plugin.execute({ ...mockEvent, messageId: 'msg-1' }),
|
|
348
|
+
plugin.execute({ ...mockEvent, messageId: 'msg-2' }),
|
|
349
|
+
plugin.execute({ ...mockEvent, messageId: 'msg-3' }),
|
|
350
|
+
];
|
|
351
|
+
|
|
352
|
+
await Promise.all(promises);
|
|
353
|
+
|
|
354
|
+
const startNewSessionIfNecessaryCalls =
|
|
355
|
+
startNewSessionIfNecessarySpy.mock.calls.length;
|
|
356
|
+
const endSessionCalls = endSessionSpy.mock.calls.length;
|
|
357
|
+
const startNewSessionCalls = startNewSessionSpy.mock.calls.length;
|
|
358
|
+
|
|
359
|
+
// For initial session creation, we should only have:
|
|
360
|
+
// - Multiple calls to startNewSessionIfNecessary (one per execute)
|
|
361
|
+
// - But only ONE actual startNewSession call
|
|
362
|
+
// - Zero endSession calls (no existing session to end)
|
|
363
|
+
|
|
364
|
+
console.log(
|
|
365
|
+
`📊 Session operations: startNewSessionIfNecessary: ${startNewSessionIfNecessaryCalls}, endSession: ${endSessionCalls}, startNewSession: ${startNewSessionCalls}`
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
if (startNewSessionCalls > 1) {
|
|
369
|
+
console.error(
|
|
370
|
+
`🐛 CRITICAL BUG DETECTED: ${startNewSessionCalls} startNewSession calls from concurrent execute operations`
|
|
371
|
+
);
|
|
372
|
+
// This should fail if there are race conditions
|
|
373
|
+
expect(startNewSessionCalls).toBe(1);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (endSessionCalls > 1) {
|
|
377
|
+
console.error(
|
|
378
|
+
`🐛 BUG DETECTED: ${endSessionCalls} endSession calls from concurrent operations`
|
|
379
|
+
);
|
|
380
|
+
expect(endSessionCalls).toBeLessThanOrEqual(1);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// Should have properly created exactly one session
|
|
384
|
+
expect(plugin.sessionId).toBeGreaterThan(0);
|
|
385
|
+
expect(plugin.lastEventTime).toBeGreaterThan(0);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('BUG: should detect overlapping session end/start operations', async () => {
|
|
389
|
+
const baseTime = Date.now();
|
|
390
|
+
jest.setSystemTime(baseTime);
|
|
391
|
+
|
|
392
|
+
// Start with an existing session that will expire
|
|
393
|
+
plugin.sessionId = baseTime - 1000;
|
|
394
|
+
plugin.lastEventTime = baseTime - (MAX_SESSION_TIME_IN_MS + 10000); // MAX_SESSION_TIME_IN_MS + 10 seconds ago, expired
|
|
395
|
+
|
|
396
|
+
const mockEvent: TrackEventType = {
|
|
397
|
+
type: EventType.TrackEvent,
|
|
398
|
+
event: 'test_event',
|
|
399
|
+
properties: {},
|
|
400
|
+
messageId: 'msg-1',
|
|
401
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
402
|
+
anonymousId: 'anon-1',
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
// Spy on session operations
|
|
406
|
+
const endSessionSpy = jest.spyOn(plugin as any, 'endSession');
|
|
407
|
+
const startNewSessionSpy = jest.spyOn(plugin as any, 'startNewSession');
|
|
408
|
+
|
|
409
|
+
// Execute multiple events that should all trigger session restart
|
|
410
|
+
const promises = [
|
|
411
|
+
plugin.execute({ ...mockEvent, messageId: 'msg-1' }),
|
|
412
|
+
plugin.execute({ ...mockEvent, messageId: 'msg-2' }),
|
|
413
|
+
plugin.execute({ ...mockEvent, messageId: 'msg-3' }),
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
await Promise.all(promises);
|
|
417
|
+
|
|
418
|
+
const endSessionCalls = endSessionSpy.mock.calls.length;
|
|
419
|
+
const startNewSessionCalls = startNewSessionSpy.mock.calls.length;
|
|
420
|
+
|
|
421
|
+
// For session restart, we should have:
|
|
422
|
+
// - Exactly ONE endSession call (to end the expired session)
|
|
423
|
+
// - Exactly ONE startNewSession call (to start the new session)
|
|
424
|
+
|
|
425
|
+
if (endSessionCalls > 1) {
|
|
426
|
+
console.error(
|
|
427
|
+
`🐛 BUG DETECTED: ${endSessionCalls} endSession calls from concurrent operations`
|
|
428
|
+
);
|
|
429
|
+
expect(endSessionCalls).toBe(1);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (startNewSessionCalls > 1) {
|
|
433
|
+
console.error(
|
|
434
|
+
`🐛 CRITICAL BUG DETECTED: ${startNewSessionCalls} startNewSession calls from concurrent operations`
|
|
435
|
+
);
|
|
436
|
+
expect(startNewSessionCalls).toBe(1);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Verify the track calls
|
|
440
|
+
const trackMock = plugin.analytics?.track as jest.Mock;
|
|
441
|
+
const sessionEndCalls = trackMock.mock.calls.filter(
|
|
442
|
+
(call: any) => call[0] === 'session_end'
|
|
443
|
+
);
|
|
444
|
+
const sessionStartCalls = trackMock.mock.calls.filter(
|
|
445
|
+
(call: any) => call[0] === 'session_start'
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
if (sessionEndCalls.length > 1 || sessionStartCalls.length > 1) {
|
|
449
|
+
console.error(
|
|
450
|
+
`🐛 BUG DETECTED: Multiple session events - ends: ${sessionEndCalls.length}, starts: ${sessionStartCalls.length}`
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
expect(sessionEndCalls).toHaveLength(1);
|
|
455
|
+
expect(sessionStartCalls).toHaveLength(1);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it('EXPECTED BEHAVIOR: single session for sequential events within session timeout', async () => {
|
|
459
|
+
const baseTime = Date.now();
|
|
460
|
+
jest.setSystemTime(baseTime);
|
|
461
|
+
|
|
462
|
+
plugin.sessionId = -1;
|
|
463
|
+
plugin.lastEventTime = -1;
|
|
464
|
+
|
|
465
|
+
const mockEvents = Array.from({ length: 5 }, (_, i) => ({
|
|
466
|
+
type: EventType.TrackEvent,
|
|
467
|
+
event: `test_event_${i}`,
|
|
468
|
+
properties: {},
|
|
469
|
+
messageId: `msg-${i}`,
|
|
470
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
471
|
+
anonymousId: 'anon-1',
|
|
472
|
+
})) as TrackEventType[];
|
|
473
|
+
|
|
474
|
+
// Execute events sequentially with small time gaps (within session timeout)
|
|
475
|
+
for (let i = 0; i < mockEvents.length; i++) {
|
|
476
|
+
jest.setSystemTime(baseTime + i * 10000); // 10 seconds apart
|
|
477
|
+
await plugin.execute(mockEvents[i]);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Should only have one session_start call for all events
|
|
481
|
+
const trackMock = plugin.analytics?.track as jest.Mock;
|
|
482
|
+
const sessionStartCalls = trackMock.mock.calls.filter(
|
|
483
|
+
(call: any) => call[0] === 'session_start'
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
expect(sessionStartCalls).toHaveLength(1);
|
|
487
|
+
|
|
488
|
+
// All events should have the same session ID
|
|
489
|
+
const sessionId = plugin.sessionId;
|
|
490
|
+
expect(sessionId).toBeGreaterThan(0);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
describe('session expiration scenarios', () => {
|
|
495
|
+
beforeEach(async () => {
|
|
496
|
+
await setupPluginWithClient();
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should expire session exactly at MAX_SESSION_TIME_IN_MS', async () => {
|
|
500
|
+
const baseTime = Date.now();
|
|
501
|
+
jest.setSystemTime(baseTime);
|
|
502
|
+
|
|
503
|
+
plugin.sessionId = baseTime - 1000;
|
|
504
|
+
plugin.lastEventTime = baseTime - MAX_SESSION_TIME_IN_MS; // Exactly 60 seconds
|
|
505
|
+
plugin.resetPending = false;
|
|
506
|
+
|
|
507
|
+
const mockEvent: TrackEventType = {
|
|
508
|
+
type: EventType.TrackEvent,
|
|
509
|
+
event: 'test_event',
|
|
510
|
+
properties: {},
|
|
511
|
+
messageId: 'msg-1',
|
|
512
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
513
|
+
anonymousId: 'anon-1',
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const oldSessionId = plugin.sessionId;
|
|
517
|
+
await plugin.execute(mockEvent);
|
|
518
|
+
|
|
519
|
+
expect(plugin.sessionId).not.toBe(oldSessionId);
|
|
520
|
+
expect(plugin.analytics?.track).toHaveBeenCalledWith('session_end', {
|
|
521
|
+
integrations: {
|
|
522
|
+
'Actions Amplitude': { session_id: oldSessionId },
|
|
523
|
+
},
|
|
524
|
+
});
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('should NOT expire session at MAX_SESSION_TIME_IN_MS - 1 second', async () => {
|
|
528
|
+
// ✅ Freeze Date.now for this test only
|
|
529
|
+
const fixedNow = 1761550980000;
|
|
530
|
+
const nowSpy = jest.spyOn(Date, 'now').mockImplementation(() => fixedNow);
|
|
531
|
+
|
|
532
|
+
plugin.sessionId = fixedNow - 1000;
|
|
533
|
+
plugin.lastEventTime = fixedNow - (MAX_SESSION_TIME_IN_MS - 2); // within limit
|
|
534
|
+
|
|
535
|
+
const mockEvent: TrackEventType = {
|
|
536
|
+
type: EventType.TrackEvent,
|
|
537
|
+
event: 'test_event',
|
|
538
|
+
properties: {},
|
|
539
|
+
messageId: 'msg-1',
|
|
540
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
541
|
+
anonymousId: 'anon-1',
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
const oldSessionId = plugin.sessionId;
|
|
545
|
+
|
|
546
|
+
await plugin.execute(mockEvent);
|
|
547
|
+
|
|
548
|
+
expect(plugin.sessionId).toBe(oldSessionId);
|
|
549
|
+
expect(plugin.analytics?.track).not.toHaveBeenCalledWith(
|
|
550
|
+
'session_start',
|
|
551
|
+
expect.any(Object)
|
|
552
|
+
);
|
|
553
|
+
expect(plugin.analytics?.track).not.toHaveBeenCalledWith(
|
|
554
|
+
'session_end',
|
|
555
|
+
expect.any(Object)
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
nowSpy.mockRestore(); // ✅ restores Date.now, unaffected by useRealTimers
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
describe('app state change scenarios', () => {
|
|
563
|
+
let appStateChangeHandler: (nextAppState: any) => void;
|
|
564
|
+
|
|
565
|
+
beforeEach(async () => {
|
|
566
|
+
// Spy on AppState methods
|
|
567
|
+
const addEventListenerSpy = jest.spyOn(AppState, 'addEventListener');
|
|
568
|
+
|
|
569
|
+
await setupPluginWithClient();
|
|
570
|
+
|
|
571
|
+
// Capture the app state change handler
|
|
572
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith(
|
|
573
|
+
'change',
|
|
574
|
+
expect.any(Function)
|
|
575
|
+
);
|
|
576
|
+
appStateChangeHandler = addEventListenerSpy.mock.calls[0][1];
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('should start new session when app comes to foreground after expiration', async () => {
|
|
580
|
+
const baseTime = Date.now();
|
|
581
|
+
jest.setSystemTime(baseTime);
|
|
582
|
+
|
|
583
|
+
// Set up an active session that will be expired
|
|
584
|
+
plugin.sessionId = baseTime - 1000;
|
|
585
|
+
plugin.lastEventTime = baseTime - (MAX_SESSION_TIME_IN_MS + 10000); // MAX_SESSION_TIME_IN_MS + 10 seconds ago, already expired
|
|
586
|
+
|
|
587
|
+
// Spy on the startNewSessionIfNecessary method to ensure it gets called
|
|
588
|
+
const startNewSessionSpy = jest.spyOn(
|
|
589
|
+
plugin as any,
|
|
590
|
+
'startNewSessionIfNecessary'
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
// Simulate app coming to foreground
|
|
594
|
+
appStateChangeHandler('active');
|
|
595
|
+
|
|
596
|
+
// Should call startNewSessionIfNecessary
|
|
597
|
+
expect(startNewSessionSpy).toHaveBeenCalled();
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
it('should NOT start new session when app comes to foreground before expiration', async () => {
|
|
601
|
+
const baseTime = Date.now();
|
|
602
|
+
jest.setSystemTime(baseTime);
|
|
603
|
+
|
|
604
|
+
// Set up an active session
|
|
605
|
+
plugin.sessionId = baseTime - 1000;
|
|
606
|
+
plugin.lastEventTime = baseTime - 30000; // 30 seconds ago, still active
|
|
607
|
+
|
|
608
|
+
// Simulate app going to background
|
|
609
|
+
appStateChangeHandler('background');
|
|
610
|
+
|
|
611
|
+
// Advance time but not beyond session timeout
|
|
612
|
+
jest.setSystemTime(baseTime + 20000); // 20 seconds later (total 50 seconds)
|
|
613
|
+
|
|
614
|
+
// Simulate app coming to foreground
|
|
615
|
+
appStateChangeHandler('active');
|
|
616
|
+
|
|
617
|
+
// Should NOT trigger new session
|
|
618
|
+
expect(plugin.analytics?.track).not.toHaveBeenCalled();
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it('should update lastEventTime when app goes to background', async () => {
|
|
622
|
+
const baseTime = Date.now();
|
|
623
|
+
jest.setSystemTime(baseTime);
|
|
624
|
+
|
|
625
|
+
plugin.sessionId = baseTime - 1000;
|
|
626
|
+
plugin.lastEventTime = baseTime - 30000;
|
|
627
|
+
|
|
628
|
+
// Simulate app going to background
|
|
629
|
+
appStateChangeHandler('background');
|
|
630
|
+
|
|
631
|
+
expect(plugin.lastEventTime).toBe(baseTime);
|
|
632
|
+
expect(mockAsyncStorage.setItem).toHaveBeenCalled();
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
describe('session data persistence', () => {
|
|
637
|
+
it('should load session data from AsyncStorage on configure', async () => {
|
|
638
|
+
const mockSessionId = '1234567890';
|
|
639
|
+
const mockLastEventTime = '1234567000';
|
|
640
|
+
|
|
641
|
+
mockAsyncStorage.getItem
|
|
642
|
+
.mockResolvedValueOnce(mockSessionId) // SESSION_ID_KEY
|
|
643
|
+
.mockResolvedValueOnce(mockLastEventTime); // LAST_EVENT_TIME_KEY
|
|
644
|
+
|
|
645
|
+
const mockClient = { track: jest.fn() } as any;
|
|
646
|
+
await plugin.configure(mockClient);
|
|
647
|
+
|
|
648
|
+
expect(plugin.sessionId).toBe(1234567890);
|
|
649
|
+
expect(plugin.lastEventTime).toBe(1234567000);
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should save session data to AsyncStorage after events', async () => {
|
|
653
|
+
await setupPluginWithClient();
|
|
654
|
+
|
|
655
|
+
const mockEvent: TrackEventType = {
|
|
656
|
+
type: EventType.TrackEvent,
|
|
657
|
+
event: 'test_event',
|
|
658
|
+
properties: {},
|
|
659
|
+
messageId: 'msg-1',
|
|
660
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
661
|
+
anonymousId: 'anon-1',
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
await plugin.execute(mockEvent);
|
|
665
|
+
|
|
666
|
+
expect(mockAsyncStorage.setItem).toHaveBeenCalledWith(
|
|
667
|
+
'event_session_id',
|
|
668
|
+
plugin.sessionId.toString()
|
|
669
|
+
);
|
|
670
|
+
expect(mockAsyncStorage.setItem).toHaveBeenCalledWith(
|
|
671
|
+
'last_event_time',
|
|
672
|
+
plugin.lastEventTime.toString()
|
|
673
|
+
);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('should clear session data on reset', async () => {
|
|
677
|
+
await setupPluginWithClient();
|
|
678
|
+
|
|
679
|
+
await plugin.reset();
|
|
680
|
+
|
|
681
|
+
expect(plugin.sessionId).toBe(-1);
|
|
682
|
+
expect(plugin.lastEventTime).toBe(-1);
|
|
683
|
+
expect(plugin.eventSessionId).toBe(-1);
|
|
684
|
+
expect(mockAsyncStorage.removeItem).toHaveBeenCalledWith(
|
|
685
|
+
'previous_session_id'
|
|
686
|
+
);
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
describe('event enrichment', () => {
|
|
691
|
+
beforeEach(async () => {
|
|
692
|
+
await setupPluginWithClient();
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('should add session_id to track events', async () => {
|
|
696
|
+
const mockEvent: TrackEventType = {
|
|
697
|
+
type: EventType.TrackEvent,
|
|
698
|
+
event: 'test_event',
|
|
699
|
+
properties: {},
|
|
700
|
+
messageId: 'msg-1',
|
|
701
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
702
|
+
anonymousId: 'anon-1',
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const result = await plugin.execute(mockEvent);
|
|
706
|
+
|
|
707
|
+
expect(result.integrations?.['Actions Amplitude']).toEqual({
|
|
708
|
+
session_id: plugin.sessionId,
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
it('should add session_id to identify events', async () => {
|
|
713
|
+
const mockEvent: IdentifyEventType = {
|
|
714
|
+
type: EventType.IdentifyEvent,
|
|
715
|
+
traits: {},
|
|
716
|
+
messageId: 'msg-1',
|
|
717
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
718
|
+
anonymousId: 'anon-1',
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
const result = await plugin.execute(mockEvent);
|
|
722
|
+
|
|
723
|
+
expect(result.integrations?.['Actions Amplitude']).toEqual({
|
|
724
|
+
session_id: plugin.sessionId,
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
it('should add name property to screen events', async () => {
|
|
729
|
+
const mockEvent: ScreenEventType = {
|
|
730
|
+
type: EventType.ScreenEvent,
|
|
731
|
+
name: 'Home Screen',
|
|
732
|
+
properties: { existing: 'prop' },
|
|
733
|
+
messageId: 'msg-1',
|
|
734
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
735
|
+
anonymousId: 'anon-1',
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
const result = (await plugin.execute(mockEvent)) as ScreenEventType;
|
|
739
|
+
|
|
740
|
+
expect(result.properties).toEqual({
|
|
741
|
+
existing: 'prop',
|
|
742
|
+
name: 'Home Screen',
|
|
743
|
+
});
|
|
744
|
+
expect(result.integrations?.['Actions Amplitude']).toEqual({
|
|
745
|
+
session_id: plugin.sessionId,
|
|
746
|
+
});
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
it('should NOT modify events when session_id already exists', async () => {
|
|
750
|
+
const mockEvent: TrackEventType = {
|
|
751
|
+
type: EventType.TrackEvent,
|
|
752
|
+
event: 'test_event',
|
|
753
|
+
properties: {},
|
|
754
|
+
messageId: 'msg-1',
|
|
755
|
+
timestamp: '2023-01-01T00:00:00.000Z',
|
|
756
|
+
anonymousId: 'anon-1',
|
|
757
|
+
integrations: {
|
|
758
|
+
'Actions Amplitude': { session_id: 999999 },
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const result = await plugin.execute(mockEvent);
|
|
763
|
+
|
|
764
|
+
expect(result.integrations?.['Actions Amplitude']).toEqual({
|
|
765
|
+
session_id: 999999, // Should preserve existing session_id
|
|
766
|
+
});
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
});
|