@@ -47,19 +47,25 @@ import com.duckduckgo.app.browser.model.LongPressTarget
4747import com.duckduckgo.app.browser.omnibar.OmnibarEntryConverter
4848import com.duckduckgo.app.browser.session.WebViewSessionStorage
4949import com.duckduckgo.app.cta.db.DismissedCtaDao
50+ import com.duckduckgo.app.cta.model.DismissedCta
5051import com.duckduckgo.app.cta.ui.CtaViewModel
52+ import com.duckduckgo.app.cta.ui.DaxBubbleCta
53+ import com.duckduckgo.app.cta.ui.HomePanelCta
5154import com.duckduckgo.app.global.db.AppDatabase
5255import com.duckduckgo.app.global.install.AppInstallStore
5356import com.duckduckgo.app.global.model.SiteFactory
57+ import com.duckduckgo.app.onboarding.store.OnboardingStore
5458import com.duckduckgo.app.privacy.db.NetworkLeaderboardDao
5559import com.duckduckgo.app.privacy.model.PrivacyPractices
5660import com.duckduckgo.app.privacy.model.TestEntity
61+ import com.duckduckgo.app.privacy.store.PrivacySettingsStore
5762import com.duckduckgo.app.settings.db.SettingsDataStore
5863import com.duckduckgo.app.statistics.VariantManager
5964import com.duckduckgo.app.statistics.VariantManager.Companion.DEFAULT_VARIANT
6065import com.duckduckgo.app.statistics.api.StatisticsUpdater
6166import com.duckduckgo.app.statistics.pixels.Pixel
6267import com.duckduckgo.app.survey.db.SurveyDao
68+ import com.duckduckgo.app.survey.model.Survey
6369import com.duckduckgo.app.tabs.model.TabEntity
6470import com.duckduckgo.app.tabs.model.TabRepository
6571import com.duckduckgo.app.trackerdetection.EntityLookup
@@ -156,12 +162,18 @@ class BrowserTabViewModelTest {
156162 @Mock
157163 private lateinit var mockPixel: Pixel
158164
165+ @Mock
166+ private lateinit var mockOnboardingStore: OnboardingStore
167+
159168 @Mock
160169 private lateinit var mockAutoCompleteService: AutoCompleteService
161170
162171 @Mock
163172 private lateinit var mockWidgetCapabilities: WidgetCapabilities
164173
174+ @Mock
175+ private lateinit var mockPrivacySettingsStore: PrivacySettingsStore
176+
165177 private lateinit var mockAutoCompleteApi: AutoCompleteApi
166178
167179 private lateinit var ctaViewModel: CtaViewModel
@@ -190,7 +202,11 @@ class BrowserTabViewModelTest {
190202 mockPixel,
191203 mockSurveyDao,
192204 mockWidgetCapabilities,
193- mockDismissedCtaDao
205+ mockDismissedCtaDao,
206+ mockVariantManager,
207+ mockSettingsStore,
208+ mockOnboardingStore,
209+ mockPrivacySettingsStore
194210 )
195211
196212 val siteFactory = SiteFactory (mockPrivacyPractices, mockEntityLookup)
@@ -201,6 +217,7 @@ class BrowserTabViewModelTest {
201217 whenever(mockTabsRepository.retrieveSiteData(any())).thenReturn(MutableLiveData ())
202218 whenever(mockPrivacyPractices.privacyPracticesFor(any())).thenReturn(PrivacyPractices .UNKNOWN )
203219 whenever(mockAppInstallStore.installTimestamp).thenReturn(System .currentTimeMillis() - TimeUnit .DAYS .toMillis(1 ))
220+ whenever(mockPrivacySettingsStore.privacyOn).thenReturn(true )
204221
205222 testee = BrowserTabViewModel (
206223 statisticsUpdater = mockStatisticsUpdater,
@@ -1271,6 +1288,212 @@ class BrowserTabViewModelTest {
12711288 verify(mockCommandObserver, never()).onChanged(commandCaptor.capture())
12721289 }
12731290
1291+ @Test
1292+ fun whenScheduledSurveyChangesAndInstalledDaysMatchThenCtaIsSurvey () {
1293+ testee.onSurveyChanged(Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1294+ assertTrue(testee.ctaViewState.value!! .cta is HomePanelCta .Survey )
1295+ }
1296+
1297+ @Test
1298+ fun whenScheduledSurveyChangesAndInstalledDaysDontMatchThenCtaIsNull () {
1299+ testee.onSurveyChanged(Survey (" abc" , " http://example.com" , daysInstalled = 2 , status = Survey .Status .SCHEDULED ))
1300+ assertNull(testee.ctaViewState.value!! .cta)
1301+ }
1302+
1303+ @Test
1304+ fun whenScheduledSurveyIsNullThenCtaIsNotSurvey () {
1305+ testee.onSurveyChanged(null )
1306+ assertFalse(testee.ctaViewState.value!! .cta is HomePanelCta .Survey )
1307+ }
1308+
1309+ @Test
1310+ fun whenSurveyCtaDismissedAndNoOtherCtaPossibleCtaIsNull () {
1311+ testee.onSurveyChanged(Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1312+ testee.onUserDismissedCta()
1313+ assertNull(testee.ctaViewState.value!! .cta)
1314+ }
1315+
1316+ @Test
1317+ fun whenSurveyCtaDismissedAndWidgetCtaIsPossibleThenNextCtaIsWidget () {
1318+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(true )
1319+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(true )
1320+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(false )
1321+
1322+ testee.onSurveyChanged(Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1323+ testee.onUserDismissedCta()
1324+ assertEquals(HomePanelCta .AddWidgetAuto , testee.ctaViewState.value!! .cta)
1325+ }
1326+
1327+ @Test
1328+ fun whenCtaRefreshedAndAutoAddSupportedAndWidgetNotInstalledThenCtaIsAutoWidget () = ruleRunBlockingTest {
1329+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(true )
1330+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(true )
1331+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(false )
1332+ testee.refreshCta(true )
1333+ assertEquals(HomePanelCta .AddWidgetAuto , testee.ctaViewState.value!! .cta)
1334+ }
1335+
1336+ @Test
1337+ fun whenCtaRefreshedAndAutoAddSupportedAndWidgetAlreadyInstalledThenCtaIsNull () = ruleRunBlockingTest {
1338+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(true )
1339+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(true )
1340+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(true )
1341+ testee.refreshCta(true )
1342+ assertNull(testee.ctaViewState.value!! .cta)
1343+ }
1344+
1345+ @Test
1346+ fun whenCtaRefreshedAndOnlyStandardAddSupportedAndWidgetNotInstalledThenCtaIsInstructionsWidget () = ruleRunBlockingTest {
1347+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(true )
1348+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(false )
1349+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(false )
1350+ testee.refreshCta(true )
1351+ assertEquals(HomePanelCta .AddWidgetInstructions , testee.ctaViewState.value!! .cta)
1352+ }
1353+
1354+ @Test
1355+ fun whenCtaRefreshedAndOnlyStandardAddSupportedAndWidgetAlreadyInstalledThenCtaIsNull () = ruleRunBlockingTest {
1356+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(true )
1357+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(false )
1358+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(true )
1359+ testee.refreshCta(true )
1360+ assertNull(testee.ctaViewState.value!! .cta)
1361+ }
1362+
1363+ @Test
1364+ fun whenCtaRefreshedAndStandardAddNotSupportedAndWidgetNotInstalledThenCtaIsNull () = ruleRunBlockingTest {
1365+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(false )
1366+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(false )
1367+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(false )
1368+ testee.refreshCta(true )
1369+ assertNull(testee.ctaViewState.value!! .cta)
1370+ }
1371+
1372+ @Test
1373+ fun whenCtaRefreshedAndStandardAddNotSupportedAndWidgetAlreadyInstalledThenCtaIsNull () = ruleRunBlockingTest {
1374+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(false )
1375+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(false )
1376+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(true )
1377+ testee.refreshCta(true )
1378+ assertNull(testee.ctaViewState.value!! .cta)
1379+ }
1380+
1381+ @Test
1382+ fun whenCtaRefreshedAndIsNewTabIsFalseThenReturnNull () = ruleRunBlockingTest {
1383+ whenever(mockWidgetCapabilities.supportsStandardWidgetAdd).thenReturn(true )
1384+ whenever(mockWidgetCapabilities.supportsAutomaticWidgetAdd).thenReturn(true )
1385+ whenever(mockWidgetCapabilities.hasInstalledWidgets).thenReturn(false )
1386+ testee.refreshCta(false )
1387+ assertNull(testee.ctaViewState.value!! .cta)
1388+ }
1389+
1390+ @Test
1391+ fun whenCtaShownThenFirePixel () {
1392+ val cta = HomePanelCta .Survey (Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1393+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1394+
1395+ testee.onCtaShown()
1396+ verify(mockPixel).fire(cta.shownPixel!! , cta.pixelShownParameters())
1397+ }
1398+
1399+ @Test
1400+ fun whenManualCtaShownThenFirePixel () {
1401+ val cta = HomePanelCta .Survey (Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1402+
1403+ testee.onManualCtaShown(cta)
1404+ verify(mockPixel).fire(cta.shownPixel!! , cta.pixelShownParameters())
1405+ }
1406+
1407+ @Test
1408+ fun whenRegisterDaxBubbleCtaShownThenFirePixel () {
1409+ val cta = DaxBubbleCta .DaxIntroCta (mockOnboardingStore, mockAppInstallStore)
1410+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1411+
1412+ testee.registerDaxBubbleCtaShown()
1413+ verify(mockPixel).fire(cta.shownPixel!! , cta.pixelShownParameters())
1414+ }
1415+
1416+ @Test
1417+ fun whenRegisterDaxBubbleCtaShownThenRegisterInDatabase () {
1418+ val cta = DaxBubbleCta .DaxIntroCta (mockOnboardingStore, mockAppInstallStore)
1419+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1420+
1421+ testee.registerDaxBubbleCtaShown()
1422+ verify(mockDismissedCtaDao).insert(DismissedCta (cta.ctaId))
1423+ }
1424+
1425+ @Test
1426+ fun whenUserClickedCtaButtonThenFirePixel () {
1427+ val cta = DaxBubbleCta .DaxIntroCta (mockOnboardingStore, mockAppInstallStore)
1428+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1429+
1430+ testee.onUserClickCtaOkButton()
1431+ verify(mockPixel).fire(cta.okPixel!! , cta.pixelOkParameters())
1432+ }
1433+
1434+ @Test
1435+ fun whenUserClickedCtaButtonThenLaunchSurveyCommand () {
1436+ val cta = HomePanelCta .Survey (Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1437+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1438+
1439+ testee.onUserClickCtaOkButton()
1440+ assertCommandIssued<Command .LaunchSurvey >()
1441+ }
1442+
1443+ @Test
1444+ fun whenUserClickedCtaButtonThenLaunchAddWidgetCommand () {
1445+ val cta = HomePanelCta .AddWidgetAuto
1446+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1447+
1448+ testee.onUserClickCtaOkButton()
1449+ assertCommandIssued<Command .LaunchAddWidget >()
1450+ }
1451+
1452+ @Test
1453+ fun whenUserClickedCtaButtonThenLaunchLegacyAddWidgetCommand () {
1454+ val cta = HomePanelCta .AddWidgetInstructions
1455+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1456+
1457+ testee.onUserClickCtaOkButton()
1458+ assertCommandIssued<Command .LaunchLegacyAddWidget >()
1459+ }
1460+
1461+ @Test
1462+ fun whenUserDismissedCtaThenFirePixel () {
1463+ val cta = HomePanelCta .Survey (Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1464+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1465+
1466+ testee.onUserDismissedCta()
1467+ verify(mockPixel).fire(cta.cancelPixel!! , cta.pixelCancelParameters())
1468+ }
1469+
1470+ @Test
1471+ fun whenUserDismissedCtaThenRegisterInDatabase () {
1472+ val cta = HomePanelCta .AddWidgetAuto
1473+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1474+
1475+ testee.onUserDismissedCta()
1476+ verify(mockDismissedCtaDao).insert(DismissedCta (cta.ctaId))
1477+ }
1478+
1479+ @Test
1480+ fun whenUserDismissedSurveyCtaThenDoNotRegisterInDatabase () {
1481+ val cta = HomePanelCta .Survey (Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1482+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1483+
1484+ testee.onUserDismissedCta()
1485+ verify(mockDismissedCtaDao, never()).insert(DismissedCta (cta.ctaId))
1486+ }
1487+
1488+ @Test
1489+ fun whenUserDismissedSurveyCtaThenCancelScheduledSurveys () {
1490+ val cta = HomePanelCta .Survey (Survey (" abc" , " http://example.com" , daysInstalled = 1 , status = Survey .Status .SCHEDULED ))
1491+ testee.ctaViewState.value = BrowserTabViewModel .CtaViewState (cta = cta)
1492+
1493+ testee.onUserDismissedCta()
1494+ verify(mockSurveyDao).cancelScheduledSurveys()
1495+ }
1496+
12741497 private inline fun <reified T : Command > assertCommandIssued (instanceAssertions : T .() -> Unit = {}) {
12751498 verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture())
12761499 val issuedCommand = commandCaptor.allValues.find { it is T }
0 commit comments