Skip to content

Commit 347333c

Browse files
authored
Add deep link tests (#258)
* refactor: uri parameter extraction into its own function. * test: new uri parameter extraction function in DeepLinkUtils. * refactor: use idomatic kotlin.
1 parent 186a791 commit 347333c

File tree

2 files changed

+151
-3
lines changed

2 files changed

+151
-3
lines changed

android/src/main/java/com/segment/analytics/kotlin/android/utilities/DeepLinkUtils.kt

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package com.segment.analytics.kotlin.android.utilities
22

33
import android.content.Intent
4+
import android.net.Uri
45
import com.segment.analytics.kotlin.core.Analytics
6+
import kotlinx.serialization.json.JsonObject
57
import kotlinx.serialization.json.buildJsonObject
68
import kotlinx.serialization.json.put
79

@@ -13,13 +15,32 @@ class DeepLinkUtils(val analytics: Analytics) {
1315
return
1416
}
1517

16-
val properties = buildJsonObject {
18+
val properties = extractLinkProperties(referrer, intent.data)
19+
analytics.track("Deep Link Opened", properties)
20+
}
1721

22+
/**
23+
* Builds a JsonObject with the parameters of a given Uri.
24+
*
25+
* Note: The Uri must be hierarchical (myUri.isHierarchical == true) for parameters to be
26+
* extracted.
27+
*
28+
* Example hierarchical Uri: http://example.com/
29+
* Example non-hierarchical Uri: mailto:[email protected]
30+
*
31+
* Note: we return the given Uri as a property named: "url" since this is what is expected
32+
* upstream.
33+
*/
34+
fun extractLinkProperties(
35+
referrer: String?,
36+
uri: Uri?
37+
): JsonObject {
38+
val properties = buildJsonObject {
1839
referrer?.let {
1940
put("referrer", it)
2041
}
2142

22-
intent.data?.let { uri ->
43+
uri?.let {
2344
if (uri.isHierarchical) {
2445
for (parameter in uri.queryParameterNames) {
2546
val value = uri.getQueryParameter(parameter)
@@ -31,6 +52,7 @@ class DeepLinkUtils(val analytics: Analytics) {
3152
put("url", uri.toString())
3253
}
3354
}
34-
analytics.track("Deep Link Opened", properties)
55+
56+
return properties
3557
}
3658
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.segment.analytics.kotlin.android.utilities
2+
3+
import android.content.Context
4+
import android.content.SharedPreferences
5+
import android.net.Uri
6+
import androidx.test.platform.app.InstrumentationRegistry
7+
import com.segment.analytics.kotlin.android.AndroidStorageProvider
8+
import com.segment.analytics.kotlin.android.plugins.getUniqueID
9+
import com.segment.analytics.kotlin.android.utils.MemorySharedPreferences
10+
import com.segment.analytics.kotlin.android.utils.testAnalytics
11+
import com.segment.analytics.kotlin.core.Analytics
12+
import com.segment.analytics.kotlin.core.Configuration
13+
import com.segment.analytics.kotlin.core.emptyJsonObject
14+
import io.mockk.every
15+
import io.mockk.mockkStatic
16+
import io.mockk.spyk
17+
import kotlinx.coroutines.test.TestScope
18+
import kotlinx.coroutines.test.UnconfinedTestDispatcher
19+
import kotlinx.serialization.json.JsonPrimitive
20+
import kotlinx.serialization.json.buildJsonObject
21+
import org.junit.Assert.assertEquals
22+
import org.junit.Before
23+
import org.junit.Test
24+
import org.junit.runner.RunWith
25+
import org.robolectric.RobolectricTestRunner
26+
import org.robolectric.annotation.Config
27+
28+
@RunWith(RobolectricTestRunner::class)
29+
@Config(manifest = Config.NONE)
30+
class DeepLinkUtilsTests {
31+
lateinit var appContext: Context
32+
lateinit var analytics: Analytics
33+
lateinit var deepLinkUtils: DeepLinkUtils
34+
private val testDispatcher = UnconfinedTestDispatcher()
35+
private val testScope = TestScope(testDispatcher)
36+
37+
38+
@Before
39+
fun setup() {
40+
appContext = spyk(InstrumentationRegistry.getInstrumentation().targetContext)
41+
val sharedPreferences: SharedPreferences = MemorySharedPreferences()
42+
every { appContext.getSharedPreferences(any(), any()) } returns sharedPreferences
43+
mockkStatic("com.segment.analytics.kotlin.android.plugins.AndroidContextPluginKt")
44+
every { getUniqueID() } returns "unknown"
45+
46+
analytics = testAnalytics(
47+
Configuration(
48+
writeKey = "123",
49+
application = appContext,
50+
storageProvider = AndroidStorageProvider
51+
),
52+
testScope, testDispatcher
53+
)
54+
deepLinkUtils = DeepLinkUtils(analytics)
55+
}
56+
57+
@Test
58+
fun extractLinkPropertiesTest() {
59+
val link =
60+
"https://stockx.com/?utm_source=af&utm_medium=imp&utm_campaign=1310690&impactSiteId=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&clickid=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&utm_term=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&utm_content=1868737_570105&irgwc=1&irclickid=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&ir_campaignid=9060&ir_adid=570105&ir_partnerid=1310690&gad_source=1&referrer=gclid%3DCjwKCAiAiaC-BhBEEiwAjY99qHbSPJ49pAI83Lo4L7bV3GKaUxSyOX4lah88GFkcNGYQ_MLIZGwXcBoCFAwQAvD_BwE&gref=EkQKPAoICICJoL4GEEQSLACNj32odtI8nj2kAjzcujgvttXcYppTFLI5fiVqHzwYWRw0ZhD8wshkbBdwGgIUDBAC8P8HARjt_K_sKQ"
61+
62+
val expectedProperties = buildJsonObject {
63+
put(
64+
"referrer",
65+
JsonPrimitive("gclid=CjwKCAiAiaC-BhBEEiwAjY99qHbSPJ49pAI83Lo4L7bV3GKaUxSyOX4lah88GFkcNGYQ_MLIZGwXcBoCFAwQAvD_BwE")
66+
)
67+
put("utm_source", JsonPrimitive("af"))
68+
put("utm_medium", JsonPrimitive("imp"))
69+
put("utm_campaign", JsonPrimitive("1310690"))
70+
put("impactSiteId", JsonPrimitive("VupTG:SM2xyKUReTwuwulVAxUksw710t1yqKR80"))
71+
put("clickid", JsonPrimitive("VupTG:SM2xyKUReTwuwulVAxUksw710t1yqKR80"))
72+
put("utm_term", JsonPrimitive("VupTG:SM2xyKUReTwuwulVAxUksw710t1yqKR80"))
73+
put("utm_content", JsonPrimitive("1868737_570105"))
74+
put("irgwc", JsonPrimitive("1"))
75+
put("irclickid", JsonPrimitive("VupTG:SM2xyKUReTwuwulVAxUksw710t1yqKR80"))
76+
put("ir_campaignid", JsonPrimitive("9060"))
77+
put("ir_adid", JsonPrimitive("570105"))
78+
put("ir_partnerid", JsonPrimitive("1310690"))
79+
put("gad_source", JsonPrimitive("1"))
80+
put(
81+
"gref",
82+
JsonPrimitive("EkQKPAoICICJoL4GEEQSLACNj32odtI8nj2kAjzcujgvttXcYppTFLI5fiVqHzwYWRw0ZhD8wshkbBdwGgIUDBAC8P8HARjt_K_sKQ")
83+
)
84+
put(
85+
"url",
86+
JsonPrimitive("https://stockx.com/?utm_source=af&utm_medium=imp&utm_campaign=1310690&impactSiteId=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&clickid=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&utm_term=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&utm_content=1868737_570105&irgwc=1&irclickid=VupTG%3ASM2xyKUReTwuwulVAxUksw710t1yqKR80&ir_campaignid=9060&ir_adid=570105&ir_partnerid=1310690&gad_source=1&referrer=gclid%3DCjwKCAiAiaC-BhBEEiwAjY99qHbSPJ49pAI83Lo4L7bV3GKaUxSyOX4lah88GFkcNGYQ_MLIZGwXcBoCFAwQAvD_BwE&gref=EkQKPAoICICJoL4GEEQSLACNj32odtI8nj2kAjzcujgvttXcYppTFLI5fiVqHzwYWRw0ZhD8wshkbBdwGgIUDBAC8P8HARjt_K_sKQ")
87+
)
88+
}
89+
90+
// This should extract all query parameters as properties including a value for the referer property
91+
val properties = deepLinkUtils.extractLinkProperties("not used", Uri.parse(link))
92+
93+
assertEquals(expectedProperties, properties)
94+
}
95+
96+
@Test
97+
fun differentUriTest() {
98+
var properties = deepLinkUtils.extractLinkProperties(null, Uri.parse("http://example.com?prop1=foo"))
99+
assertEquals(
100+
buildJsonObject {
101+
put("prop1", JsonPrimitive("foo"))
102+
put("url", JsonPrimitive("http://example.com?prop1=foo"))
103+
},
104+
properties
105+
)
106+
107+
properties = deepLinkUtils.extractLinkProperties(null, Uri.parse("example.com?prop1=foo"))
108+
assertEquals(
109+
buildJsonObject {
110+
put("prop1", JsonPrimitive("foo"))
111+
put("url", JsonPrimitive("example.com?prop1=foo"))
112+
},
113+
properties
114+
)
115+
116+
// Even though this Uri has a "?prop1=foo" string at the end, it's not a known part of
117+
// the Uri scheme so we won't be able to use it.
118+
properties = deepLinkUtils.extractLinkProperties(null, Uri.parse("mailto:[email protected]?prop1=foo"))
119+
assertEquals(
120+
buildJsonObject {
121+
put("url", JsonPrimitive("mailto:[email protected]?prop1=foo"))
122+
},
123+
properties
124+
)
125+
}
126+
}

0 commit comments

Comments
 (0)