1+ /*
2+ * Copyright (c) 2020 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.app.cta.ui
18+
19+ import com.duckduckgo.app.browser.defaultbrowsing.DefaultBrowserDetector
20+ import com.duckduckgo.app.cta.db.DismissedCtaDao
21+ import com.duckduckgo.app.cta.model.CtaId
22+ import com.duckduckgo.app.statistics.Variant
23+ import com.duckduckgo.app.statistics.VariantManager
24+ import com.duckduckgo.app.widget.ui.WidgetCapabilities
25+ import com.nhaarman.mockitokotlin2.mock
26+ import com.nhaarman.mockitokotlin2.whenever
27+ import org.junit.Assert.assertNull
28+ import org.junit.Assert.assertTrue
29+ import org.junit.Assume.assumeFalse
30+ import org.junit.Assume.assumeTrue
31+ import org.junit.experimental.theories.DataPoint
32+ import org.junit.experimental.theories.Theories
33+ import org.junit.experimental.theories.Theory
34+ import org.junit.runner.RunWith
35+
36+ @RunWith(Theories ::class )
37+ class CtaViewModelNextCtaTest {
38+
39+ private val mockWidgetCapabilities = mock<WidgetCapabilities >()
40+ private val mockDismissedCtaDao = mock<DismissedCtaDao >()
41+ private val mockVariantManager = mock<VariantManager >()
42+ private val mockDefaultBrowserDetector = mock<DefaultBrowserDetector >()
43+
44+ private val testee = CtaViewModel (
45+ mock(),
46+ mock(),
47+ mock(),
48+ mockWidgetCapabilities,
49+ mockDismissedCtaDao,
50+ mockVariantManager,
51+ mock(),
52+ mock(),
53+ mock(),
54+ mockDefaultBrowserDetector
55+ )
56+
57+ @Theory
58+ fun whenPreviousCtaIsDaxSerpCtaButConditionsAreValidThenExpectedNextCtaIsSearchWidgetCta (
59+ supportsStandardWidgetAdd : Boolean ,
60+ hasInstalledWidget : Boolean ,
61+ hasSearchWidgetDaxCtaFeature : Boolean ,
62+ daxSearchWidgetShown : Boolean ,
63+ daxNetworkDialogShown : Boolean ,
64+ daxTrackersDialogShown : Boolean ,
65+ daxOtherDialogShown : Boolean
66+ ) {
67+ val previousCta = DaxDialogCta .DaxSerpCta (mock(), mock())
68+ assumeTrue(supportsStandardWidgetAdd)
69+ assumeFalse(hasInstalledWidget)
70+ assumeTrue(hasSearchWidgetDaxCtaFeature)
71+ assumeFalse(daxSearchWidgetShown)
72+ assumeTrue(daxNetworkDialogShown || daxTrackersDialogShown || daxOtherDialogShown)
73+ givenSearchWidgetScenario(
74+ supportsStandardWidgetAdd,
75+ hasInstalledWidget,
76+ hasSearchWidgetDaxCtaFeature,
77+ daxSearchWidgetShown,
78+ daxNetworkDialogShown,
79+ daxTrackersDialogShown,
80+ daxOtherDialogShown
81+ )
82+
83+ val nextCta = testee.obtainNextCta(previousCta)
84+
85+ assertTrue(nextCta is DaxDialogCta .SearchWidgetCta )
86+ }
87+
88+ @Theory
89+ fun whenPreviousCtaIsDaxSerpCtaButConditionsAreInvalidThenExpectedNextCtaIsNull (
90+ supportsStandardWidgetAdd : Boolean ,
91+ hasInstalledWidget : Boolean ,
92+ hasSearchWidgetDaxCtaFeature : Boolean ,
93+ daxSearchWidgetShown : Boolean ,
94+ daxNetworkDialogShown : Boolean ,
95+ daxTrackersDialogShown : Boolean ,
96+ daxOtherDialogShown : Boolean
97+ ) {
98+ val previousCta = DaxDialogCta .DaxSerpCta (mock(), mock())
99+ assumeFalse(
100+ supportsStandardWidgetAdd &&
101+ ! hasInstalledWidget &&
102+ hasSearchWidgetDaxCtaFeature &&
103+ ! daxSearchWidgetShown &&
104+ (daxNetworkDialogShown || daxTrackersDialogShown || daxOtherDialogShown)
105+ )
106+ givenSearchWidgetScenario(
107+ supportsStandardWidgetAdd,
108+ hasInstalledWidget,
109+ hasSearchWidgetDaxCtaFeature,
110+ daxSearchWidgetShown,
111+ daxNetworkDialogShown,
112+ daxTrackersDialogShown,
113+ daxOtherDialogShown
114+ )
115+
116+ val nextCta = testee.obtainNextCta(previousCta)
117+
118+ assertNull(nextCta)
119+ }
120+
121+ @Theory
122+ fun whenPreviousCtaIsDaxTrackersBlockedCtaAndConditionsAreValidThenExpectedNextCtaIsDefaultBrowserCta (
123+ deviceSupportsDefaultBrowserConfiguration : Boolean ,
124+ isDefaultBrowser : Boolean ,
125+ hasDefaultBrowserDaxCtaFeature : Boolean ,
126+ daxDefaultBrowserShown : Boolean
127+ ) {
128+ val previousCta = DaxDialogCta .DaxTrackersBlockedCta (mock(), mock(), emptyList(), " " )
129+ assumeTrue(deviceSupportsDefaultBrowserConfiguration)
130+ assumeFalse(isDefaultBrowser)
131+ assumeTrue(hasDefaultBrowserDaxCtaFeature)
132+ assumeFalse(daxDefaultBrowserShown)
133+ givenDefaultBrowserScenario(
134+ deviceSupportsDefaultBrowserConfiguration,
135+ isDefaultBrowser,
136+ hasDefaultBrowserDaxCtaFeature,
137+ daxDefaultBrowserShown
138+ )
139+
140+ val nextCta = testee.obtainNextCta(previousCta)
141+
142+ assertTrue(nextCta is DaxDialogCta .DefaultBrowserCta )
143+ }
144+
145+ @Theory
146+ fun whenPreviousCtaIsDaxTrackersBlockedCtaCtaButConditionsAreInvalidThenExpectedNextCtaIsNull (
147+ deviceSupportsDefaultBrowserConfiguration : Boolean ,
148+ isDefaultBrowser : Boolean ,
149+ hasDefaultBrowserDaxCtaFeature : Boolean ,
150+ daxDefaultBrowserShown : Boolean
151+ ) {
152+ val previousCta = DaxDialogCta .DaxTrackersBlockedCta (mock(), mock(), emptyList(), " " )
153+ assumeFalse(deviceSupportsDefaultBrowserConfiguration && ! isDefaultBrowser && hasDefaultBrowserDaxCtaFeature && ! daxDefaultBrowserShown)
154+ givenDefaultBrowserScenario(
155+ deviceSupportsDefaultBrowserConfiguration,
156+ isDefaultBrowser,
157+ hasDefaultBrowserDaxCtaFeature,
158+ daxDefaultBrowserShown
159+ )
160+
161+ val nextCta = testee.obtainNextCta(previousCta)
162+
163+ assertNull(nextCta)
164+ }
165+
166+ private fun givenDefaultBrowserScenario (
167+ deviceSupportsDefaultBrowserConfiguration : Boolean ,
168+ isDefaultBrowser : Boolean ,
169+ hasDefaultBrowserDaxCtaFeature : Boolean ,
170+ daxDefaultBrowserShown : Boolean
171+ ) {
172+ whenever(mockDefaultBrowserDetector.deviceSupportsDefaultBrowserConfiguration()).thenReturn(deviceSupportsDefaultBrowserConfiguration)
173+ whenever(mockDefaultBrowserDetector.isDefaultBrowser()).thenReturn(isDefaultBrowser)
174+ whenever(mockVariantManager.getVariant()).thenReturn(
175+ Variant (" test" , features = getFeatures(false , hasDefaultBrowserDaxCtaFeature), filterBy = { true })
176+ )
177+ whenever(mockDismissedCtaDao.exists(CtaId .DAX_DIALOG_DEFAULT_BROWSER )).thenReturn(daxDefaultBrowserShown)
178+ }
179+
180+ private fun givenSearchWidgetScenario (
181+ supportsStandardWidgetAdd : Boolean ,
182+ hasInstalledWidget : Boolean ,
183+ hasSearchWidgetDaxCtaFeature : Boolean ,
184+ daxSearchWidgetShown : Boolean ,
185+ daxNetworkDialogShown : Boolean ,
186+ daxTrackersDialogShown : Boolean ,
187+ daxOtherDialogShown : Boolean
188+ ) {
189+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(supportsStandardWidgetAdd)
190+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(hasInstalledWidget)
191+ whenever(mockVariantManager.getVariant()).thenReturn(
192+ Variant (" test" , features = getFeatures(hasSearchWidgetDaxCtaFeature, false ), filterBy = { true })
193+ )
194+ whenever(mockDismissedCtaDao.exists(CtaId .DAX_DIALOG_SEARCH_WIDGET )).thenReturn(daxSearchWidgetShown)
195+ whenever(mockDismissedCtaDao.exists(CtaId .DAX_DIALOG_NETWORK )).thenReturn(daxNetworkDialogShown)
196+ whenever(mockDismissedCtaDao.exists(CtaId .DAX_DIALOG_TRACKERS_FOUND )).thenReturn(daxTrackersDialogShown)
197+ whenever(mockDismissedCtaDao.exists(CtaId .DAX_DIALOG_OTHER )).thenReturn(daxOtherDialogShown)
198+ }
199+
200+ private fun getFeatures (searchWidgetFeature : Boolean , defaultBrowserFeature : Boolean ): List <VariantManager .VariantFeature > {
201+ val featureList = mutableListOf<VariantManager .VariantFeature >()
202+ if (searchWidgetFeature) {
203+ featureList.add(VariantManager .VariantFeature .SearchWidgetDaxCta )
204+ }
205+ if (defaultBrowserFeature) {
206+ featureList.add(VariantManager .VariantFeature .DefaultBrowserDaxCta )
207+ }
208+ return featureList
209+ }
210+
211+ companion object {
212+ @JvmStatic
213+ @DataPoint
214+ fun supportsStandardWidgetAdd () = listOf (true , false )
215+
216+ @JvmStatic
217+ @DataPoint
218+ fun hasInstalledWidget () = listOf (true , false )
219+
220+ @JvmStatic
221+ @DataPoint
222+ fun hasSearchWidgetDaxCtaFeature () = listOf (true , false )
223+
224+ @JvmStatic
225+ @DataPoint
226+ fun daxSearchWidgetShown () = listOf (true , false )
227+
228+ @JvmStatic
229+ @DataPoint
230+ fun deviceSupportsDefaultBrowserConfiguration () = listOf (true , false )
231+
232+ @JvmStatic
233+ @DataPoint
234+ fun isDefaultBrowser () = listOf (true , false )
235+
236+ @JvmStatic
237+ @DataPoint
238+ fun hasDefaultBrowserDaxCtaFeature () = listOf (true , false )
239+
240+ @JvmStatic
241+ @DataPoint
242+ fun daxDefaultBrowserShown () = listOf (true , false )
243+
244+ @JvmStatic
245+ @DataPoint
246+ fun daxNetworkDialogShown () = listOf (true , false )
247+
248+ @JvmStatic
249+ @DataPoint
250+ fun daxTrackersDialogShown () = listOf (true , false )
251+
252+ @JvmStatic
253+ @DataPoint
254+ fun daxOtherDialogShown () = listOf (true , false )
255+ }
256+ }
0 commit comments