Skip to content

Commit

Permalink
Android autofill MVP implementation (#2033)
Browse files Browse the repository at this point in the history
* Create autofill base branch

* Initial implementation of Secure Storage with L1 encryption (#1924)

Task/Issue URL: https://app.asana.com/0/0/1202210732792278/f

Description

This PR includes:

API following API design
Database structure for secure storage
Initial implementation of SecureStorage API with L1 using SQLCipher
This PR doesn't include:

complete tests. I will add that on the next tasks.
Steps to test this PR

Not applicable

UI changes

None

* Create autofill base branch

* Create autofill base branch

* Create autofill base branch

* Address comments in #1924 (#1926)

Task/Issue URL: N/A

Description

Addressing all comments mentioned in #1924

Steps to test this PR

N/A

UI changes

N/A

* Partial implementation of credential autofill (incomplete, non-pretty) (#1969)

Task/Issue URL: https://app.asana.com/0/0/1202361561968346/f

Partial, unpolished implementation of autofill. Most functionality is available.

**Included**
- saving credentials
- autofilling credentials into sites
- credentials management screen (accessible from settings)
  - view saved credentials
  - delete
  - edit credentials
  - copy username
  - copy password

**Still to be done**
- A nice UI 🙈
- Cody tidy-up
- Device authentication

* Implement device auth module (#1994)

Task/Issue URL: https://app.asana.com/0/0/1202418883857446/f

Description

This PR introduces the DeviceAuthentication API which can be used to launch the device authentication flow using (Fingerprint, PIN, pattern or password)
Steps to test this PR

This currently is not used in any feature at the moment

* Integrate device authentication to autofill (#1995)

Task/Issue URL: https://app.asana.com/0/0/1202418883857448/f
PR depends on: #1994

Description

This PR integrates the device auth flow into the autofill flow.

This doesn't include the full flow in management page since the page will likely change in implementation since it is still in the MVP stage. Will address this in https://app.asana.com/0/0/1202418883857449/f

Steps to test this PR

User has device auth enrolled on device

  Open a website with log in.
  Saves credentials without needing to do any authentication.
  Log out and attempt to login using saved credentials.
  Authentication using whatever is enrolled to the device (Fingerprint, faceid, pin, password or pattern) should be required everytime when trying to autofill a login.
  Open the management screen in settings.
  Authentication should be required every time.
User has no device auth enrolled on device

  Open a website with log in.
  Save credential should not be shown.
  If a credential was previously saved, no prompt to autofill should be shown.
  Management link in settings should be disabled.

* Implement L2 encryption on secure storage (#1980)

Task/Issue URL: https://app.asana.com/0/0/1202210732792279/f

Description

This PR adds the L2 encryption for passwords in the secure storage.

What's not yet included:

expiring sessions / passwords
user authentication on secure storage

Steps to test this PR

Use autofill [Make sure to clear storage before you install this changes)

  Open a website with login
  Login and save password in autofill
  Open the autofill manage screen
  Check if password shown is correct

* Fix failures on tests for PRs against autofill_base (#2002)

Task/Issue URL: https://app.asana.com/0/0/1202463484351795/f

Description

This PR includes:

Pointing to a different commit for autofill instead of 4.6.0 to make autofill_base changes to work
Add baseline files for new modules
Delete submodules/autofill - since this is causing issues.

* Remove auth related code on secure storage (#1998)

Task/Issue URL: https://app.asana.com/0/0/1202434443764031/f

Description

Removal of user authentication from the secure storage API v1

Steps to test this PR

Not applicable

* Add tests to secure-storage (#2000)

Task/Issue URL: https://app.asana.com/0/0/1202253978523860/f
Stack on: #1980

Description

This PR includes a few refactoring and addition for tests for secure storage classes.

Steps to test this PR

 Sanity test autofill

* Align autofill device authentication to design and copy (#2013)

* Update text for auth flow

* Add strings for locked and disable modes

* Remove logic for disabling settings link for autofill when no auth is available

* Add locked mode UI

* Add disabled mode UI

* Integrate locked and disabled states in management screen

* Avoid blank screen where auth result is not successful upon first launch

Co-authored-by: Craig Russell <CDRussell@users.noreply.github.com>

* Inline autofill with new JS communication mechanism (#1973)

Task/Issue URL: https://app.asana.com/0/0/1202370114120269/f

### Description
Switching away from using `window.postMessage` to injecting in configuration and available input types directly.

### Not included (to be done in future PRs)

- Handling the "update saved login" flow"
- Pretty UI
- Improvements to credential management screen
- More tests

### Steps to test this PR
FOR FIRST TESTS, ENSURE DEVICE AUTH IS **ENABLED**

#### Test no Autofill where you have not saved credentials
- [x]  Visit site with a login (e.g., https://trello.com/login)
- [x]  Click on email field; verify no autofill prompts 

#### Test Autofill offers to save when logging in
- [x]  Visit site with a login (e.g., https://trello.com/login) (tip: don't have to use valid login details on trello to save dialog)
- [x]  Enter login details and press "log in" button 
- [x] Verify dialog shown asking if you want to save login details 
- [x] Choose to save, then logout/refresh the page
- [x] Click on email field; verify that you do see the autofill prompt

#### Test Autofill offers to save when logging in
- [x]  Visit trello login page https://trello.com/login
- [x]  Enter login details and press "log in" button 
- [x] Verify dialog shown asking if you want to save login details 
- [x] Choose not to save, refresh and verify credentials not offered to autofill
- [x] Repeat the above steps, but this time choose to save, then logout/refresh the page
- [x] Click on email field; verify that you do see the autofill prompt

#### Test login credentials autofilled when chosen
- [x] Carrying on from last test where you'll have saved login credentials for trello...
- [x] Enter login details and press "log in" button 
- [x] Verify you see dialog with saved login shown
- [x] Choose to use the saved login
- [x] Verify you see the auth prompt. 
- [x] Let it fail
- [x] Verify autofill doesn't happen
- [x] Repeat the above (you'll have to refresh the page), this time let auth succeed
- [x] Verify autofill does happen

FOR THESE TESTS, ENSURE DEVICE AUTH IS **DISABLED**

#### Test login credentials autofilled when chosen

- [x] Repeat tests above, verifying that autofill prompts are never shown

Co-authored-by: Karl Dimla <klmbdimla@gmail.com>

* Disable save prompt when autofill toggle disabled (#2018)

* Restrict autofill to internal test users (#2020)

Task/Issue URL: https://app.asana.com/0/0/1202494823153095/f

### Description
This PR includes
- Introducing a mechanism to allow users to be internal test users
- Enabled autofill only for internal test users

### Steps to test this PR

_User is NOT an internal tester_
- [x] Open a website login
- [x] Enter credentials
- [x] Check that Autofill save dialog doesn't show
- [x] Open Settings
- [x] Check that Autofill management doesn't show

_User is opens internal tester success url_
- [x] Open a https://use-login.duckduckgo.com/patestsucceeded
- [x] Error page should load
- [x] Check that Autofill management doesn't show in Settings
- [x] Check that Autofill save dialog doesn't show when attempting to login in any website

_User is signs up to be internal test user_
- [x] Open a https://use-login.duckduckgo.com
- [x] Complete setup
- [x] Check that Autofill management shows in Settings
- [x] Check that Autofill is enabled by default
- [x] Check that Autofill save dialog shows when attempting to login in any website
- [x] Check that Autofill dialog shows when attempting to login in any website with saved credentials

_User is signs up to be internal test user but autofill is disbled_
- [x] Open a https://use-login.duckduckgo.com
- [x] Complete setup
- [x] Check that Autofill management shows in Settings
- [x] Disabled autofill
- [x] Check that Autofill save dialog doesn't show when attempting to login in any website
- [x] Check that Autofil dialog doesn't show when attempting to loginin any website with saved credentials

* Add flag to easily enable debug mode for autofill (#2025)

Task/Issue URL: https://app.asana.com/0/414730916066338/1202532114531456/f

### Description
Adds a boolean to enable autofill debug mode. This is will stay `false` for default so we always test against the production impl and only toggled locally to `true` for debugging autofill integration issues.

### Steps to test this PR
Set the flag to true and add a logcat filter for `console`; verify you see lots more console output
Set it back to false and verify everything working as expected

* Restore guard that was removed as a temporary fix (#2021)

Task/Issue URL: https://app.asana.com/0/0/1202525190970047/f

### Description
Remove a temporary fix now that the real fix is coming through a JS update.

* Module tidy up for autofill (#2030)

Task/Issue URL: https://app.asana.com/0/0/1202539310746053/f

### Description
Minor tidy up around where to place files in modules

### Steps to test this PR
That everything still compiles and checks pass is enough

* Fix known issues for Autofill (#2027)

Task/Issue URL:
https://app.asana.com/0/0/1202518798495133/f
https://app.asana.com/0/0/1202518798495138/f

Description

This PR includes:

Add handling for Xiaomi devices when launching settings page
Synchronizing functions accessing the Cipher and separating cipher for encryption and decryption

Steps to test this PR

Smoke test autofill

* Move EmailManager to browser-api module (#2032)

Task/Issue URL: https://app.asana.com/0/0/1202540505198731/f

### Description
Move `EmailManager` to `browser-api` module (as per previous PR feedback)

### Steps to test this PR

- [ ] Check that email autofill still works as expected

* Make internal builds automatically internal test users (#2034)

Task/Issue URL: https://app.asana.com/0/0/1202494823153095/f

Description

This PR includes automatically making the user an internal test user when using an internal build.

Steps to test this PR

Internal build

  Use internal build
  Verify that autofill is still available
Internal build, failed verifications

  Use internal build
  Open use-login.duckduckgo.com/patestsucceeded (without going through verifcation) this should show the error page.
  Verify that autofill is still available
Other builds

  Use any build that is not internal
  Autofill should NOT be available.
  Go through verification in use-login.duckduckgo.com
  Verify that autofill is still available

* Autofill: loose end tidy up (#2037)

* Tidy strings resources

* Remove onConsoleMessage webview callback

* Tidy lint baseline

* Remove unused proguard files

* Tidy library dependencies

* Tidy autofill layouts and internationlize strings

* Tidy module test declarations

* Standardise capitalisation on string resources

* Tidy build.gradle files

* Tidy build.gradle

* Address design review changes for Autofill auth (#2036)

Task/Issue URL: https://app.asana.com/0/0/1202517616610078/f

Description

This PR includes the following:

Fix dark mode for Autofill management disabled mode
Add toolbar on autofill management (basic toolbar)
Fix usage of Autofill management locked mode
Improve disabled mode text linebreak
Changes to the Autofill management locked mode
Addition of FLAG_SECURE in AutofillManagementActivity
Updates to the copy for Autofill management disabled mode

* Remove modules directory

* tidy: inline variable

* tidy: build.gradle file, remove unnecessary config

* tidy: remove exported property from autofill activity

* Update app/build.gradle

Co-authored-by: Karl Dimla <klmbdimla@gmail.com>

* tidy: autofill build.gradle files, removed unneccessary config

* tidy: autofill build.gradle files, removed unneccessary config

* tidy: autofill build.gradle files, removed unneccessary config

* tidy: delete unused layout

* tidy: di usage around javascript interface

* tidy: remove unused strings

* Document public autofill-api classes

* Tolerate fingerprint-read failures before giving up on auth (#2064)

* Add exported:false in autofill-impl manifest

* tidy: Change gradle dependency from api to implentation

* tidy: change DI scope for viewmodel

* tidy: change DI scope for viewmodel

Co-authored-by: Karl Dimla <klmbdimla@gmail.com>
  • Loading branch information
CDRussell and karlenDimla authored Jul 14, 2022
1 parent 33db1a6 commit 011949c
Show file tree
Hide file tree
Showing 148 changed files with 8,178 additions and 341 deletions.
11 changes: 11 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ fladle {
}

dependencies {
implementation project(path: ':autofill-api')
implementation project(path: ':autofill-impl')
implementation project(path: ':autofill-store')

anvil project(path: ':anvil-compiler')
implementation project(path: ':anvil-annotations')

Expand Down Expand Up @@ -187,6 +191,13 @@ dependencies {

implementation project(path: ':bandwidth-impl')

implementation project(path: ':secure-storage-api')
implementation project(path: ':secure-storage-impl')
implementation project(path: ':secure-storage-store')

implementation project(path: ':device-auth-api')
implementation project(path: ':device-auth-impl')

// Deprecated. TODO: Stop using this artifact.
implementation "androidx.legacy:legacy-support-v4:_"
debugImplementation Square.leakCanary.android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ import com.duckduckgo.app.trackerdetection.EntityLookup
import com.duckduckgo.app.trackerdetection.model.TrackingEvent
import com.duckduckgo.app.usage.search.SearchCountDao
import com.duckduckgo.app.widget.ui.WidgetCapabilities
import com.duckduckgo.autofill.store.AutofillStore
import com.duckduckgo.downloads.api.DownloadCallback
import com.duckduckgo.downloads.api.FileDownloader
import com.duckduckgo.downloads.api.FileDownloader.PendingFileDownload
Expand Down Expand Up @@ -363,6 +364,8 @@ class BrowserTabViewModelTest {

private val favoriteListFlow = Channel<List<Favorite>>()

private val mockAutofillStore: AutofillStore = mock()

@Before
fun before() {
MockitoAnnotations.openMocks(this)
Expand Down Expand Up @@ -459,7 +462,8 @@ class BrowserTabViewModelTest {
trackingParameters = mockTrackingParameters,
voiceSearchAvailability = voiceSearchAvailability,
voiceSearchPixelLogger = voiceSearchPixelLogger,
settingsDataStore = mockSettingsDataStore
settingsDataStore = mockSettingsDataStore,
autofillStore = mockAutofillStore
)

testee.loadData("abc", null, false, false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,11 @@ import com.duckduckgo.app.browser.logindetection.DOMLoginDetector
import com.duckduckgo.app.browser.logindetection.WebNavigationEvent
import com.duckduckgo.app.browser.model.BasicAuthenticationRequest
import com.duckduckgo.app.browser.print.PrintInjector
import com.duckduckgo.app.email.EmailInjector
import com.duckduckgo.app.global.exception.UncaughtExceptionRepository
import com.duckduckgo.app.global.exception.UncaughtExceptionSource
import com.duckduckgo.app.statistics.store.OfflinePixelCountDataStore
import com.duckduckgo.autofill.BrowserAutofill
import com.duckduckgo.autofill.InternalTestUserChecker
import com.duckduckgo.privacy.config.api.Gpc
import com.duckduckgo.privacy.config.api.AmpLinks
import org.mockito.kotlin.any
Expand Down Expand Up @@ -86,10 +87,11 @@ class BrowserWebViewClientTest {
private val trustedCertificateStore: TrustedCertificateStore = mock()
private val webViewHttpAuthStore: WebViewHttpAuthStore = mock()
private val thirdPartyCookieManager: ThirdPartyCookieManager = mock()
private val emailInjector: EmailInjector = mock()
private val browserAutofill: BrowserAutofill = mock()
private val webResourceRequest: WebResourceRequest = mock()
private val ampLinks: AmpLinks = mock()
private val printInjector: PrintInjector = mock()
private val internalTestUserChecker: InternalTestUserChecker = mock()

@UiThreadTest
@Before
Expand All @@ -110,10 +112,11 @@ class BrowserWebViewClientTest {
thirdPartyCookieManager,
TestScope(),
coroutinesTestRule.testDispatcherProvider,
emailInjector,
browserAutofill,
accessibilitySettings,
ampLinks,
printInjector
printInjector,
internalTestUserChecker
)
testee.webViewClientListener = listener
whenever(webResourceRequest.url).thenReturn(Uri.EMPTY)
Expand Down Expand Up @@ -289,7 +292,7 @@ class BrowserWebViewClientTest {
@Test
fun whenOnPageStartedCalledThenInjectEmailAutofillJsCalled() {
testee.onPageStarted(webView, null, null)
verify(emailInjector).injectEmailAutofillJs(webView, null)
verify(browserAutofill).configureAutofillForCurrentPage(webView, null)
}

@Test
Expand Down Expand Up @@ -571,6 +574,22 @@ class BrowserWebViewClientTest {
assertFalse(testee.shouldOverrideUrlLoading(mockWebView, webResourceRequest))
}

@Test
fun whenOnPageFinishedThenCallVerifyVerificationCompleted() {
testee.onPageFinished(webView, EXAMPLE_URL)

verify(internalTestUserChecker).verifyVerificationCompleted(EXAMPLE_URL)
}

@Test
fun whenOnReceivedHttpErrorThenCallVerifyVerificationErrorReceived() {
val mockWebView = getImmediatelyInvokedMockWebView()
whenever(mockWebView.url).thenReturn(EXAMPLE_URL)
testee.onReceivedHttpError(mockWebView, null, null)

verify(internalTestUserChecker).verifyVerificationErrorReceived(EXAMPLE_URL)
}

private fun getImmediatelyInvokedMockWebView(): WebView {
val mockWebView = mock<WebView>()
whenever(mockWebView.originalUrl).thenReturn(EXAMPLE_URL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import com.duckduckgo.app.fire.fireproofwebsite.data.FireproofWebsiteRepository
import com.duckduckgo.app.global.faviconLocation
import com.duckduckgo.app.location.data.LocationPermissionsDao
import com.duckduckgo.app.location.data.LocationPermissionsRepository
import com.duckduckgo.autofill.store.AutofillStore
import org.mockito.kotlin.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Assert.assertNull
Expand All @@ -59,6 +60,7 @@ class DuckDuckGoFaviconManagerTest {
private val mockFireproofWebsiteDao: FireproofWebsiteDao = mock()
private val mockLocationPermissionsDao: LocationPermissionsDao = mock()
private val mockFaviconDownloader: FaviconDownloader = mock()
private val mockAutofillStore: AutofillStore = mock()
private val mockFile: File = File("test")
private val context: Context = InstrumentationRegistry.getInstrumentation().targetContext

Expand All @@ -67,6 +69,7 @@ class DuckDuckGoFaviconManagerTest {
@Before
fun setup() {
whenever(mockFavoriteRepository.favoritesCountByDomain(any())).thenReturn(0)
mockAutofillStore.stub { onBlocking { getCredentials(any()) }.thenReturn(emptyList()) }

testee = DuckDuckGoFaviconManager(
faviconPersister = mockFaviconPersister,
Expand All @@ -75,7 +78,8 @@ class DuckDuckGoFaviconManagerTest {
locationPermissionsRepository = LocationPermissionsRepository(mockLocationPermissionsDao, mock(), coroutineRule.testDispatcherProvider),
favoritesRepository = mockFavoriteRepository,
faviconDownloader = mockFaviconDownloader,
dispatcherProvider = coroutineRule.testDispatcherProvider
dispatcherProvider = coroutineRule.testDispatcherProvider,
autofillStore = mockAutofillStore
)
}

Expand Down
119 changes: 12 additions & 107 deletions app/src/androidTest/java/com/duckduckgo/app/email/EmailInjectorJsTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020 DuckDuckGo
* Copyright (c) 2022 DuckDuckGo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -20,6 +20,8 @@ import android.webkit.WebView
import androidx.test.annotation.UiThreadTest
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry
import com.duckduckgo.app.autofill.FileBasedJavascriptInjector
import com.duckduckgo.app.autofill.JavascriptInjector
import com.duckduckgo.app.browser.DuckDuckGoUrlDetector
import com.duckduckgo.app.browser.R
import com.duckduckgo.app.global.DispatcherProvider
Expand All @@ -37,96 +39,18 @@ class EmailInjectorJsTest {
private val mockDispatcherProvider: DispatcherProvider = mock()
private val mockFeatureToggle: FeatureToggle = mock()
private val mockAutofill: Autofill = mock()
private val javascriptInjector: JavascriptInjector = FileBasedJavascriptInjector()
lateinit var testee: EmailInjectorJs

@Before
fun setup() {
testee = EmailInjectorJs(mockEmailManager, DuckDuckGoUrlDetector(), mockDispatcherProvider, mockFeatureToggle, mockAutofill)
testee =
EmailInjectorJs(mockEmailManager, DuckDuckGoUrlDetector(), mockDispatcherProvider, mockFeatureToggle, javascriptInjector, mockAutofill)

whenever(mockFeatureToggle.isFeatureEnabled(AutofillFeatureName)).thenReturn(true)
whenever(mockAutofill.isAnException(any())).thenReturn(false)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenInjectEmailAutofillJsAndUrlIsFromDuckDuckGoDomainThenInjectJsCode() {
val jsToEvaluate = getJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.injectEmailAutofillJs(webView, "https://duckduckgo.com/email")

verify(webView).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenInjectEmailAutofillJsAndUrlIsFromDuckDuckGoSubdomainThenDoNotInjectJsCode() {
val jsToEvaluate = getJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.injectEmailAutofillJs(webView, "https://test.duckduckgo.com/email")

verify(webView, never()).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenInjectEmailAutofillJsAndUrlIsNotFromDuckDuckGoAndEmailIsSignedInThenInjectJsCode() {
whenever(mockEmailManager.isSignedIn()).thenReturn(true)
val jsToEvaluate = getJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.injectEmailAutofillJs(webView, "https://example.com")

verify(webView).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenInjectEmailAutofillJsAndUrlIsNotFromDuckDuckGoAndEmailIsNotSignedInThenDoNotInjectJsCode() {
whenever(mockEmailManager.isSignedIn()).thenReturn(false)
val jsToEvaluate = getJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.injectEmailAutofillJs(webView, "https://example.com")

verify(webView, never()).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenInjectEmailAutofillJsAndUrlIsFromDuckDuckGoAndFeatureIsDisabledThenInjectJsCode() {
whenever(mockEmailManager.isSignedIn()).thenReturn(true)
whenever(mockFeatureToggle.isFeatureEnabled(AutofillFeatureName)).thenReturn(false)

val jsToEvaluate = getJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.injectEmailAutofillJs(webView, "https://duckduckgo.com/email")

verify(webView).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenInjectEmailAutofillJsAndUrlIsFromDuckDuckGoAndUrlIsInExceptionsThenInjectJsCode() {
whenever(mockEmailManager.isSignedIn()).thenReturn(true)
whenever(mockAutofill.isAnException(any())).thenReturn(true)

val jsToEvaluate = getJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.injectEmailAutofillJs(webView, "https://duckduckgo.com/email")

verify(webView).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
Expand Down Expand Up @@ -224,26 +148,6 @@ class EmailInjectorJsTest {
verify(webView).evaluateJavascript(jsToEvaluate, null)
}

@UiThreadTest
@Test
@SdkSuppress(minSdkVersion = 24)
fun whenNotifyWebAppSignEventAndUrlIsFromDuckDuckGoAndFeatureIsEnabledAndEmailIsSignedInThenDoNotEvaluateJsCode() {
whenever(mockEmailManager.isSignedIn()).thenReturn(true)
whenever(mockFeatureToggle.isFeatureEnabled(AutofillFeatureName)).thenReturn(true)

val jsToEvaluate = getNotifySignOutJsToEvaluate()
val webView = spy(WebView(InstrumentationRegistry.getInstrumentation().targetContext))

testee.notifyWebAppSignEvent(webView, "https://duckduckgo.com/email")

verify(webView, never()).evaluateJavascript(jsToEvaluate, null)
}

private fun getJsToEvaluate(): String {
val js = readResource().use { it?.readText() }.orEmpty()
return "javascript:$js"
}

private fun getAliasJsToEvaluate(): String {
val js = InstrumentationRegistry.getInstrumentation().targetContext.resources.openRawResource(R.raw.inject_alias)
.bufferedReader()
Expand All @@ -252,13 +156,14 @@ class EmailInjectorJsTest {
}

private fun getNotifySignOutJsToEvaluate(): String {
val js = InstrumentationRegistry.getInstrumentation().targetContext.resources.openRawResource(R.raw.signout_autofill)
.bufferedReader()
.use { it.readText() }
val js =
InstrumentationRegistry.getInstrumentation().targetContext.resources.openRawResource(R.raw.signout_autofill)
.bufferedReader()
.use { it.readText() }
return "javascript:$js"
}

private fun readResource(): BufferedReader? {
return javaClass.classLoader?.getResource("autofill.js")?.openStream()?.bufferedReader()
private fun readResource(resourceName: String): BufferedReader? {
return javaClass.classLoader?.getResource(resourceName)?.openStream()?.bufferedReader()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import androidx.work.impl.utils.SynchronousExecutor
import androidx.work.testing.WorkManagerTestInitHelper
import app.cash.turbine.test
import com.duckduckgo.app.CoroutineTestRule
import com.duckduckgo.app.email.AppEmailManager.WaitlistState.JoinedQueue
import com.duckduckgo.app.email.AppEmailManager.WaitlistState.NotJoinedQueue
import com.duckduckgo.app.email.EmailManager.WaitlistState.JoinedQueue
import com.duckduckgo.app.email.EmailManager.WaitlistState.NotJoinedQueue
import com.duckduckgo.app.email.EmailManager
import com.duckduckgo.app.email.api.EmailAlias
import com.duckduckgo.app.email.api.EmailInviteCodeResponse
Expand Down
Loading

0 comments on commit 011949c

Please sign in to comment.