Skip to content

Commit 2ea0400

Browse files
Add autofill (#1197)
1 parent a72d06f commit 2ea0400

40 files changed

+3899
-23
lines changed

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,9 @@ dependencies {
192192
// Lottie
193193
implementation "com.airbnb.android:lottie:_"
194194

195+
// Security crypto
196+
implementation AndroidX.security.crypto
197+
195198
// Play Store referrer library
196199
playImplementation("com.android.installreferrer:installreferrer:_")
197200

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

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import com.duckduckgo.app.cta.db.DismissedCtaDao
6060
import com.duckduckgo.app.cta.model.CtaId
6161
import com.duckduckgo.app.cta.model.DismissedCta
6262
import com.duckduckgo.app.cta.ui.*
63+
import com.duckduckgo.app.email.EmailManager
6364
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteDao
6465
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteEntity
6566
import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteRepository
@@ -113,9 +114,12 @@ import dagger.Lazy
113114
import io.reactivex.Observable
114115
import io.reactivex.Single
115116
import kotlinx.coroutines.ExperimentalCoroutinesApi
117+
import kotlinx.coroutines.FlowPreview
116118
import kotlinx.coroutines.channels.Channel
117119
import kotlinx.coroutines.flow.MutableSharedFlow
120+
import kotlinx.coroutines.flow.MutableStateFlow
118121
import kotlinx.coroutines.flow.asSharedFlow
122+
import kotlinx.coroutines.flow.asStateFlow
119123
import kotlinx.coroutines.flow.consumeAsFlow
120124
import kotlinx.coroutines.flow.flowOf
121125
import kotlinx.coroutines.runBlocking
@@ -134,6 +138,7 @@ import java.io.File
134138
import java.util.*
135139
import java.util.concurrent.TimeUnit
136140

141+
@FlowPreview
137142
@ExperimentalCoroutinesApi
138143
class BrowserTabViewModelTest {
139144

@@ -237,6 +242,9 @@ class BrowserTabViewModelTest {
237242
@Mock
238243
private lateinit var fireproofDialogsEventHandler: FireproofDialogsEventHandler
239244

245+
@Mock
246+
private lateinit var mockEmailManager: EmailManager
247+
240248
private val lazyFaviconManager = Lazy { mockFaviconManager }
241249

242250
private lateinit var mockAutoCompleteApi: AutoCompleteApi
@@ -266,6 +274,8 @@ class BrowserTabViewModelTest {
266274

267275
private val childClosedTabsFlow = childClosedTabsSharedFlow.asSharedFlow()
268276

277+
private val emailStateFlow = MutableStateFlow(false)
278+
269279
@Before
270280
fun before() {
271281
MockitoAnnotations.openMocks(this)
@@ -280,6 +290,7 @@ class BrowserTabViewModelTest {
280290

281291
whenever(mockDismissedCtaDao.dismissedCtas()).thenReturn(dismissedCtaDaoChannel.consumeAsFlow())
282292
whenever(mockTabRepository.flowTabs).thenReturn(flowOf(emptyList()))
293+
whenever(mockEmailManager.signedInFlow()).thenReturn(emailStateFlow.asStateFlow())
283294

284295
ctaViewModel = CtaViewModel(
285296
mockAppInstallStore,
@@ -345,7 +356,8 @@ class BrowserTabViewModelTest {
345356
variantManager = mockVariantManager,
346357
fileDownloader = mockFileDownloader,
347358
globalPrivacyControl = GlobalPrivacyControlManager(mockSettingsStore),
348-
fireproofDialogsEventHandler = fireproofDialogsEventHandler
359+
fireproofDialogsEventHandler = fireproofDialogsEventHandler,
360+
emailManager = mockEmailManager
349361
)
350362

351363
testee.loadData("abc", null, false)
@@ -3150,6 +3162,98 @@ class BrowserTabViewModelTest {
31503162
assertCommandNotIssued<Command.ChildTabClosed>()
31513163
}
31523164

3165+
@Test
3166+
fun whenConsumeAliasAndCopyToClipboardThenCopyAliasToClipboardCommandSent() {
3167+
whenever(mockEmailManager.getAlias()).thenReturn("alias")
3168+
3169+
testee.consumeAliasAndCopyToClipboard()
3170+
3171+
assertCommandIssued<Command.CopyAliasToClipboard>()
3172+
}
3173+
3174+
@Test
3175+
fun whenEmailIsSignedOutThenIsEmailSignedInReturnsFalse() = coroutineRule.runBlocking {
3176+
emailStateFlow.emit(false)
3177+
3178+
assertFalse(browserViewState().isEmailSignedIn)
3179+
}
3180+
3181+
@Test
3182+
fun whenEmailIsSignedInThenIsEmailSignedInReturnsTrue() = coroutineRule.runBlocking {
3183+
emailStateFlow.emit(true)
3184+
3185+
assertTrue(browserViewState().isEmailSignedIn)
3186+
}
3187+
3188+
@Test
3189+
fun whenConsumeAliasThenInjectAddressCommandSent() {
3190+
whenever(mockEmailManager.getAlias()).thenReturn("alias")
3191+
3192+
testee.consumeAlias()
3193+
3194+
assertCommandIssued<Command.InjectEmailAddress> {
3195+
assertEquals("alias", this.address)
3196+
}
3197+
}
3198+
3199+
@Test
3200+
fun whenConsumeAliasThenPixelSent() {
3201+
whenever(mockEmailManager.getAlias()).thenReturn("alias")
3202+
3203+
testee.consumeAlias()
3204+
3205+
verify(mockPixel).enqueueFire(AppPixelName.EMAIL_USE_ALIAS)
3206+
}
3207+
3208+
@Test
3209+
fun whenCancelAutofillTooltipThenPixelSent() {
3210+
whenever(mockEmailManager.getAlias()).thenReturn("alias")
3211+
3212+
testee.cancelAutofillTooltip()
3213+
3214+
verify(mockPixel).enqueueFire(AppPixelName.EMAIL_TOOLTIP_DISMISSED)
3215+
}
3216+
3217+
@Test
3218+
fun whenUseAddressThenInjectAddressCommandSent() {
3219+
whenever(mockEmailManager.getEmailAddress()).thenReturn("address")
3220+
3221+
testee.useAddress()
3222+
3223+
assertCommandIssued<Command.InjectEmailAddress> {
3224+
assertEquals("address", this.address)
3225+
}
3226+
}
3227+
3228+
@Test
3229+
fun whenUseAddressThenPixelSent() {
3230+
whenever(mockEmailManager.getEmailAddress()).thenReturn("address")
3231+
3232+
testee.useAddress()
3233+
3234+
verify(mockPixel).enqueueFire(AppPixelName.EMAIL_USE_ADDRESS)
3235+
}
3236+
3237+
@Test
3238+
fun whenShowEmailTooltipIfAddressExistsThenShowEmailTooltipCommandSent() {
3239+
whenever(mockEmailManager.getEmailAddress()).thenReturn("address")
3240+
3241+
testee.showEmailTooltip()
3242+
3243+
assertCommandIssued<Command.ShowEmailTooltip> {
3244+
assertEquals("address", this.address)
3245+
}
3246+
}
3247+
3248+
@Test
3249+
fun whenShowEmailTooltipIfAddressDoesNotExistThenCommandNotSent() {
3250+
whenever(mockEmailManager.getEmailAddress()).thenReturn(null)
3251+
3252+
testee.showEmailTooltip()
3253+
3254+
assertCommandNotIssued<Command.ShowEmailTooltip>()
3255+
}
3256+
31533257
private suspend fun givenFireButtonPulsing() {
31543258
whenever(mockUserStageStore.getUserAppStage()).thenReturn(AppStage.DAX_ONBOARDING)
31553259
dismissedCtaDaoChannel.send(listOf(DismissedCta(CtaId.DAX_DIALOG_TRACKERS_FOUND)))

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import com.duckduckgo.app.browser.httpauth.WebViewHttpAuthStore
3030
import com.duckduckgo.app.browser.logindetection.DOMLoginDetector
3131
import com.duckduckgo.app.browser.logindetection.WebNavigationEvent
3232
import com.duckduckgo.app.browser.model.BasicAuthenticationRequest
33+
import com.duckduckgo.app.email.EmailInjector
3334
import com.duckduckgo.app.global.exception.UncaughtExceptionRepository
3435
import com.duckduckgo.app.globalprivacycontrol.GlobalPrivacyControl
3536
import com.duckduckgo.app.runBlocking
@@ -64,6 +65,7 @@ class BrowserWebViewClientTest {
6465
private val trustedCertificateStore: TrustedCertificateStore = mock()
6566
private val webViewHttpAuthStore: WebViewHttpAuthStore = mock()
6667
private val thirdPartyCookieManager: ThirdPartyCookieManager = mock()
68+
private val emailInjector: EmailInjector = mock()
6769

6870
@UiThreadTest
6971
@Before
@@ -83,7 +85,8 @@ class BrowserWebViewClientTest {
8385
globalPrivacyControl,
8486
thirdPartyCookieManager,
8587
GlobalScope,
86-
coroutinesTestRule.testDispatcherProvider
88+
coroutinesTestRule.testDispatcherProvider,
89+
emailInjector
8790
)
8891
testee.webViewClientListener = listener
8992
}
@@ -197,6 +200,20 @@ class BrowserWebViewClientTest {
197200
verify(listener, never()).prefetchFavicon(any())
198201
}
199202

203+
@UiThreadTest
204+
@Test
205+
fun whenOnPageFinishedCalledThenInjectEmailAutofillJsCalled() {
206+
testee.onPageFinished(webView, null)
207+
verify(emailInjector).injectEmailAutofillJs(webView, null)
208+
}
209+
210+
@UiThreadTest
211+
@Test
212+
fun whenOnPageStartedCalledThenResetInjectedJsFlagCalled() {
213+
testee.onPageStarted(webView, null, null)
214+
verify(emailInjector).resetInjectedJsFlag()
215+
}
216+
200217
private class TestWebView(context: Context) : WebView(context)
201218

202219
companion object {

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,23 @@ class DuckDuckGoUrlDetectorTest {
118118
assertFalse(testee.isDuckDuckGoStaticUrl("https://example.com/settings"))
119119
}
120120

121+
@Test
122+
fun whenDomainIsNotDuckDuckGoThenReturnFalse() {
123+
assertFalse(testee.isDuckDuckGoDomain("https://example.com"))
124+
}
125+
126+
@Test
127+
fun whenDomainIsDuckDuckGoThenReturnTrue() {
128+
assertTrue(testee.isDuckDuckGoDomain("https://duckduckgo.com"))
129+
}
130+
131+
@Test
132+
fun whenUrlContainsSubdomainAndIsFromDuckDuckGoDomainThenReturnTrue() {
133+
assertTrue(testee.isDuckDuckGoDomain("https://test.duckduckgo.com"))
134+
}
135+
136+
@Test
137+
fun whenUrlHasNoSchemeAndIsFromDuckDuckGoDomainThenReturnsTrue() {
138+
assertTrue(testee.isDuckDuckGoDomain("duckduckgo.com"))
139+
}
121140
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ class CtaViewModelTest {
143143

144144
@Before
145145
fun before() {
146-
MockitoAnnotations.initMocks(this)
146+
MockitoAnnotations.openMocks(this)
147147
db = Room.inMemoryDatabaseBuilder(context, AppDatabase::class.java)
148148
.allowMainThreadQueries()
149149
.build()

0 commit comments

Comments
 (0)