-
-
Notifications
You must be signed in to change notification settings - Fork 344
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
44 changed files
with
2,532 additions
and
739 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,57 @@ | |
## Unreleased | ||
|
||
### Features | ||
|
||
- Capture App Start errors and crashes by initializing Sentry from `sentry.options.json` ([#4472](https://github.com/getsentry/sentry-react-native/pull/4472)) | ||
|
||
Create `sentry.options.json` in the React Native project root and set options the same as you currently have in `Sentry.init` in JS. | ||
|
||
```json | ||
{ | ||
"dsn": "https://[email protected]/value", | ||
} | ||
``` | ||
|
||
Initialize Sentry on the native layers by newly provided native methods. | ||
|
||
```kotlin | ||
import io.sentry.react.RNSentrySDK | ||
|
||
class MainApplication : Application(), ReactApplication { | ||
override fun onCreate() { | ||
super.onCreate() | ||
RNSentrySDK.init(this) | ||
} | ||
} | ||
``` | ||
|
||
```obj-c | ||
#import <RNSentry/RNSentry.h> | ||
|
||
@implementation AppDelegate | ||
- (BOOL)application:(UIApplication *)application | ||
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions | ||
{ | ||
[RNSentrySDK start]; | ||
return [super application:application didFinishLaunchingWithOptions:launchOptions]; | ||
} | ||
@end | ||
``` | ||
### Changes | ||
- Load `optionsFile` into the JS bundle during Metro bundle process ([#4476](https://github.com/getsentry/sentry-react-native/pull/4476)) | ||
- Add experimental version of `startWithConfigureOptions` for Apple platforms ([#4444](https://github.com/getsentry/sentry-react-native/pull/4444)) | ||
- Add experimental version of `init` with optional `OptionsConfiguration<SentryAndroidOptions>` for Android ([#4451](https://github.com/getsentry/sentry-react-native/pull/4451)) | ||
- Add initialization using `sentry.options.json` for Apple platforms ([#4447](https://github.com/getsentry/sentry-react-native/pull/4447)) | ||
- Add initialization using `sentry.options.json` for Android ([#4451](https://github.com/getsentry/sentry-react-native/pull/4451)) | ||
### Internal | ||
- Extract iOS native initialization to standalone structures ([#4442](https://github.com/getsentry/sentry-react-native/pull/4442)) | ||
- Extract Android native initialization to standalone structures ([#4445](https://github.com/getsentry/sentry-react-native/pull/4445)) | ||
### Dependencies | ||
- Bump JavaScript SDK from v8.53.0 to v8.54.0 ([#4503](https://github.com/getsentry/sentry-react-native/pull/4503)) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
packages/core/RNSentryAndroidTester/app/src/androidTest/assets/invalid.options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"dsn": "invalid-dsn" | ||
} |
1 change: 1 addition & 0 deletions
1
packages/core/RNSentryAndroidTester/app/src/androidTest/assets/invalid.options.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
invalid-options |
5 changes: 5 additions & 0 deletions
5
packages/core/RNSentryAndroidTester/app/src/androidTest/assets/sentry.options.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"dsn": "https://[email protected]/123456", | ||
"enableTracing": true, | ||
"tracesSampleRate": 1.0 | ||
} |
103 changes: 103 additions & 0 deletions
103
...SentryAndroidTester/app/src/androidTest/java/io/sentry/react/RNSentryJsonConverterTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package io.sentry.react | ||
|
||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import com.facebook.react.bridge.WritableArray | ||
import com.facebook.react.bridge.WritableMap | ||
import io.sentry.react.RNSentryJsonConverter.convertToWritable | ||
import org.json.JSONArray | ||
import org.json.JSONObject | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Assert.assertNotNull | ||
import org.junit.Assert.assertNull | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class RNSentryJsonConverterTest { | ||
@Test | ||
fun testConvertToWritableWithSimpleJsonObject() { | ||
val jsonObject = | ||
JSONObject().apply { | ||
put("floatKey", 12.3f) | ||
put("doubleKey", 12.3) | ||
put("intKey", 123) | ||
put("stringKey", "test") | ||
put("nullKey", JSONObject.NULL) | ||
} | ||
|
||
val result: WritableMap? = convertToWritable(jsonObject) | ||
|
||
assertNotNull(result) | ||
assertEquals(12.3, result!!.getDouble("floatKey"), 0.0001) | ||
assertEquals(12.3, result.getDouble("doubleKey"), 0.0) | ||
assertEquals(123, result.getInt("intKey")) | ||
assertEquals("test", result.getString("stringKey")) | ||
assertNull(result.getString("nullKey")) | ||
} | ||
|
||
@Test | ||
fun testConvertToWritableWithNestedJsonObject() { | ||
val jsonObject = | ||
JSONObject().apply { | ||
put( | ||
"nested", | ||
JSONObject().apply { | ||
put("key", "value") | ||
}, | ||
) | ||
} | ||
|
||
val result: WritableMap? = convertToWritable(jsonObject) | ||
|
||
assertNotNull(result) | ||
val nestedMap = result!!.getMap("nested") | ||
assertNotNull(nestedMap) | ||
assertEquals("value", nestedMap!!.getString("key")) | ||
} | ||
|
||
@Test | ||
fun testConvertToWritableWithJsonArray() { | ||
val jsonArray = | ||
JSONArray().apply { | ||
put(1) | ||
put(2.5) | ||
put("string") | ||
put(JSONObject.NULL) | ||
} | ||
|
||
val result: WritableArray = convertToWritable(jsonArray) | ||
|
||
assertEquals(1, result.getInt(0)) | ||
assertEquals(2.5, result.getDouble(1), 0.0) | ||
assertEquals("string", result.getString(2)) | ||
assertNull(result.getString(3)) | ||
} | ||
|
||
@Test | ||
fun testConvertToWritableWithNestedJsonArray() { | ||
val jsonObject = | ||
JSONObject().apply { | ||
put( | ||
"array", | ||
JSONArray().apply { | ||
put( | ||
JSONObject().apply { | ||
put("key1", "value1") | ||
}, | ||
) | ||
put( | ||
JSONObject().apply { | ||
put("key2", "value2") | ||
}, | ||
) | ||
}, | ||
) | ||
} | ||
|
||
val result: WritableMap? = convertToWritable(jsonObject) | ||
|
||
val array = result?.getArray("array") | ||
assertEquals("value1", array?.getMap(0)?.getString("key1")) | ||
assertEquals("value2", array?.getMap(1)?.getString("key2")) | ||
} | ||
} |
200 changes: 200 additions & 0 deletions
200
...es/core/RNSentryAndroidTester/app/src/androidTest/java/io/sentry/react/RNSentrySDKTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
package io.sentry.react | ||
|
||
import android.content.Context | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import com.facebook.react.common.JavascriptException | ||
import io.sentry.Hint | ||
import io.sentry.ILogger | ||
import io.sentry.Sentry | ||
import io.sentry.Sentry.OptionsConfiguration | ||
import io.sentry.SentryEvent | ||
import io.sentry.android.core.AndroidLogger | ||
import io.sentry.android.core.SentryAndroidOptions | ||
import io.sentry.protocol.SdkVersion | ||
import org.junit.After | ||
import org.junit.Assert.assertEquals | ||
import org.junit.Assert.assertFalse | ||
import org.junit.Assert.assertNotNull | ||
import org.junit.Assert.assertNull | ||
import org.junit.Assert.assertTrue | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class RNSentrySDKTest { | ||
private val logger: ILogger = AndroidLogger(RNSentrySDKTest::class.java.simpleName) | ||
private lateinit var context: Context | ||
|
||
companion object { | ||
private const val INITIALISATION_ERROR = "Failed to initialize Sentry's React Native SDK" | ||
private const val VALID_OPTIONS = "sentry.options.json" | ||
private const val INVALID_OPTIONS = "invalid.options.json" | ||
private const val INVALID_JSON = "invalid.options.txt" | ||
private const val MISSING = "non-existing-file" | ||
|
||
private val validConfig = | ||
OptionsConfiguration<SentryAndroidOptions> { options -> | ||
options.dsn = "https://[email protected]/123456" | ||
} | ||
private val invalidConfig = | ||
OptionsConfiguration<SentryAndroidOptions> { options -> | ||
options.dsn = "invalid-dsn" | ||
} | ||
private val emptyConfig = OptionsConfiguration<SentryAndroidOptions> {} | ||
} | ||
|
||
@Before | ||
fun setUp() { | ||
context = InstrumentationRegistry.getInstrumentation().context | ||
} | ||
|
||
@After | ||
fun tearDown() { | ||
Sentry.close() | ||
} | ||
|
||
@Test | ||
fun initialisesSuccessfullyWithDefaultValidJsonFile() { // sentry.options.json | ||
RNSentrySDK.init(context) | ||
assertTrue(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun initialisesSuccessfullyWithValidConfigurationAndDefaultValidJsonFile() { | ||
RNSentrySDK.init(context, validConfig) | ||
assertTrue(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun initialisesSuccessfullyWithValidConfigurationAndInvalidJsonFile() { | ||
RNSentrySDK.init(context, validConfig, INVALID_OPTIONS, logger) | ||
assertTrue(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun initialisesSuccessfullyWithValidConfigurationAndMissingJsonFile() { | ||
RNSentrySDK.init(context, validConfig, MISSING, logger) | ||
assertTrue(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun initialisesSuccessfullyWithValidConfigurationAndErrorInParsingJsonFile() { | ||
RNSentrySDK.init(context, validConfig, INVALID_JSON, logger) | ||
assertTrue(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun initialisesSuccessfullyWithNoConfigurationAndValidJsonFile() { | ||
RNSentrySDK.init(context, emptyConfig, VALID_OPTIONS, logger) | ||
assertTrue(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun failsToInitialiseWithNoConfigurationAndInvalidJsonFile() { | ||
try { | ||
RNSentrySDK.init(context, emptyConfig, INVALID_OPTIONS, logger) | ||
} catch (e: Exception) { | ||
assertEquals(INITIALISATION_ERROR, e.message) | ||
} | ||
assertFalse(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun failsToInitialiseWithInvalidConfigAndInvalidJsonFile() { | ||
try { | ||
RNSentrySDK.init(context, invalidConfig, INVALID_OPTIONS, logger) | ||
} catch (e: Exception) { | ||
assertEquals(INITIALISATION_ERROR, e.message) | ||
} | ||
assertFalse(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun failsToInitialiseWithInvalidConfigAndValidJsonFile() { | ||
try { | ||
RNSentrySDK.init(context, invalidConfig, VALID_OPTIONS, logger) | ||
} catch (e: Exception) { | ||
assertEquals(INITIALISATION_ERROR, e.message) | ||
} | ||
assertFalse(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun failsToInitialiseWithInvalidConfigurationAndDefaultValidJsonFile() { | ||
try { | ||
RNSentrySDK.init(context, invalidConfig) | ||
} catch (e: Exception) { | ||
assertEquals(INITIALISATION_ERROR, e.message) | ||
} | ||
assertFalse(Sentry.isEnabled()) | ||
} | ||
|
||
@Test | ||
fun defaultsAndFinalsAreSetWithValidJsonFile() { | ||
RNSentrySDK.init(context, emptyConfig, VALID_OPTIONS, logger) | ||
val actualOptions = Sentry.getCurrentHub().options as SentryAndroidOptions | ||
verifyDefaults(actualOptions) | ||
verifyFinals(actualOptions) | ||
// options file | ||
assert(actualOptions.dsn == "https://[email protected]/123456") | ||
} | ||
|
||
@Test | ||
fun defaultsAndFinalsAreSetWithValidConfiguration() { | ||
RNSentrySDK.init(context, validConfig, MISSING, logger) | ||
val actualOptions = Sentry.getCurrentHub().options as SentryAndroidOptions | ||
verifyDefaults(actualOptions) | ||
verifyFinals(actualOptions) | ||
// configuration | ||
assert(actualOptions.dsn == "https://[email protected]/123456") | ||
} | ||
|
||
@Test | ||
fun defaultsOverrideOptionsJsonFile() { | ||
RNSentrySDK.init(context, emptyConfig, VALID_OPTIONS, logger) | ||
val actualOptions = Sentry.getCurrentHub().options as SentryAndroidOptions | ||
assertNull(actualOptions.tracesSampleRate) | ||
assertEquals(false, actualOptions.enableTracing) | ||
} | ||
|
||
@Test | ||
fun configurationOverridesDefaultOptions() { | ||
val validConfig = | ||
OptionsConfiguration<SentryAndroidOptions> { options -> | ||
options.dsn = "https://[email protected]/123456" | ||
options.tracesSampleRate = 0.5 | ||
options.enableTracing = true | ||
} | ||
RNSentrySDK.init(context, validConfig, MISSING, logger) | ||
val actualOptions = Sentry.getCurrentHub().options as SentryAndroidOptions | ||
assertEquals(0.5, actualOptions.tracesSampleRate) | ||
assertEquals(true, actualOptions.enableTracing) | ||
assert(actualOptions.dsn == "https://[email protected]/123456") | ||
} | ||
|
||
private fun verifyDefaults(actualOptions: SentryAndroidOptions) { | ||
assertTrue(actualOptions.ignoredExceptionsForType.contains(JavascriptException::class.java)) | ||
assertEquals(RNSentryVersion.ANDROID_SDK_NAME, actualOptions.sdkVersion?.name) | ||
assertEquals( | ||
io.sentry.android.core.BuildConfig.VERSION_NAME, | ||
actualOptions.sdkVersion?.version, | ||
) | ||
val pack = actualOptions.sdkVersion?.packages?.first { it.name == RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_NAME } | ||
assertNotNull(pack) | ||
assertEquals(RNSentryVersion.REACT_NATIVE_SDK_PACKAGE_VERSION, pack?.version) | ||
assertNull(actualOptions.tracesSampleRate) | ||
assertNull(actualOptions.tracesSampler) | ||
assertEquals(false, actualOptions.enableTracing) | ||
} | ||
|
||
private fun verifyFinals(actualOptions: SentryAndroidOptions) { | ||
val event = | ||
SentryEvent().apply { sdk = SdkVersion(RNSentryVersion.ANDROID_SDK_NAME, "1.0") } | ||
val result = actualOptions.beforeSend?.execute(event, Hint()) | ||
assertNotNull(result) | ||
assertEquals("android", result?.getTag("event.origin")) | ||
assertEquals("java", result?.getTag("event.environment")) | ||
} | ||
} |
Oops, something went wrong.