Skip to content

Commit 506cdae

Browse files
committed
Add tests
1 parent 0f55f08 commit 506cdae

File tree

3 files changed

+257
-1
lines changed

3 files changed

+257
-1
lines changed

app/src/test/java/com/duckduckgo/app/browser/RealDuckDuckGoWebViewTest.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package com.duckduckgo.app.browser
22

33
import androidx.test.ext.junit.runners.AndroidJUnit4
44
import androidx.test.platform.app.InstrumentationRegistry
5+
import androidx.webkit.WebViewCompat.WebMessageListener
56
import com.duckduckgo.app.browser.api.WebViewCapabilityChecker
7+
import com.duckduckgo.app.browser.api.WebViewCapabilityChecker.WebViewCapability
68
import com.duckduckgo.app.browser.api.WebViewCapabilityChecker.WebViewCapability.DocumentStartJavaScript
79
import com.duckduckgo.contentscopescripts.impl.WebViewCompatWrapper
810
import kotlinx.coroutines.test.runTest
@@ -21,6 +23,7 @@ class RealDuckDuckGoWebViewTest {
2123

2224
private val mockWebViewCapabilityChecker: WebViewCapabilityChecker = mock()
2325
private val mockWebViewCompatWrapper: WebViewCompatWrapper = mock()
26+
private val mockWebMessageListener: WebMessageListener = mock()
2427

2528
@Before
2629
fun setUp() {
@@ -45,4 +48,39 @@ class RealDuckDuckGoWebViewTest {
4548

4649
verify(mockWebViewCompatWrapper, never()).addDocumentStartJavaScript(testee, "script", setOf("*"))
4750
}
51+
52+
@Test
53+
fun whenSafeAddWebMessageListenerWithFeatureEnabledThenAddListener() = runTest {
54+
whenever(mockWebViewCapabilityChecker.isSupported(WebViewCapability.WebMessageListener)).thenReturn(true)
55+
56+
testee.safeAddWebMessageListener("test", setOf("*"), mockWebMessageListener)
57+
verify(mockWebViewCompatWrapper)
58+
.addWebMessageListener(testee, "test", setOf("*"), mockWebMessageListener)
59+
}
60+
61+
@Test
62+
fun whenSafeAddWebMessageListenerWithFeatureDisabledThenDoNotAddListener() = runTest {
63+
whenever(mockWebViewCapabilityChecker.isSupported(WebViewCapability.WebMessageListener)).thenReturn(false)
64+
65+
testee.safeAddWebMessageListener("test", setOf("*"), mockWebMessageListener)
66+
verify(mockWebViewCompatWrapper, never())
67+
.addWebMessageListener(testee, "test", setOf("*"), mockWebMessageListener)
68+
}
69+
70+
@Test
71+
fun whenSafeRemoveWebMessageListenerWithFeatureEnabledThenRemoveListener() = runTest {
72+
whenever(mockWebViewCapabilityChecker.isSupported(WebViewCapability.WebMessageListener)).thenReturn(true)
73+
74+
testee.safeRemoveWebMessageListener("test")
75+
76+
verify(mockWebViewCompatWrapper).removeWebMessageListener(testee, "test")
77+
}
78+
79+
@Test
80+
fun whenSafeRemoveWebMessageListenerWithFeatureDisabledThenDoNotRemoveListener() = runTest {
81+
whenever(mockWebViewCapabilityChecker.isSupported(WebViewCapability.WebMessageListener)).thenReturn(false)
82+
83+
testee.safeRemoveWebMessageListener("test")
84+
verify(mockWebViewCompatWrapper, never()).removeWebMessageListener(testee, "test")
85+
}
4886
}

content-scope-scripts/content-scope-scripts-impl/src/main/java/com/duckduckgo/contentscopescripts/impl/messaging/AdsjsContentScopeMessaging.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.duckduckgo.contentscopescripts.impl.messaging
1818

1919
import android.webkit.WebView
20+
import androidx.annotation.VisibleForTesting
2021
import com.duckduckgo.app.browser.api.DuckDuckGoWebView
2122
import com.duckduckgo.common.utils.DispatcherProvider
2223
import com.duckduckgo.common.utils.plugins.PluginPoint
@@ -54,7 +55,8 @@ class AdsjsContentScopeMessaging @Inject constructor(
5455
override val context: String = "contentScopeScripts"
5556
override val allowedDomains: Set<String> = setOf("*")
5657

57-
private fun process(
58+
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
59+
internal fun process(
5860
message: String,
5961
jsMessageCallback: JsMessageCallback,
6062
) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Copyright (c) 2023 DuckDuckGo
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.duckduckgo.contentscopescripts.impl.messaging
18+
19+
import androidx.test.ext.junit.runners.AndroidJUnit4
20+
import com.duckduckgo.app.browser.api.DuckDuckGoWebView
21+
import com.duckduckgo.common.test.CoroutineTestRule
22+
import com.duckduckgo.common.utils.plugins.PluginPoint
23+
import com.duckduckgo.contentscopescripts.api.AdsjsContentScopeJsMessageHandlersPlugin
24+
import com.duckduckgo.contentscopescripts.api.GlobalContentScopeJsMessageHandlersPlugin
25+
import com.duckduckgo.contentscopescripts.api.GlobalJsMessageHandler
26+
import com.duckduckgo.contentscopescripts.impl.AdsJsContentScopeScripts
27+
import com.duckduckgo.js.messaging.api.AdsjsMessageHandler
28+
import com.duckduckgo.js.messaging.api.JsMessage
29+
import com.duckduckgo.js.messaging.api.JsMessageCallback
30+
import junit.framework.TestCase.assertEquals
31+
import kotlinx.coroutines.test.runTest
32+
import org.json.JSONObject
33+
import org.junit.Before
34+
import org.junit.Rule
35+
import org.junit.Test
36+
import org.junit.runner.RunWith
37+
import org.mockito.kotlin.any
38+
import org.mockito.kotlin.mock
39+
import org.mockito.kotlin.never
40+
import org.mockito.kotlin.verify
41+
import org.mockito.kotlin.whenever
42+
43+
@RunWith(AndroidJUnit4::class)
44+
class AdsjsContentScopeScriptsJsMessagingTest {
45+
@get:Rule var coroutineRule = CoroutineTestRule()
46+
47+
private val mockWebView: DuckDuckGoWebView = mock()
48+
private val adsJsContentScopeScripts: AdsJsContentScopeScripts = mock()
49+
private val handlers: PluginPoint<AdsjsContentScopeJsMessageHandlersPlugin> = FakePluginPoint()
50+
private val globalHandlers: PluginPoint<GlobalContentScopeJsMessageHandlersPlugin> = FakeGlobalHandlersPluginPoint()
51+
private lateinit var contentScopeScriptsJsMessaging: AdsjsContentScopeMessaging
52+
53+
private class FakePluginPoint : PluginPoint<AdsjsContentScopeJsMessageHandlersPlugin> {
54+
override fun getPlugins(): Collection<AdsjsContentScopeJsMessageHandlersPlugin> {
55+
return listOf(FakePlugin())
56+
}
57+
58+
inner class FakePlugin : AdsjsContentScopeJsMessageHandlersPlugin {
59+
override fun getJsMessageHandler(): AdsjsMessageHandler {
60+
return object : AdsjsMessageHandler {
61+
override fun process(
62+
jsMessage: JsMessage,
63+
jsMessageCallback: JsMessageCallback?,
64+
) {
65+
jsMessageCallback?.process(jsMessage.featureName, jsMessage.method, jsMessage.id, jsMessage.params)
66+
}
67+
68+
override val featureName: String = "webCompat"
69+
override val methods: List<String> = listOf("webShare", "permissionsQuery")
70+
}
71+
}
72+
}
73+
}
74+
75+
private class FakeGlobalHandlersPluginPoint : PluginPoint<GlobalContentScopeJsMessageHandlersPlugin> {
76+
override fun getPlugins(): Collection<GlobalContentScopeJsMessageHandlersPlugin> {
77+
return listOf(FakeGlobalHandlerPlugin())
78+
}
79+
80+
inner class FakeGlobalHandlerPlugin : GlobalContentScopeJsMessageHandlersPlugin {
81+
82+
override fun getGlobalJsMessageHandler(): GlobalJsMessageHandler {
83+
return object : GlobalJsMessageHandler {
84+
85+
override fun process(
86+
jsMessage: JsMessage,
87+
jsMessageCallback: JsMessageCallback,
88+
) {
89+
jsMessageCallback.process(jsMessage.featureName, jsMessage.method, jsMessage.id, jsMessage.params)
90+
}
91+
92+
override val method: String = "addDebugFlag"
93+
}
94+
}
95+
}
96+
}
97+
98+
@Before
99+
fun setUp() = runTest {
100+
whenever(adsJsContentScopeScripts.isEnabled()).thenReturn(true)
101+
contentScopeScriptsJsMessaging = AdsjsContentScopeMessaging(
102+
handlers = handlers,
103+
globalHandlers = globalHandlers,
104+
adsJsContentScopeScripts = adsJsContentScopeScripts,
105+
coroutineRule.testDispatcherProvider,
106+
)
107+
}
108+
109+
@Test
110+
fun `when process and message can be handled then execute callback`() = runTest {
111+
givenInterfaceIsRegistered()
112+
113+
val message = """
114+
{"context":"contentScopeScripts","featureName":"webCompat","id":"myId","method":"webShare","params":{}}
115+
""".trimIndent()
116+
117+
contentScopeScriptsJsMessaging.process(message, callback)
118+
119+
assertEquals(1, callback.counter)
120+
}
121+
122+
@Test
123+
fun `when processing unknown message do nothing`() = runTest {
124+
givenInterfaceIsRegistered()
125+
126+
contentScopeScriptsJsMessaging.process("", callback)
127+
128+
assertEquals(0, callback.counter)
129+
}
130+
131+
@Test
132+
fun `when feature does not match do nothing`() = runTest {
133+
givenInterfaceIsRegistered()
134+
135+
val message = """
136+
{"context":"contentScopeScripts","featureName":"test","id":"myId","method":"webShare","params":{}}
137+
""".trimIndent()
138+
139+
contentScopeScriptsJsMessaging.process(message, callback)
140+
141+
assertEquals(0, callback.counter)
142+
}
143+
144+
@Test
145+
fun `when id does not exist do nothing`() = runTest {
146+
givenInterfaceIsRegistered()
147+
148+
val message = """
149+
{"context":"contentScopeScripts","webCompat":"test","method":"webShare","params":{}}
150+
""".trimIndent()
151+
152+
contentScopeScriptsJsMessaging.process(message, callback)
153+
154+
assertEquals(0, callback.counter)
155+
}
156+
157+
@Test
158+
fun `when processing addDebugFlag message then process message`() = runTest {
159+
givenInterfaceIsRegistered()
160+
161+
val message = """
162+
{"context":"contentScopeScripts","featureName":"debugFeature","id":"debugId","method":"addDebugFlag","params":{}}
163+
""".trimIndent()
164+
165+
contentScopeScriptsJsMessaging.process(message, callback)
166+
167+
assertEquals(1, callback.counter)
168+
}
169+
170+
@Test
171+
fun `when registering and adsjs is disabled then do not register`() = runTest {
172+
whenever(adsJsContentScopeScripts.isEnabled()).thenReturn(false)
173+
174+
contentScopeScriptsJsMessaging.register(mockWebView, callback)
175+
176+
verify(mockWebView, never()).safeAddWebMessageListener(any(), any(), any())
177+
}
178+
179+
@Test
180+
fun `when registering and adsjs is enabled then register`() = runTest {
181+
whenever(adsJsContentScopeScripts.isEnabled()).thenReturn(true)
182+
183+
contentScopeScriptsJsMessaging.register(mockWebView, callback)
184+
185+
verify(mockWebView).safeAddWebMessageListener(any(), any(), any())
186+
}
187+
188+
@Test
189+
fun `when unregistering and adsjs is disabled then do not unregister`() = runTest {
190+
whenever(adsJsContentScopeScripts.isEnabled()).thenReturn(false)
191+
192+
contentScopeScriptsJsMessaging.unregister(mockWebView)
193+
194+
verify(mockWebView, never()).safeRemoveWebMessageListener(any())
195+
}
196+
197+
@Test
198+
fun `when unregistering and adsjs is enabled then unregister`() = runTest {
199+
whenever(adsJsContentScopeScripts.isEnabled()).thenReturn(true)
200+
201+
contentScopeScriptsJsMessaging.unregister(mockWebView)
202+
203+
verify(mockWebView).safeRemoveWebMessageListener(any())
204+
}
205+
206+
private val callback = object : JsMessageCallback() {
207+
var counter = 0
208+
override fun process(featureName: String, method: String, id: String?, data: JSONObject?) {
209+
counter++
210+
}
211+
}
212+
213+
private fun givenInterfaceIsRegistered() = runTest {
214+
contentScopeScriptsJsMessaging.register(mockWebView, callback)
215+
}
216+
}

0 commit comments

Comments
 (0)