Skip to content

Commit 06bf710

Browse files
mikescamellnalcalagmalmstein
authored
Add Sense of Protection Experiment (#5924)
Task/Issue URL: https://app.asana.com/1/137249556945/project/72649045549333/task/1209509235270500?focus=true ### Description See the [handover](https://app.asana.com/1/137249556945/project/0/task/1209946370931962) task for current state and check the commit messages. ### Steps to test this PR _Modified Control_ - [x] Install app and enable the modified control variant - [x] Open a site with trackers and load it - [x] Verify that the privacy shield appears but there isn’t a trackers animation - [x] Open the Tab Manager and verify that nothing has changed _Variant 1_ - [x] Install app and enable the variant 1 - [x] Open a site with trackers and load it - [x] Verify that the new privacy shield appears and “Trackers Blocked” text appears - [x] Open the Tab Manager and verify that nothing has changed _Variant 2_ - [x] Install app and enable the variant 2 - [x] Open a site with trackers and load it - [x] Verify that the new privacy shield appears and “Trackers Blocked” text appears - [x] Open the Tab Manager and verify that there’s a new panel at the top --- - To see the specific tasks where the Asana app for GitHub is being used, see below: - https://app.asana.com/0/0/1210104316606177 --------- Co-authored-by: Noelia Alcala <[email protected]> Co-authored-by: David González <[email protected]>
1 parent 0d99151 commit 06bf710

File tree

32 files changed

+1283
-427
lines changed

32 files changed

+1283
-427
lines changed

app/src/androidTest/java/com/duckduckgo/app/browser/BrowserTabViewModelTest.kt

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition.BOTTOM
125125
import com.duckduckgo.app.browser.omnibar.model.OmnibarPosition.TOP
126126
import com.duckduckgo.app.browser.refreshpixels.RefreshPixelSender
127127
import com.duckduckgo.app.browser.remotemessage.RemoteMessagingModel
128+
import com.duckduckgo.app.browser.senseofprotection.SenseOfProtectionExperiment
128129
import com.duckduckgo.app.browser.session.WebViewSessionStorage
129130
import com.duckduckgo.app.browser.trafficquality.AndroidFeaturesHeaderPlugin.Companion.X_DUCKDUCKGO_ANDROID_HEADER
130131
import com.duckduckgo.app.browser.viewstate.BrowserViewState
@@ -214,7 +215,6 @@ import com.duckduckgo.browser.api.UserBrowserProperties
214215
import com.duckduckgo.browser.api.brokensite.BrokenSiteContext
215216
import com.duckduckgo.common.test.CoroutineTestRule
216217
import com.duckduckgo.common.test.InstantSchedulersRule
217-
import com.duckduckgo.common.ui.experiments.visual.AppPersonalityFeature
218218
import com.duckduckgo.common.ui.experiments.visual.store.VisualDesignExperimentDataStore
219219
import com.duckduckgo.common.ui.experiments.visual.store.VisualDesignExperimentDataStore.FeatureState
220220
import com.duckduckgo.common.utils.DispatcherProvider
@@ -256,7 +256,6 @@ import com.duckduckgo.privacy.config.api.ContentBlocking
256256
import com.duckduckgo.privacy.config.api.TrackingParameters
257257
import com.duckduckgo.privacy.config.impl.features.gpc.RealGpc.Companion.GPC_HEADER
258258
import com.duckduckgo.privacy.config.impl.features.gpc.RealGpc.Companion.GPC_HEADER_VALUE
259-
import com.duckduckgo.privacy.dashboard.api.PrivacyDashboardExternalPixelParams
260259
import com.duckduckgo.privacy.dashboard.api.PrivacyProtectionTogglePlugin
261260
import com.duckduckgo.privacy.dashboard.api.PrivacyToggleOrigin
262261
import com.duckduckgo.privacy.dashboard.api.ui.ToggleReports
@@ -537,6 +536,7 @@ class BrowserTabViewModelTest {
537536
private val mockDuckChatJSHelper: DuckChatJSHelper = mock()
538537
private val swipingTabsFeature = FakeFeatureToggleFactory.create(SwipingTabsFeature::class.java)
539538
private val swipingTabsFeatureProvider = SwipingTabsFeatureProvider(swipingTabsFeature)
539+
private val mockSenseOfProtectionExperiment: SenseOfProtectionExperiment = mock()
540540

541541
private val defaultBrowserPromptsExperimentShowPopupMenuItemFlow = MutableStateFlow(false)
542542
private val mockDefaultBrowserPromptsExperiment: DefaultBrowserPromptsExperiment = mock()
@@ -551,9 +551,6 @@ class BrowserTabViewModelTest {
551551
private val mockSiteErrorHandler: StringSiteErrorHandler = mock()
552552
private val mockSiteHttpErrorHandler: HttpCodeSiteErrorHandler = mock()
553553

554-
private val fakeAppPersonalityFeature = FakeFeatureToggleFactory.create(AppPersonalityFeature::class.java)
555-
private val mockPrivacyDashboardExternalPixelParams: PrivacyDashboardExternalPixelParams = mock()
556-
557554
private val selectedTab = TabEntity("TAB_ID", "https://example.com", position = 0, sourceTabId = "TAB_ID_SOURCE")
558555

559556
@Before
@@ -629,6 +626,7 @@ class BrowserTabViewModelTest {
629626
duckPlayer = mockDuckPlayer,
630627
brokenSitePrompt = mockBrokenSitePrompt,
631628
userBrowserProperties = mockUserBrowserProperties,
629+
senseOfProtectionExperiment = mockSenseOfProtectionExperiment,
632630
)
633631

634632
val siteFactory = SiteFactoryImpl(
@@ -745,9 +743,7 @@ class BrowserTabViewModelTest {
745743
siteErrorHandlerKillSwitch = mockSiteErrorHandlerKillSwitch,
746744
siteErrorHandler = mockSiteErrorHandler,
747745
siteHttpErrorHandler = mockSiteHttpErrorHandler,
748-
appPersonalityFeature = fakeAppPersonalityFeature,
749-
userStageStore = mockUserStageStore,
750-
privacyDashboardExternalPixelParams = mockPrivacyDashboardExternalPixelParams,
746+
senseOfProtectionExperiment = mockSenseOfProtectionExperiment,
751747
)
752748

753749
testee.loadData("abc", null, false, false)
@@ -6223,26 +6219,7 @@ class BrowserTabViewModelTest {
62236219

62246220
@Test
62256221
fun whenOnAnimationFinishedAndSelfAndVariant2EnabledThenStartTrackersExperimentShieldPopAnimation() = runTest {
6226-
// Variant 2 is enabled
6227-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
6228-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = true))
6229-
// All other variants are disabled
6230-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = false))
6231-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = false))
6232-
6233-
testee.onAnimationFinished()
6234-
6235-
assertCommandIssued<Command.StartTrackersExperimentShieldPopAnimation>()
6236-
}
6237-
6238-
@Test
6239-
fun whenOnAnimationFinishedAndSelfAndVariant3EnabledThenStartTrackersExperimentShieldPopAnimation() = runTest {
6240-
// Variant 3 is enabled
6241-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
6242-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = true))
6243-
// All other variants are disabled
6244-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = false))
6245-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = false))
6222+
whenever(mockSenseOfProtectionExperiment.isUserEnrolledInAVariantAndExperimentEnabled()).thenReturn(true)
62466223

62476224
testee.onAnimationFinished()
62486225

app/src/androidTest/java/com/duckduckgo/app/browser/omnibar/animations/LottiePrivacyShieldAnimationHelperTest.kt

Lines changed: 29 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,26 @@ package com.duckduckgo.app.browser.omnibar.animations
1919
import android.annotation.SuppressLint
2020
import com.airbnb.lottie.LottieAnimationView
2121
import com.duckduckgo.app.browser.R
22+
import com.duckduckgo.app.browser.senseofprotection.SenseOfProtectionExperiment
2223
import com.duckduckgo.app.global.model.PrivacyShield.MALICIOUS
2324
import com.duckduckgo.app.global.model.PrivacyShield.PROTECTED
2425
import com.duckduckgo.app.global.model.PrivacyShield.UNPROTECTED
25-
import com.duckduckgo.common.ui.experiments.visual.AppPersonalityFeature
2626
import com.duckduckgo.common.ui.store.AppTheme
27-
import com.duckduckgo.feature.toggles.api.FakeFeatureToggleFactory
28-
import com.duckduckgo.feature.toggles.api.Toggle.State
2927
import org.junit.Test
3028
import org.mockito.kotlin.mock
3129
import org.mockito.kotlin.verify
3230
import org.mockito.kotlin.whenever
3331

3432
class LottiePrivacyShieldAnimationHelperTest {
3533

36-
private val fakeAppPersonalityFeature = FakeFeatureToggleFactory.create(AppPersonalityFeature::class.java)
34+
private val senseOfProtectionExperiment: SenseOfProtectionExperiment = mock()
3735

3836
@Test
3937
fun whenLightModeAndPrivacyShieldProtectedThenSetLightShieldAnimation() {
4038
val holder: LottieAnimationView = mock()
4139
val appTheme: AppTheme = mock()
4240
whenever(appTheme.isLightModeEnabled()).thenReturn(true)
43-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
41+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
4442

4543
testee.setAnimationView(holder, PROTECTED)
4644

@@ -52,7 +50,7 @@ class LottiePrivacyShieldAnimationHelperTest {
5250
val holder: LottieAnimationView = mock()
5351
val appTheme: AppTheme = mock()
5452
whenever(appTheme.isLightModeEnabled()).thenReturn(false)
55-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
53+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
5654

5755
testee.setAnimationView(holder, PROTECTED)
5856

@@ -64,7 +62,7 @@ class LottiePrivacyShieldAnimationHelperTest {
6462
val holder: LottieAnimationView = mock()
6563
val appTheme: AppTheme = mock()
6664
whenever(appTheme.isLightModeEnabled()).thenReturn(true)
67-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
65+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
6866

6967
testee.setAnimationView(holder, UNPROTECTED)
7068

@@ -77,7 +75,7 @@ class LottiePrivacyShieldAnimationHelperTest {
7775
val holder: LottieAnimationView = mock()
7876
val appTheme: AppTheme = mock()
7977
whenever(appTheme.isLightModeEnabled()).thenReturn(false)
80-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
78+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
8179

8280
testee.setAnimationView(holder, UNPROTECTED)
8381

@@ -90,7 +88,7 @@ class LottiePrivacyShieldAnimationHelperTest {
9088
val holder: LottieAnimationView = mock()
9189
val appTheme: AppTheme = mock()
9290
whenever(appTheme.isLightModeEnabled()).thenReturn(true)
93-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
91+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
9492

9593
testee.setAnimationView(holder, MALICIOUS)
9694

@@ -103,7 +101,7 @@ class LottiePrivacyShieldAnimationHelperTest {
103101
val holder: LottieAnimationView = mock()
104102
val appTheme: AppTheme = mock()
105103
whenever(appTheme.isLightModeEnabled()).thenReturn(false)
106-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
104+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
107105

108106
testee.setAnimationView(holder, MALICIOUS)
109107

@@ -113,18 +111,14 @@ class LottiePrivacyShieldAnimationHelperTest {
113111

114112
@SuppressLint("DenyListedApi")
115113
@Test
116-
fun whenLightModeAndProtectedAndSelfEnabledAndVariant1DisabledThenUseExperimentAssets() {
114+
fun whenLightModeAndProtectedAndSelfEnabledAndShouldShowNewShieldThenUseExperimentAssets() {
115+
whenever(senseOfProtectionExperiment.shouldShowNewPrivacyShield()).thenReturn(true)
116+
117117
val holder: LottieAnimationView = mock()
118118
val appTheme: AppTheme = mock()
119119
whenever(appTheme.isLightModeEnabled()).thenReturn(true)
120-
// Variant 2 is enabled
121-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
122-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = true))
123-
// All other variants are disabled, including Variant 1
124-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = false))
125-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = false))
126120

127-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
121+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
128122

129123
testee.setAnimationView(holder, PROTECTED)
130124

@@ -133,18 +127,14 @@ class LottiePrivacyShieldAnimationHelperTest {
133127

134128
@SuppressLint("DenyListedApi")
135129
@Test
136-
fun whenLightModeAndUnprotectedAndSelfEnabledAndVariant1DisabledThenUseExperimentAssets() {
130+
fun whenLightModeAndUnprotectedAndSelfEnabledAndShouldShowNewShieldThenUseExperimentAssets() {
131+
whenever(senseOfProtectionExperiment.shouldShowNewPrivacyShield()).thenReturn(true)
132+
137133
val holder: LottieAnimationView = mock()
138134
val appTheme: AppTheme = mock()
139135
whenever(appTheme.isLightModeEnabled()).thenReturn(true)
140-
// Variant 2 is enabled
141-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
142-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = true))
143-
// All other variants are disabled, including Variant 1
144-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = false))
145-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = false))
146136

147-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
137+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
148138

149139
testee.setAnimationView(holder, UNPROTECTED)
150140

@@ -153,18 +143,14 @@ class LottiePrivacyShieldAnimationHelperTest {
153143

154144
@SuppressLint("DenyListedApi")
155145
@Test
156-
fun whenDarkModeAndProtectedAndSelfEnabledAndVariant1DisabledThenUseExperimentAssets() {
146+
fun whenDarkModeAndProtectedAndSelfEnabledAndShouldShowNewShieldThenUseExperimentAssets() {
147+
whenever(senseOfProtectionExperiment.shouldShowNewPrivacyShield()).thenReturn(true)
148+
157149
val holder: LottieAnimationView = mock()
158150
val appTheme: AppTheme = mock()
159151
whenever(appTheme.isLightModeEnabled()).thenReturn(false)
160-
// Variant 2 is enabled
161-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
162-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = true))
163-
// All other variants are disabled, including Variant 1
164-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = false))
165-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = false))
166152

167-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
153+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
168154

169155
testee.setAnimationView(holder, PROTECTED)
170156

@@ -173,18 +159,14 @@ class LottiePrivacyShieldAnimationHelperTest {
173159

174160
@SuppressLint("DenyListedApi")
175161
@Test
176-
fun whenDarkModeAndUnprotectedAndSelfEnabledAndVariant1DisabledThenUseExperimentAssets() {
162+
fun whenDarkModeAndUnprotectedAndSelfEnabledAndShouldShowNewShieldThenUseExperimentAssets() {
163+
whenever(senseOfProtectionExperiment.shouldShowNewPrivacyShield()).thenReturn(true)
164+
177165
val holder: LottieAnimationView = mock()
178166
val appTheme: AppTheme = mock()
179167
whenever(appTheme.isLightModeEnabled()).thenReturn(false)
180-
// Variant 2 is enabled
181-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
182-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = true))
183-
// All other variants are disabled, including Variant 1
184-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = false))
185-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = false))
186168

187-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
169+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
188170

189171
testee.setAnimationView(holder, UNPROTECTED)
190172

@@ -193,18 +175,14 @@ class LottiePrivacyShieldAnimationHelperTest {
193175

194176
@SuppressLint("DenyListedApi")
195177
@Test
196-
fun whenLightModeAndProtectedAndSelfEnabledAndVariant1EnabledThenUseNonExperimentAssets() {
178+
fun whenLightModeAndProtectedAndSelfEnabledAndShouldShowNotNewShieldThenUseNonExperimentAssets() {
179+
whenever(senseOfProtectionExperiment.isUserEnrolledInAVariantAndExperimentEnabled()).thenReturn(false)
180+
197181
val holder: LottieAnimationView = mock()
198182
val appTheme: AppTheme = mock()
199183
whenever(appTheme.isLightModeEnabled()).thenReturn(true)
200-
// Variant 1 is enabled
201-
fakeAppPersonalityFeature.self().setRawStoredState(State(enable = true))
202-
fakeAppPersonalityFeature.variant1().setRawStoredState(State(enable = true))
203-
// All other variants are disabled
204-
fakeAppPersonalityFeature.variant2().setRawStoredState(State(enable = false))
205-
fakeAppPersonalityFeature.variant3().setRawStoredState(State(enable = false))
206-
207-
val testee = LottiePrivacyShieldAnimationHelper(appTheme, fakeAppPersonalityFeature)
184+
185+
val testee = LottiePrivacyShieldAnimationHelper(appTheme, senseOfProtectionExperiment)
208186

209187
testee.setAnimationView(holder, PROTECTED)
210188

app/src/androidTest/java/com/duckduckgo/app/cta/ui/CtaViewModelTest.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.room.Room
2424
import androidx.test.platform.app.InstrumentationRegistry
2525
import com.duckduckgo.app.browser.DuckDuckGoUrlDetectorImpl
2626
import com.duckduckgo.app.browser.R
27+
import com.duckduckgo.app.browser.senseofprotection.SenseOfProtectionExperiment
2728
import com.duckduckgo.app.cta.db.DismissedCtaDao
2829
import com.duckduckgo.app.cta.model.CtaId
2930
import com.duckduckgo.app.cta.model.DismissedCta
@@ -71,6 +72,7 @@ import org.junit.Assert.*
7172
import org.junit.Before
7273
import org.junit.Rule
7374
import org.junit.Test
75+
import org.mockito.Mockito
7476
import org.mockito.kotlin.*
7577

7678
@FlowPreview
@@ -120,6 +122,8 @@ class CtaViewModelTest {
120122

121123
private val mockUserBrowserProperties: UserBrowserProperties = mock()
122124

125+
private val mockSenseOfProtectionExperiment: SenseOfProtectionExperiment = Mockito.mock()
126+
123127
private val requiredDaxOnboardingCtas: List<CtaId> = listOf(
124128
CtaId.DAX_INTRO,
125129
CtaId.DAX_DIALOG_SERP,
@@ -174,6 +178,7 @@ class CtaViewModelTest {
174178
duckPlayer = mockDuckPlayer,
175179
brokenSitePrompt = mockBrokenSitePrompt,
176180
userBrowserProperties = mockUserBrowserProperties,
181+
senseOfProtectionExperiment = mockSenseOfProtectionExperiment,
177182
)
178183
}
179184

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package com.duckduckgo.common.ui.internal.experiments.trackersblocking
17+
package experiments.trackersblocking
1818

1919
import android.content.Context
2020
import android.view.View

0 commit comments

Comments
 (0)