Skip to content

Commit ecbeb51

Browse files
authored
[Android] [WebView] Convert successful init message to internal log (#860)
* [Android] Remove webview debug * bazel test fix
1 parent 1321c63 commit ecbeb51

File tree

6 files changed

+171
-2
lines changed

6 files changed

+171
-2
lines changed

platform/jvm/capture/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ bitdrift_kt_android_local_test(
104104
artifact("org.robolectric:robolectric"),
105105
"@rules_robolectric//bazel:android-all",
106106
artifact("androidx.test:core"),
107+
artifact("androidx.webkit:webkit"),
107108
artifact("org.mockito:mockito-core"),
108109
artifact("com.nhaarman.mockitokotlin2:mockito-kotlin"),
109110
artifact("com.squareup.okhttp3:mockwebserver"),

platform/jvm/capture/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ dependencies {
4141
testImplementation(libs.mockito.core)
4242
testImplementation(libs.mockito.kotlin)
4343
testImplementation(libs.androidx.test.core)
44+
testImplementation(libs.androidx.webkit)
4445
testImplementation(libs.robolectric)
4546
testImplementation(libs.mockwebserver)
4647
testImplementation(libs.retrofit)

platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/webview/WebViewCapture.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ import androidx.webkit.WebViewCompat
1414
import androidx.webkit.WebViewFeature
1515
import io.bitdrift.capture.Capture
1616
import io.bitdrift.capture.Capture.LOG_TAG
17+
import io.bitdrift.capture.IInternalLogger
1718
import io.bitdrift.capture.ILogger
1819
import io.bitdrift.capture.LogLevel
20+
import io.bitdrift.capture.LogType
1921
import io.bitdrift.capture.LoggerImpl
2022
import io.bitdrift.capture.experimental.ExperimentalBitdriftApi
2123
import io.bitdrift.capture.providers.ArrayFields
@@ -143,7 +145,7 @@ internal object WebViewCapture {
143145
@SuppressLint("RequiresFeature")
144146
private fun injectScript(
145147
webview: WebView,
146-
logger: ILogger?,
148+
logger: IInternalLogger?,
147149
config: WebViewConfiguration,
148150
) {
149151
runCatching {
@@ -153,7 +155,7 @@ internal object WebViewCapture {
153155
script,
154156
setOf("*"), // Apply to all frames
155157
)
156-
logger?.log(LogLevel.DEBUG, ArrayFields.EMPTY) {
158+
logger?.logInternal(type = LogType.INTERNALSDK, LogLevel.DEBUG, ArrayFields.EMPTY) {
157159
"WebView bridge script injected successfully"
158160
}
159161
}.getOrElse { error ->
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// capture-sdk - bitdrift's client SDK
2+
// Copyright Bitdrift, Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a source available license that can be found in the
5+
// LICENSE file or at:
6+
// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt
7+
8+
package io.bitdrift.capture.webview
9+
10+
import android.webkit.WebView
11+
import androidx.webkit.WebViewCompat
12+
import org.robolectric.annotation.Implementation
13+
import org.robolectric.annotation.Implements
14+
15+
@Implements(WebViewCompat::class, isInAndroidSdk = false)
16+
object ShadowWebViewCompat {
17+
@Implementation
18+
@JvmStatic
19+
fun addDocumentStartJavaScript(
20+
@Suppress("UNUSED_PARAMETER") webView: WebView,
21+
@Suppress("UNUSED_PARAMETER") script: String,
22+
@Suppress("UNUSED_PARAMETER") allowedOriginRules: Set<String>,
23+
) {
24+
// no-op
25+
}
26+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// capture-sdk - bitdrift's client SDK
2+
// Copyright Bitdrift, Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a source available license that can be found in the
5+
// LICENSE file or at:
6+
// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt
7+
8+
package io.bitdrift.capture.webview
9+
10+
import androidx.webkit.WebViewFeature
11+
import org.robolectric.annotation.Implementation
12+
import org.robolectric.annotation.Implements
13+
14+
@Implements(WebViewFeature::class, isInAndroidSdk = false)
15+
@Suppress("detekt:FunctionOnlyReturningConstant")
16+
object ShadowWebViewFeature {
17+
@Implementation
18+
@JvmStatic
19+
fun isFeatureSupported(
20+
@Suppress("UNUSED_PARAMETER") feature: String,
21+
): Boolean = true
22+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// capture-sdk - bitdrift's client SDK
2+
// Copyright Bitdrift, Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a source available license that can be found in the
5+
// LICENSE file or at:
6+
// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt
7+
8+
package io.bitdrift.capture.webview
9+
10+
import android.content.Context
11+
import android.webkit.WebView
12+
import androidx.test.core.app.ApplicationProvider
13+
import com.nhaarman.mockitokotlin2.argumentCaptor
14+
import com.nhaarman.mockitokotlin2.eq
15+
import com.nhaarman.mockitokotlin2.spy
16+
import com.nhaarman.mockitokotlin2.verify
17+
import io.bitdrift.capture.Capture
18+
import io.bitdrift.capture.Configuration
19+
import io.bitdrift.capture.ContextHolder
20+
import io.bitdrift.capture.LogLevel
21+
import io.bitdrift.capture.LogType
22+
import io.bitdrift.capture.LoggerImpl
23+
import io.bitdrift.capture.providers.ArrayFields
24+
import io.bitdrift.capture.providers.SystemDateProvider
25+
import io.bitdrift.capture.providers.session.SessionStrategy
26+
import io.bitdrift.capture.utils.toStringMap
27+
import org.assertj.core.api.Assertions.assertThat
28+
import org.junit.After
29+
import org.junit.Before
30+
import org.junit.Test
31+
import org.junit.runner.RunWith
32+
import org.robolectric.RobolectricTestRunner
33+
import org.robolectric.annotation.Config
34+
35+
@RunWith(RobolectricTestRunner::class)
36+
@Config(sdk = [24], shadows = [ShadowWebViewFeature::class, ShadowWebViewCompat::class])
37+
class WebViewCaptureTest {
38+
private lateinit var webView: WebView
39+
private lateinit var appContext: Context
40+
private val fieldsCaptor = argumentCaptor<ArrayFields>()
41+
private val messageCaptor = argumentCaptor<() -> String>()
42+
43+
@Before
44+
fun setup() {
45+
appContext = ApplicationProvider.getApplicationContext()
46+
val initializer = ContextHolder()
47+
initializer.create(appContext)
48+
webView = WebView(appContext)
49+
}
50+
51+
@After
52+
fun tearDown() {
53+
Capture.Logger.resetShared()
54+
}
55+
56+
@Test
57+
fun instrument_withoutSdkStarted_shouldNotEnableJavascript() {
58+
WebViewCapture.instrument(webView)
59+
60+
assertThat(webView.settings.javaScriptEnabled).isFalse()
61+
}
62+
63+
@Test
64+
fun instrument_withSdkStartedButNoWebViewConfiguration_shouldLogNotInitialized() {
65+
startSdk(webViewConfiguration = null)
66+
val spyLogger = spyLogger()
67+
68+
WebViewCapture.instrument(webView, spyLogger)
69+
70+
assertThat(webView.settings.javaScriptEnabled).isFalse()
71+
verify(spyLogger).log(
72+
eq(LogLevel.WARNING),
73+
fieldsCaptor.capture(),
74+
eq(null),
75+
messageCaptor.capture(),
76+
)
77+
val fields = fieldsCaptor.firstValue.toStringMap()
78+
assertThat(fields["reason"]).isEqualTo("WebViewConfiguration not provided")
79+
assertThat(fields["_source"]).isEqualTo("webview")
80+
assertThat(messageCaptor.firstValue()).isEqualTo("webview.notInitialized")
81+
}
82+
83+
@Test
84+
fun instrument_withValidWebViewConfiguration_shouldEnableJavascriptAndLogSuccess() {
85+
startSdk(webViewConfiguration = WebViewConfiguration())
86+
val spyLogger = spyLogger()
87+
88+
WebViewCapture.instrument(webView, spyLogger)
89+
90+
assertThat(webView.settings.javaScriptEnabled).isTrue()
91+
verify(spyLogger).logInternal(
92+
eq(LogType.INTERNALSDK),
93+
eq(LogLevel.DEBUG),
94+
eq(ArrayFields.EMPTY),
95+
eq(ArrayFields.EMPTY),
96+
eq(null),
97+
eq(false),
98+
messageCaptor.capture(),
99+
)
100+
assertThat(messageCaptor.firstValue()).isEqualTo("WebView bridge script injected successfully")
101+
}
102+
103+
private fun startSdk(webViewConfiguration: WebViewConfiguration?) {
104+
Capture.Logger.start(
105+
apiKey = "test",
106+
sessionStrategy = SessionStrategy.Fixed(),
107+
configuration = Configuration(webViewConfiguration = webViewConfiguration),
108+
dateProvider = SystemDateProvider(),
109+
context = appContext,
110+
)
111+
}
112+
113+
private fun spyLogger(): LoggerImpl {
114+
val logger = Capture.logger()
115+
return spy(logger as LoggerImpl)
116+
}
117+
}

0 commit comments

Comments
 (0)