diff --git a/android/build.gradle b/android/build.gradle index 3dc151b6..bd042c03 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -68,6 +68,13 @@ android { } } } + + testOptions { + unitTests { + includeAndroidResources = false + returnDefaultValues = true + } + } } repositories { @@ -86,4 +93,7 @@ dependencies { implementation "com.google.firebase:firebase-messaging:24.1.2" implementation 'io.intercom.android:intercom-sdk:18.2.0' implementation 'io.intercom.android:intercom-sdk-ui:18.2.0' + + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.mockito:mockito-core:5.11.0' } diff --git a/android/src/main/java/com/intercom/reactnative/IntercomHelpers.java b/android/src/main/java/com/intercom/reactnative/IntercomHelpers.java index b40fa86f..7cef1ed1 100644 --- a/android/src/main/java/com/intercom/reactnative/IntercomHelpers.java +++ b/android/src/main/java/com/intercom/reactnative/IntercomHelpers.java @@ -248,6 +248,9 @@ public static String getValueAsStringForKey(ReadableMap map, String key) { public static WritableMap deconstructRegistration(Registration registration) { WritableMap registrationMap = Arguments.createMap(); + if (registration == null) { + return registrationMap; + } if (registration.getEmail() != null) { registrationMap.putString("email", registration.getEmail()); } diff --git a/android/src/test/java/com/intercom/reactnative/IntercomHelpersTest.java b/android/src/test/java/com/intercom/reactnative/IntercomHelpersTest.java new file mode 100644 index 00000000..39c33c32 --- /dev/null +++ b/android/src/test/java/com/intercom/reactnative/IntercomHelpersTest.java @@ -0,0 +1,63 @@ +package com.intercom.reactnative; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mockStatic; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.JavaOnlyMap; +import com.facebook.react.bridge.WritableMap; + +import io.intercom.android.sdk.identity.Registration; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.MockedStatic; + +public class IntercomHelpersTest { + + private MockedStatic argumentsMock; + + @Before + public void setUp() { + argumentsMock = mockStatic(Arguments.class); + argumentsMock.when(Arguments::createMap).thenAnswer(invocation -> new JavaOnlyMap()); + } + + @After + public void tearDown() { + argumentsMock.close(); + } + + // Regression guard for intercom/intercom#520691: a null Registration arrives here whenever + // Intercom.client().fetchLoggedInUserAttributes() returns null (no logged-in user, app + // re-launched after OS kill, etc.). Before the fix, this crashed the host process with + // NullPointerException on registration.getEmail(). + @Test + public void deconstructRegistration_returnsEmptyMap_whenRegistrationIsNull() { + WritableMap result = IntercomHelpers.deconstructRegistration(null); + + assertNotNull("expected an empty map, not null", result); + JavaOnlyMap map = (JavaOnlyMap) result; + assertFalse("expected no email key when registration is null", map.hasKey("email")); + assertFalse("expected no userId key when registration is null", map.hasKey("userId")); + } + + @Test + public void deconstructRegistration_returnsBothFields_whenRegistrationIsFullyPopulated() { + Registration registration = new Registration() + .withEmail("test@example.com") + .withUserId("user-42"); + + WritableMap result = IntercomHelpers.deconstructRegistration(registration); + JavaOnlyMap map = (JavaOnlyMap) result; + + assertTrue(map.hasKey("email")); + assertEquals("test@example.com", map.getString("email")); + assertTrue(map.hasKey("userId")); + assertEquals("user-42", map.getString("userId")); + } +}