Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
353ecf3
Delete privacy store add whitelist functionality and update toggle
subsymbolic Apr 9, 2020
263c9f3
Update pixels
subsymbolic Apr 24, 2020
94398b6
New buttons
subsymbolic Apr 24, 2020
2ccff7d
Add whitelist
subsymbolic Apr 30, 2020
2a5b9ab
Merge branch 'develop' into feature/mia/toggle
subsymbolic May 1, 2020
e374aef
Add db migration test
subsymbolic May 1, 2020
0ae138a
Add browser menu item, error messages and more tests
subsymbolic May 4, 2020
ae745be
Merge branch 'develop' into feature/mia/toggle
subsymbolic May 4, 2020
adfb5f2
Lint tidy up
subsymbolic May 4, 2020
e276c1d
Merge branch 'develop' into feature/mia/toggle
subsymbolic May 5, 2020
bf5197d
Add icon tint
subsymbolic May 6, 2020
b73f89d
Merge branch 'develop' into feature/mia/toggle
subsymbolic May 7, 2020
7b271e7
Fix double pixel when activities not kept
subsymbolic May 8, 2020
528924c
Remove mixed viewstate set and post value and access of viewState fro…
subsymbolic May 8, 2020
1ccd425
Merge branch 'develop' into feature/mia/toggle
subsymbolic May 8, 2020
8bdbf4a
Update test to also check pixel
subsymbolic May 11, 2020
356b1d8
Cleanup unneeded annotations and client definition
subsymbolic May 11, 2020
7e18623
Make db writes suspend functions to avoid future use without consider…
subsymbolic May 11, 2020
98e88f1
Update coroutines to use suspend functions for private methods and ju…
subsymbolic May 11, 2020
6b9d6d6
Extract repeated isWhitelisted check into own suspect function and ot…
subsymbolic May 11, 2020
fb9a63c
Merge branch 'develop' into feature/mia/toggle
subsymbolic May 11, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
700 changes: 700 additions & 0 deletions app/schemas/com.duckduckgo.app.global.db.AppDatabase/20.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.duckduckgo.app.bookmarks.db.BookmarksDao
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
Expand Down Expand Up @@ -62,6 +63,12 @@ class BookmarksViewModelTest {
whenever(bookmarksDao.bookmarks()).thenReturn(liveData)
}

@After
fun after() {
testee.viewState.removeObserver(viewStateObserver)
testee.command.removeObserver(commandObserver)
}

@Test
fun whenBookmarkDeletedThenDaoUpdated() {
testee.delete(bookmark)
Expand Down Expand Up @@ -94,5 +101,4 @@ class BookmarksViewModelTest {
assertNotNull(captor.value)
assertNotNull(captor.value.bookmarks)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2020 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.duckduckgo.app.brokensite

import com.duckduckgo.app.global.model.Site
import com.duckduckgo.app.global.model.SiteMonitor
import com.duckduckgo.app.surrogates.SurrogateResponse
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
import org.junit.Assert.*
import org.junit.Test

class BrokenSiteDataTest {

@Test
fun whenSiteIsNullThenDataIsEmptyAndUpgradedIsFalse() {
val data = BrokenSiteData.fromSite(null)
assertTrue(data.url.isEmpty())
assertTrue(data.blockedTrackers.isEmpty())
assertTrue(data.surrogates.isEmpty())
assertFalse(data.upgradedToHttps)
}

@Test
fun whenSiteExistsThenDataContainsUrl() {
val site = buildSite(SITE_URL)
val data = BrokenSiteData.fromSite(site)
assertEquals(SITE_URL, data.url)
}

@Test
fun whenSiteUpgradedThenHttpsUpgradedIsTrue() {
val site = buildSite(SITE_URL, httpsUpgraded = true)
val data = BrokenSiteData.fromSite(site)
assertTrue(data.upgradedToHttps)
}

@Test
fun whenSiteNotUpgradedThenHttpsUpgradedIsFalse() {
val site = buildSite(SITE_URL, httpsUpgraded = false)
val data = BrokenSiteData.fromSite(site)
assertFalse(data.upgradedToHttps)
}

@Test
fun whenSiteHasNoTrackersThenBlockedTrackersIsEmpty() {
val site = buildSite(SITE_URL)
val data = BrokenSiteData.fromSite(site)
assertTrue(data.blockedTrackers.isEmpty())
}

@Test
fun whenSiteHasBlockedTrackersThenBlockedTrackersExist() {
val site = buildSite(SITE_URL)
val event = TrackingEvent("http://www.example.com", "http://www.tracker.com/tracker.js", emptyList(), null, false)
val anotherEvent = TrackingEvent("http://www.example.com/test", "http://www.anothertracker.com/tracker.js", emptyList(), null, false)
site.trackerDetected(event)
site.trackerDetected(anotherEvent)
assertEquals("www.tracker.com,www.anothertracker.com", BrokenSiteData.fromSite(site).blockedTrackers)
}

@Test
fun whenSiteHasSameHostBlockedTrackersThenOnlyUniqueTrackersIncludedInData() {
val site = buildSite(SITE_URL)
val event = TrackingEvent("http://www.example.com", "http://www.tracker.com/tracker.js", emptyList(), null, false)
val anotherEvent = TrackingEvent("http://www.example.com/test", "http://www.tracker.com/tracker2.js", emptyList(), null, false)
site.trackerDetected(event)
site.trackerDetected(anotherEvent)
assertEquals("www.tracker.com", BrokenSiteData.fromSite(site).blockedTrackers)
}

@Test
fun whenSiteHasNoSurrogatesThenSurrogatesIsEmpty() {
val site = buildSite(SITE_URL)
val data = BrokenSiteData.fromSite(site)
assertTrue(data.surrogates.isEmpty())
}

@Test
fun whenSiteHasSurrogatesThenSurrogatesExist() {
val surrogate = SurrogateResponse(true, "surrogate.com/test.js", "", "")
val anotherSurrogate = SurrogateResponse(true, "anothersurrogate.com/test.js", "", "")
val site = buildSite(SITE_URL)
site.surrogateDetected(surrogate)
site.surrogateDetected(anotherSurrogate)
assertEquals("surrogate.com,anothersurrogate.com", BrokenSiteData.fromSite(site).surrogates)
}

@Test
fun whenSiteHasSameHostSurrogatesThenOnlyUniqueSurrogateIncludedInData() {
val surrogate = SurrogateResponse(true, "surrogate.com/test.js", "", "")
val anotherSurrogate = SurrogateResponse(true, "surrogate.com/test2.js", "", "")
val site = buildSite(SITE_URL)
site.surrogateDetected(surrogate)
site.surrogateDetected(anotherSurrogate)
assertEquals("surrogate.com", BrokenSiteData.fromSite(site).surrogates)
}

private fun buildSite(url: String, httpsUpgraded: Boolean = false): Site {
return SiteMonitor(url, "", upgradedHttps = httpsUpgraded)
}

companion object {
private const val SITE_URL = "foo.com"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ import com.duckduckgo.app.cta.model.CtaId
import com.duckduckgo.app.cta.model.DismissedCta
import com.duckduckgo.app.cta.ui.*
import com.duckduckgo.app.global.db.AppDatabase
import com.duckduckgo.app.cta.ui.HomeTopPanelCta
import com.duckduckgo.app.global.install.AppInstallStore
import com.duckduckgo.app.global.model.SiteFactory
import com.duckduckgo.app.onboarding.store.OnboardingStore
import com.duckduckgo.app.onboarding.store.UserStageStore
import com.duckduckgo.app.privacy.db.NetworkLeaderboardDao
import com.duckduckgo.app.privacy.db.UserWhitelistDao
import com.duckduckgo.app.privacy.model.PrivacyPractices
import com.duckduckgo.app.privacy.model.TestEntity
import com.duckduckgo.app.privacy.store.PrivacySettingsStore
import com.duckduckgo.app.privacy.model.UserWhitelistedDomain
import com.duckduckgo.app.runBlocking
import com.duckduckgo.app.settings.db.SettingsDataStore
import com.duckduckgo.app.statistics.VariantManager
Expand Down Expand Up @@ -172,10 +172,10 @@ class BrowserTabViewModelTest {
private lateinit var mockWidgetCapabilities: WidgetCapabilities

@Mock
private lateinit var mockPrivacySettingsStore: PrivacySettingsStore
private lateinit var mockUserStageStore: UserStageStore

@Mock
private lateinit var mockUserStageStore: UserStageStore
private lateinit var mockUserWhitelistDao: UserWhitelistDao

private lateinit var mockAutoCompleteApi: AutoCompleteApi

Expand Down Expand Up @@ -206,10 +206,10 @@ class BrowserTabViewModelTest {
mockSurveyDao,
mockWidgetCapabilities,
mockDismissedCtaDao,
mockUserWhitelistDao,
mockVariantManager,
mockSettingsStore,
mockOnboardingStore,
mockPrivacySettingsStore,
mockUserStageStore,
coroutineRule.testDispatcherProvider
)
Expand All @@ -222,14 +222,15 @@ class BrowserTabViewModelTest {
whenever(mockTabsRepository.retrieveSiteData(any())).thenReturn(MutableLiveData())
whenever(mockPrivacyPractices.privacyPracticesFor(any())).thenReturn(PrivacyPractices.UNKNOWN)
whenever(mockAppInstallStore.installTimestamp).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1))
whenever(mockPrivacySettingsStore.privacyOn).thenReturn(true)
whenever(mockUserWhitelistDao.contains(anyString())).thenReturn(false)

testee = BrowserTabViewModel(
statisticsUpdater = mockStatisticsUpdater,
queryUrlConverter = mockOmnibarConverter,
duckDuckGoUrlDetector = DuckDuckGoUrlDetector(),
siteFactory = siteFactory,
tabRepository = mockTabsRepository,
userWhitelistDao = mockUserWhitelistDao,
networkLeaderboardDao = mockNetworkLeaderboardDao,
autoComplete = mockAutoCompleteApi,
appSettingsPreferencesStore = mockSettingsStore,
Expand Down Expand Up @@ -1061,26 +1062,46 @@ class BrowserTabViewModelTest {
}

@Test
fun whenUserSelectsToShareLinkThenShareLinkCommandSent() {
loadUrl("foo.com")
testee.onShareSelected()
val command = captureCommands().value as Command.ShareLink
assertEquals("foo.com", command.url)
fun whenUserTogglesNonWhitelistedSiteThenSiteAddedToWhitelistAndPixelSentAndPageRefreshed() = coroutineRule.runBlocking {
whenever(mockUserWhitelistDao.contains("www.example.com")).thenReturn(false)
loadUrl("http://www.example.com/home.html")
testee.onWhitelistSelected()
verify(mockUserWhitelistDao).insert(UserWhitelistedDomain("www.example.com"))
verify(mockPixel).fire(Pixel.PixelName.BROWSER_MENU_WHITELIST_ADD)
verify(mockCommandObserver).onChanged(Command.Refresh)
}

@Test
fun whenUserTogglesWhitelsitedSiteThenSiteRemovedFromWhitelistAndPixelSentAndPageRefreshed() = coroutineRule.runBlocking {
whenever(mockUserWhitelistDao.contains("www.example.com")).thenReturn(true)
loadUrl("http://www.example.com/home.html")
testee.onWhitelistSelected()
verify(mockUserWhitelistDao).delete(UserWhitelistedDomain("www.example.com"))
verify(mockPixel).fire(Pixel.PixelName.BROWSER_MENU_WHITELIST_REMOVE)
verify(mockCommandObserver).onChanged(Command.Refresh)
}

@Test
fun whenOnSiteAndBrokenSiteSelectedThenBrokenSiteFeedbackCommandSentWithUrl() = coroutineRule.runBlocking {
loadUrl("foo.com", isBrowserShowing = true)
testee.onBrokenSiteSelected()
val command = captureCommands().value as Command.BrokenSiteFeedback
assertEquals("foo.com", command.url)
assertEquals("foo.com", command.data.url)
}

@Test
fun whenNoSiteAndBrokenSiteSelectedThenBrokenSiteFeedbackCommandSentWithoutUrl() {
testee.onBrokenSiteSelected()
val command = captureCommands().value as Command.BrokenSiteFeedback
assertEquals("", command.url)
assertEquals("", command.data.url)
}

@Test
fun whenUserSelectsToShareLinkThenShareLinkCommandSent() {
loadUrl("foo.com")
testee.onShareSelected()
val command = captureCommands().value as Command.ShareLink
assertEquals("foo.com", command.url)
}

@Test
Expand Down Expand Up @@ -1576,111 +1597,6 @@ class BrowserTabViewModelTest {
assertCommandIssued<Command.BrokenSiteFeedback>()
}

@Test
fun whenOnBrokenSiteSelectedAndNoHttpsUpgradedThenReturnHttpsUpgradedFalse() {
testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertFalse(brokenSiteFeedback.httpsUpgraded)
}

@Test
fun whenOnBrokenSiteSelectedAndNoTrackersThenReturnBlockedTrackersEmptyString() {
givenOneActiveTabSelected()

testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertEquals("", brokenSiteFeedback.blockedTrackers)
}

@Test
fun whenOnBrokenSiteSelectedAndTrackersBlockedThenReturnBlockedTrackers() {
givenOneActiveTabSelected()
val event = TrackingEvent("http://www.example.com", "http://www.tracker.com/tracker.js", emptyList(), null, false)
val anotherEvent = TrackingEvent("http://www.example.com/test", "http://www.anothertracker.com/tracker.js", emptyList(), null, false)

testee.trackerDetected(event)
testee.trackerDetected(anotherEvent)
testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertEquals("www.tracker.com,www.anothertracker.com", brokenSiteFeedback.blockedTrackers)
}

@Test
fun whenOnBrokenSiteSelectedAndSameHostTrackersBlockedThenDoNotReturnDuplicatedBlockedTrackers() {
givenOneActiveTabSelected()
val event = TrackingEvent("http://www.example.com", "http://www.tracker.com/tracker.js", emptyList(), null, false)
val anotherEvent = TrackingEvent("http://www.example.com/test", "http://www.tracker.com/tracker2.js", emptyList(), null, false)

testee.trackerDetected(event)
testee.trackerDetected(anotherEvent)
testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertEquals("www.tracker.com", brokenSiteFeedback.blockedTrackers)
}

@Test
fun whenOnBrokenSiteSelectedAndNoSurrogatesThenReturnSurrogatesEmptyString() {
givenOneActiveTabSelected()

testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertEquals("", brokenSiteFeedback.surrogates)
}

@Test
fun whenOnBrokenSiteSelectedAndSurrogatesThenReturnSurrogates() {
givenOneActiveTabSelected()
val surrogate = SurrogateResponse(true, "surrogate.com/test.js", "", "")
val anotherSurrogate = SurrogateResponse(true, "anothersurrogate.com/test.js", "", "")

testee.surrogateDetected(surrogate)
testee.surrogateDetected(anotherSurrogate)
testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertEquals("surrogate.com,anothersurrogate.com", brokenSiteFeedback.surrogates)
}

@Test
fun whenOnBrokenSiteSelectedAndSameHostSurrogatesThenDoNotReturnDuplicatedSurrogates() {
givenOneActiveTabSelected()
val surrogate = SurrogateResponse(true, "surrogate.com/test.js", "", "")
val anotherSurrogate = SurrogateResponse(true, "surrogate.com/test2.js", "", "")

testee.surrogateDetected(surrogate)
testee.surrogateDetected(anotherSurrogate)
testee.onBrokenSiteSelected()

val command = captureCommands().lastValue
assertTrue(command is Command.BrokenSiteFeedback)

val brokenSiteFeedback = command as Command.BrokenSiteFeedback
assertEquals("surrogate.com", brokenSiteFeedback.surrogates)
}

private inline fun <reified T : Command> assertCommandIssued(instanceAssertions: T.() -> Unit = {}) {
verify(mockCommandObserver, atLeastOnce()).onChanged(commandCaptor.capture())
val issuedCommand = commandCaptor.allValues.find { it is T }
Expand Down
Loading