mirror of
https://github.com/bitwarden/android.git
synced 2025-12-11 13:57:03 -06:00
PM-26560: Fix cross-origin autofill issues (#5977)
This commit is contained in:
parent
5706ca2ba3
commit
0604d15d7d
@ -9,6 +9,7 @@ import com.x8bit.bitwarden.data.autofill.model.FilledData
|
|||||||
import com.x8bit.bitwarden.data.autofill.model.FilledPartition
|
import com.x8bit.bitwarden.data.autofill.model.FilledPartition
|
||||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
|
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
|
||||||
import com.x8bit.bitwarden.data.autofill.util.buildFilledItemOrNull
|
import com.x8bit.bitwarden.data.autofill.util.buildFilledItemOrNull
|
||||||
|
import com.x8bit.bitwarden.data.autofill.util.buildUri
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,6 +84,7 @@ class FilledDataBuilderImpl(
|
|||||||
autofillCipher = autofillCipher,
|
autofillCipher = autofillCipher,
|
||||||
autofillViews = autofillRequest.partition.views,
|
autofillViews = autofillRequest.partition.views,
|
||||||
inlinePresentationSpec = getCipherInlinePresentationOrNull(),
|
inlinePresentationSpec = getCipherInlinePresentationOrNull(),
|
||||||
|
packageName = autofillRequest.packageName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +98,9 @@ class FilledDataBuilderImpl(
|
|||||||
?.getOrLastOrNull(inlineSuggestionsAdded)
|
?.getOrLastOrNull(inlineSuggestionsAdded)
|
||||||
|
|
||||||
return FilledData(
|
return FilledData(
|
||||||
filledPartitions = filledPartitions.take(n = MAX_FILLED_PARTITIONS_COUNT),
|
filledPartitions = filledPartitions
|
||||||
|
.filter { it.filledItems.isNotEmpty() }
|
||||||
|
.take(n = MAX_FILLED_PARTITIONS_COUNT),
|
||||||
ignoreAutofillIds = autofillRequest.ignoreAutofillIds,
|
ignoreAutofillIds = autofillRequest.ignoreAutofillIds,
|
||||||
originalPartition = autofillRequest.partition,
|
originalPartition = autofillRequest.partition,
|
||||||
uri = autofillRequest.uri,
|
uri = autofillRequest.uri,
|
||||||
@ -140,16 +144,21 @@ class FilledDataBuilderImpl(
|
|||||||
autofillCipher: AutofillCipher.Login,
|
autofillCipher: AutofillCipher.Login,
|
||||||
autofillViews: List<AutofillView.Login>,
|
autofillViews: List<AutofillView.Login>,
|
||||||
inlinePresentationSpec: InlinePresentationSpec?,
|
inlinePresentationSpec: InlinePresentationSpec?,
|
||||||
|
packageName: String?,
|
||||||
): FilledPartition {
|
): FilledPartition {
|
||||||
val filledItems = autofillViews
|
val filledItems = autofillViews
|
||||||
.mapNotNull { autofillView ->
|
.mapNotNull { autofillView ->
|
||||||
val value = when (autofillView) {
|
if (autofillView.data.website == autofillCipher.website ||
|
||||||
is AutofillView.Login.Username -> autofillCipher.username
|
buildUri(packageName.orEmpty(), "androidapp") == autofillCipher.website
|
||||||
is AutofillView.Login.Password -> autofillCipher.password
|
) {
|
||||||
|
val value = when (autofillView) {
|
||||||
|
is AutofillView.Login.Username -> autofillCipher.username
|
||||||
|
is AutofillView.Login.Password -> autofillCipher.password
|
||||||
|
}
|
||||||
|
autofillView.buildFilledItemOrNull(value = value)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
autofillView.buildFilledItemOrNull(
|
|
||||||
value = value,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FilledPartition(
|
return FilledPartition(
|
||||||
|
|||||||
@ -66,6 +66,7 @@ sealed class AutofillCipher {
|
|||||||
override val subtitle: String,
|
override val subtitle: String,
|
||||||
val password: String,
|
val password: String,
|
||||||
val username: String,
|
val username: String,
|
||||||
|
val website: String,
|
||||||
) : AutofillCipher() {
|
) : AutofillCipher() {
|
||||||
override val iconRes: Int
|
override val iconRes: Int
|
||||||
@DrawableRes get() = BitwardenDrawable.ic_globe
|
@DrawableRes get() = BitwardenDrawable.ic_globe
|
||||||
|
|||||||
@ -16,6 +16,7 @@ sealed class AutofillView {
|
|||||||
* @param isFocused Whether the view is currently focused.
|
* @param isFocused Whether the view is currently focused.
|
||||||
* @param textValue A text value that represents the input present in the field.
|
* @param textValue A text value that represents the input present in the field.
|
||||||
* @param hasPasswordTerms Indicates that the field includes password terms.
|
* @param hasPasswordTerms Indicates that the field includes password terms.
|
||||||
|
* @param website website associated with this view.
|
||||||
*/
|
*/
|
||||||
data class Data(
|
data class Data(
|
||||||
val autofillId: AutofillId,
|
val autofillId: AutofillId,
|
||||||
@ -24,6 +25,7 @@ sealed class AutofillView {
|
|||||||
val isFocused: Boolean,
|
val isFocused: Boolean,
|
||||||
val textValue: String?,
|
val textValue: String?,
|
||||||
val hasPasswordTerms: Boolean,
|
val hasPasswordTerms: Boolean,
|
||||||
|
val website: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -8,11 +8,9 @@ import android.view.autofill.AutofillId
|
|||||||
* @param autofillViews The list of views we care about for autofilling.
|
* @param autofillViews The list of views we care about for autofilling.
|
||||||
* @param idPackage The package id for this view, if there is one.
|
* @param idPackage The package id for this view, if there is one.
|
||||||
* @param ignoreAutofillIds The list of [AutofillId]s that should be ignored in the fill response.
|
* @param ignoreAutofillIds The list of [AutofillId]s that should be ignored in the fill response.
|
||||||
* @param website The website that is being displayed in the app, given there is one.
|
|
||||||
*/
|
*/
|
||||||
data class ViewNodeTraversalData(
|
data class ViewNodeTraversalData(
|
||||||
val autofillViews: List<AutofillView>,
|
val autofillViews: List<AutofillView>,
|
||||||
val idPackage: String?,
|
val idPackage: String?,
|
||||||
val ignoreAutofillIds: List<AutofillId>,
|
val ignoreAutofillIds: List<AutofillId>,
|
||||||
val website: String?,
|
|
||||||
)
|
)
|
||||||
|
|||||||
@ -95,16 +95,21 @@ class AutofillParserImpl(
|
|||||||
.firstOrNull { it.data.isFocused }
|
.firstOrNull { it.data.isFocused }
|
||||||
?: autofillViews.firstOrNull()
|
?: autofillViews.firstOrNull()
|
||||||
|
|
||||||
|
if (focusedView == null) {
|
||||||
|
// The view is unfillable if there are no focused views.
|
||||||
|
return AutofillRequest.Unfillable
|
||||||
|
}
|
||||||
|
|
||||||
val packageName = traversalDataList.buildPackageNameOrNull(
|
val packageName = traversalDataList.buildPackageNameOrNull(
|
||||||
assistStructure = assistStructure,
|
assistStructure = assistStructure,
|
||||||
)
|
)
|
||||||
val uri = traversalDataList.buildUriOrNull(
|
val uri = focusedView.buildUriOrNull(
|
||||||
packageName = packageName,
|
packageName = packageName,
|
||||||
)
|
)
|
||||||
|
|
||||||
val blockListedURIs = settingsRepository.blockedAutofillUris + BLOCK_LISTED_URIS
|
val blockListedURIs = settingsRepository.blockedAutofillUris + BLOCK_LISTED_URIS
|
||||||
if (focusedView == null || blockListedURIs.contains(uri)) {
|
if (blockListedURIs.contains(uri)) {
|
||||||
// The view is unfillable if there are no focused views or the URI is block listed.
|
// The view is unfillable if the URI is block listed.
|
||||||
return AutofillRequest.Unfillable
|
return AutofillRequest.Unfillable
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +170,7 @@ private fun AssistStructure.traverse(): List<ViewNodeTraversalData> =
|
|||||||
.mapNotNull { windowNode ->
|
.mapNotNull { windowNode ->
|
||||||
windowNode
|
windowNode
|
||||||
.rootViewNode
|
.rootViewNode
|
||||||
?.traverse()
|
?.traverse(parentWebsite = null)
|
||||||
?.updateForMissingPasswordFields()
|
?.updateForMissingPasswordFields()
|
||||||
?.updateForMissingUsernameFields()
|
?.updateForMissingUsernameFields()
|
||||||
}
|
}
|
||||||
@ -243,16 +248,17 @@ private fun ViewNodeTraversalData.copyAndMapAutofillViews(
|
|||||||
* Recursively traverse this [AssistStructure.ViewNode] and all of its descendants. Convert the
|
* Recursively traverse this [AssistStructure.ViewNode] and all of its descendants. Convert the
|
||||||
* data into [ViewNodeTraversalData].
|
* data into [ViewNodeTraversalData].
|
||||||
*/
|
*/
|
||||||
private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
private fun AssistStructure.ViewNode.traverse(
|
||||||
|
parentWebsite: String?,
|
||||||
|
): ViewNodeTraversalData {
|
||||||
// Set up mutable lists for collecting valid AutofillViews and ignorable view ids.
|
// Set up mutable lists for collecting valid AutofillViews and ignorable view ids.
|
||||||
val mutableAutofillViewList: MutableList<AutofillView> = mutableListOf()
|
val mutableAutofillViewList: MutableList<AutofillView> = mutableListOf()
|
||||||
val mutableIgnoreAutofillIdList: MutableList<AutofillId> = mutableListOf()
|
val mutableIgnoreAutofillIdList: MutableList<AutofillId> = mutableListOf()
|
||||||
var idPackage: String? = this.idPackage
|
var idPackage: String? = this.idPackage
|
||||||
var website: String? = this.website
|
|
||||||
|
|
||||||
// Try converting this `ViewNode` into an `AutofillView`. If a valid instance is returned, add
|
// Try converting this `ViewNode` into an `AutofillView`. If a valid instance is returned, add
|
||||||
// it to the list. Otherwise, ignore the `AutofillId` associated with this `ViewNode`.
|
// it to the list. Otherwise, ignore the `AutofillId` associated with this `ViewNode`.
|
||||||
toAutofillView()
|
toAutofillView(parentWebsite = parentWebsite)
|
||||||
?.run(mutableAutofillViewList::add)
|
?.run(mutableAutofillViewList::add)
|
||||||
?: autofillId?.run(mutableIgnoreAutofillIdList::add)
|
?: autofillId?.run(mutableIgnoreAutofillIdList::add)
|
||||||
|
|
||||||
@ -260,7 +266,7 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
|||||||
for (i in 0 until childCount) {
|
for (i in 0 until childCount) {
|
||||||
// Extract the traversal data from each child view node and add it to the lists.
|
// Extract the traversal data from each child view node and add it to the lists.
|
||||||
getChildAt(i)
|
getChildAt(i)
|
||||||
.traverse()
|
.traverse(parentWebsite = website)
|
||||||
.let { viewNodeTraversalData ->
|
.let { viewNodeTraversalData ->
|
||||||
viewNodeTraversalData.autofillViews.forEach(mutableAutofillViewList::add)
|
viewNodeTraversalData.autofillViews.forEach(mutableAutofillViewList::add)
|
||||||
viewNodeTraversalData.ignoreAutofillIds.forEach(mutableIgnoreAutofillIdList::add)
|
viewNodeTraversalData.ignoreAutofillIds.forEach(mutableIgnoreAutofillIdList::add)
|
||||||
@ -273,10 +279,6 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
|||||||
) {
|
) {
|
||||||
idPackage = viewNodeTraversalData.idPackage
|
idPackage = viewNodeTraversalData.idPackage
|
||||||
}
|
}
|
||||||
// Get the first non-null website.
|
|
||||||
if (website == null) {
|
|
||||||
website = viewNodeTraversalData.website
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,6 +288,5 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData {
|
|||||||
autofillViews = mutableAutofillViewList,
|
autofillViews = mutableAutofillViewList,
|
||||||
idPackage = idPackage,
|
idPackage = idPackage,
|
||||||
ignoreAutofillIds = mutableIgnoreAutofillIdList,
|
ignoreAutofillIds = mutableIgnoreAutofillIdList,
|
||||||
website = website,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -127,6 +127,7 @@ class AutofillCipherProviderImpl(
|
|||||||
password = cipherView.login?.password.orEmpty(),
|
password = cipherView.login?.password.orEmpty(),
|
||||||
subtitle = cipherView.subtitle.orEmpty(),
|
subtitle = cipherView.subtitle.orEmpty(),
|
||||||
username = cipherView.login?.username.orEmpty(),
|
username = cipherView.login?.username.orEmpty(),
|
||||||
|
website = uri,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,15 @@ import android.view.View
|
|||||||
import android.view.autofill.AutofillValue
|
import android.view.autofill.AutofillValue
|
||||||
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
||||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||||
|
import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData
|
||||||
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
import com.x8bit.bitwarden.ui.vault.model.VaultCardBrand
|
||||||
import com.x8bit.bitwarden.ui.vault.model.findVaultCardBrandWithNameOrNull
|
import com.x8bit.bitwarden.ui.vault.model.findVaultCardBrandWithNameOrNull
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The android app URI scheme. Example: androidapp://com.x8bit.bitwarden
|
||||||
|
*/
|
||||||
|
private const val ANDROID_APP_SCHEME: String = "androidapp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert this [AutofillView] into a [FilledItem]. Return null if not possible.
|
* Convert this [AutofillView] into a [FilledItem]. Return null if not possible.
|
||||||
*/
|
*/
|
||||||
@ -96,3 +102,17 @@ private fun AutofillView.buildListAutofillValueOrNull(
|
|||||||
?.let { AutofillValue.forList(it) }
|
?.let { AutofillValue.forList(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try and build a URI. First, try building a website from the list of [ViewNodeTraversalData]. If
|
||||||
|
* that fails, try converting [packageName] into an Android app URI.
|
||||||
|
*/
|
||||||
|
fun AutofillView.buildUriOrNull(
|
||||||
|
packageName: String?,
|
||||||
|
): String? {
|
||||||
|
// Search list of ViewNodeTraversalData for a website URI.
|
||||||
|
this.data.website?.let { websiteUri -> return websiteUri }
|
||||||
|
|
||||||
|
// If the package name is available, build a URI out of that.
|
||||||
|
return packageName?.let { buildUri(domain = it, scheme = ANDROID_APP_SCHEME) }
|
||||||
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ fun CipherView.toAutofillCipherProvider(): AutofillCipherProvider =
|
|||||||
password = login.password.orEmpty(),
|
password = login.password.orEmpty(),
|
||||||
subtitle = subtitle.orEmpty(),
|
subtitle = subtitle.orEmpty(),
|
||||||
username = login.username.orEmpty(),
|
username = login.username.orEmpty(),
|
||||||
|
website = uri,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,9 @@ private val AssistStructure.ViewNode.isInputField: Boolean
|
|||||||
* doesn't contain a valid autofillId, it isn't an a view setup for autofill, so we return null. If
|
* doesn't contain a valid autofillId, it isn't an a view setup for autofill, so we return null. If
|
||||||
* it doesn't have a supported hint and isn't an input field, we also return null.
|
* it doesn't have a supported hint and isn't an input field, we also return null.
|
||||||
*/
|
*/
|
||||||
fun AssistStructure.ViewNode.toAutofillView(): AutofillView? =
|
fun AssistStructure.ViewNode.toAutofillView(
|
||||||
|
parentWebsite: String?,
|
||||||
|
): AutofillView? =
|
||||||
this
|
this
|
||||||
.autofillId
|
.autofillId
|
||||||
// We only care about nodes with a valid `AutofillId`.
|
// We only care about nodes with a valid `AutofillId`.
|
||||||
@ -67,6 +69,7 @@ fun AssistStructure.ViewNode.toAutofillView(): AutofillView? =
|
|||||||
isFocused = this.isFocused,
|
isFocused = this.isFocused,
|
||||||
textValue = this.autofillValue?.extractTextValue(),
|
textValue = this.autofillValue?.extractTextValue(),
|
||||||
hasPasswordTerms = this.hasPasswordTerms(),
|
hasPasswordTerms = this.hasPasswordTerms(),
|
||||||
|
website = this.website ?: parentWebsite,
|
||||||
)
|
)
|
||||||
buildAutofillView(
|
buildAutofillView(
|
||||||
autofillOptions = autofillOptions,
|
autofillOptions = autofillOptions,
|
||||||
|
|||||||
@ -4,36 +4,6 @@ import android.app.assist.AssistStructure
|
|||||||
import com.bitwarden.ui.platform.base.util.orNullIfBlank
|
import com.bitwarden.ui.platform.base.util.orNullIfBlank
|
||||||
import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData
|
import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData
|
||||||
|
|
||||||
/**
|
|
||||||
* The android app URI scheme. Example: androidapp://com.x8bit.bitwarden
|
|
||||||
*/
|
|
||||||
private const val ANDROID_APP_SCHEME: String = "androidapp"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try and build a URI. First, try building a website from the list of [ViewNodeTraversalData]. If
|
|
||||||
* that fails, try converting [packageName] into an Android app URI.
|
|
||||||
*/
|
|
||||||
fun List<ViewNodeTraversalData>.buildUriOrNull(
|
|
||||||
packageName: String?,
|
|
||||||
): String? {
|
|
||||||
// Search list of ViewNodeTraversalData for a website URI.
|
|
||||||
this
|
|
||||||
.firstOrNull { it.website != null }
|
|
||||||
?.website
|
|
||||||
?.let { websiteUri ->
|
|
||||||
return websiteUri
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the package name is available, build a URI out of that.
|
|
||||||
return packageName
|
|
||||||
?.let { nonNullPackageName ->
|
|
||||||
buildUri(
|
|
||||||
domain = nonNullPackageName,
|
|
||||||
scheme = ANDROID_APP_SCHEME,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try and build a package name. First, try searching traversal data for package names. If that
|
* Try and build a package name. First, try searching traversal data for package names. If that
|
||||||
* fails, try extracting a package name from [assistStructure].
|
* fails, try extracting a package name from [assistStructure].
|
||||||
|
|||||||
@ -145,6 +145,7 @@ class FillResponseBuilderTest {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -246,6 +247,7 @@ class FillResponseBuilderTest {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -61,16 +61,20 @@ class FilledDataBuilderTest {
|
|||||||
password = password,
|
password = password,
|
||||||
username = username,
|
username = username,
|
||||||
subtitle = "Subtitle",
|
subtitle = "Subtitle",
|
||||||
|
website = URI,
|
||||||
)
|
)
|
||||||
val filledItemPassword: FilledItem = mockk()
|
val filledItemPassword: FilledItem = mockk()
|
||||||
val filledItemUsername: FilledItem = mockk()
|
val filledItemUsername: FilledItem = mockk()
|
||||||
val autofillViewPassword: AutofillView.Login.Password = mockk {
|
val autofillViewPassword: AutofillView.Login.Password = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(password) } returns filledItemPassword
|
every { buildFilledItemOrNull(password) } returns filledItemPassword
|
||||||
}
|
}
|
||||||
val autofillViewUsernameOne: AutofillView.Login.Username = mockk {
|
val autofillViewUsernameOne: AutofillView.Login.Username = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(username) } returns filledItemUsername
|
every { buildFilledItemOrNull(username) } returns filledItemUsername
|
||||||
}
|
}
|
||||||
val autofillViewUsernameTwo: AutofillView.Login.Username = mockk {
|
val autofillViewUsernameTwo: AutofillView.Login.Username = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(username) } returns null
|
every { buildFilledItemOrNull(username) } returns null
|
||||||
}
|
}
|
||||||
val autofillPartition = AutofillPartition.Login(
|
val autofillPartition = AutofillPartition.Login(
|
||||||
@ -341,15 +345,8 @@ class FilledDataBuilderTest {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
val filledPartition = FilledPartition(
|
|
||||||
autofillCipher = autofillCipher,
|
|
||||||
filledItems = emptyList(),
|
|
||||||
inlinePresentationSpec = null,
|
|
||||||
)
|
|
||||||
val expected = FilledData(
|
val expected = FilledData(
|
||||||
filledPartitions = listOf(
|
filledPartitions = emptyList(),
|
||||||
filledPartition,
|
|
||||||
),
|
|
||||||
ignoreAutofillIds = ignoreAutofillIds,
|
ignoreAutofillIds = ignoreAutofillIds,
|
||||||
originalPartition = autofillPartition,
|
originalPartition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
@ -396,14 +393,17 @@ class FilledDataBuilderTest {
|
|||||||
password = password,
|
password = password,
|
||||||
username = username,
|
username = username,
|
||||||
subtitle = "Subtitle",
|
subtitle = "Subtitle",
|
||||||
|
website = URI,
|
||||||
)
|
)
|
||||||
|
|
||||||
val filledItemPassword: FilledItem = mockk()
|
val filledItemPassword: FilledItem = mockk()
|
||||||
val filledItemUsername: FilledItem = mockk()
|
val filledItemUsername: FilledItem = mockk()
|
||||||
val autofillViewPassword: AutofillView.Login.Password = mockk {
|
val autofillViewPassword: AutofillView.Login.Password = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(password) } returns filledItemPassword
|
every { buildFilledItemOrNull(password) } returns filledItemPassword
|
||||||
}
|
}
|
||||||
val autofillViewUsername: AutofillView.Login.Username = mockk {
|
val autofillViewUsername: AutofillView.Login.Username = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(username) } returns filledItemUsername
|
every { buildFilledItemOrNull(username) } returns filledItemUsername
|
||||||
}
|
}
|
||||||
val autofillPartition = AutofillPartition.Login(
|
val autofillPartition = AutofillPartition.Login(
|
||||||
@ -490,13 +490,16 @@ class FilledDataBuilderTest {
|
|||||||
password = password,
|
password = password,
|
||||||
username = username,
|
username = username,
|
||||||
subtitle = "Subtitle",
|
subtitle = "Subtitle",
|
||||||
|
website = URI,
|
||||||
)
|
)
|
||||||
val filledItemPassword: FilledItem = mockk()
|
val filledItemPassword: FilledItem = mockk()
|
||||||
val filledItemUsername: FilledItem = mockk()
|
val filledItemUsername: FilledItem = mockk()
|
||||||
val autofillViewPassword: AutofillView.Login.Password = mockk {
|
val autofillViewPassword: AutofillView.Login.Password = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(password) } returns filledItemPassword
|
every { buildFilledItemOrNull(password) } returns filledItemPassword
|
||||||
}
|
}
|
||||||
val autofillViewUsername: AutofillView.Login.Username = mockk {
|
val autofillViewUsername: AutofillView.Login.Username = mockk {
|
||||||
|
every { data } returns mockk { every { website } returns URI }
|
||||||
every { buildFilledItemOrNull(username) } returns filledItemUsername
|
every { buildFilledItemOrNull(username) } returns filledItemUsername
|
||||||
}
|
}
|
||||||
val autofillPartition = AutofillPartition.Login(
|
val autofillPartition = AutofillPartition.Login(
|
||||||
|
|||||||
@ -35,6 +35,7 @@ class SaveInfoBuilderTest {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
)
|
)
|
||||||
private val autofillIdValid: AutofillId = mockk()
|
private val autofillIdValid: AutofillId = mockk()
|
||||||
private val autofillViewDataValid = AutofillView.Data(
|
private val autofillViewDataValid = AutofillView.Data(
|
||||||
@ -44,6 +45,7 @@ class SaveInfoBuilderTest {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
)
|
)
|
||||||
private val autofillPartitionCard: AutofillPartition.Card = AutofillPartition.Card(
|
private val autofillPartitionCard: AutofillPartition.Card = AutofillPartition.Card(
|
||||||
views = listOf(
|
views = listOf(
|
||||||
|
|||||||
@ -78,8 +78,9 @@ class AutofillParserTests {
|
|||||||
mockkStatic(
|
mockkStatic(
|
||||||
FillRequest::getMaxInlineSuggestionsCount,
|
FillRequest::getMaxInlineSuggestionsCount,
|
||||||
FillRequest::getInlinePresentationSpecs,
|
FillRequest::getInlinePresentationSpecs,
|
||||||
|
AutofillView::buildUriOrNull,
|
||||||
|
List<ViewNodeTraversalData>::buildPackageNameOrNull,
|
||||||
)
|
)
|
||||||
mockkStatic(List<ViewNodeTraversalData>::buildUriOrNull)
|
|
||||||
every { cardViewNode.website } returns WEBSITE
|
every { cardViewNode.website } returns WEBSITE
|
||||||
every { loginViewNode.website } returns WEBSITE
|
every { loginViewNode.website } returns WEBSITE
|
||||||
every {
|
every {
|
||||||
@ -121,7 +122,7 @@ class AutofillParserTests {
|
|||||||
every {
|
every {
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
} returns PACKAGE_NAME
|
} returns PACKAGE_NAME
|
||||||
every { any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME) } returns URI
|
every { any<AutofillView>().buildUriOrNull(PACKAGE_NAME) } returns URI
|
||||||
parser = AutofillParserImpl(
|
parser = AutofillParserImpl(
|
||||||
settingsRepository = settingsRepository,
|
settingsRepository = settingsRepository,
|
||||||
)
|
)
|
||||||
@ -134,8 +135,9 @@ class AutofillParserTests {
|
|||||||
unmockkStatic(
|
unmockkStatic(
|
||||||
FillRequest::getMaxInlineSuggestionsCount,
|
FillRequest::getMaxInlineSuggestionsCount,
|
||||||
FillRequest::getInlinePresentationSpecs,
|
FillRequest::getInlinePresentationSpecs,
|
||||||
|
AutofillView::buildUriOrNull,
|
||||||
|
List<ViewNodeTraversalData>::buildPackageNameOrNull,
|
||||||
)
|
)
|
||||||
unmockkStatic(List<ViewNodeTraversalData>::buildUriOrNull)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -181,7 +183,7 @@ class AutofillParserTests {
|
|||||||
every { this@mockk.childCount } returns 0
|
every { this@mockk.childCount } returns 0
|
||||||
every { this@mockk.idPackage } returns null
|
every { this@mockk.idPackage } returns null
|
||||||
every { this@mockk.isFocused } returns false
|
every { this@mockk.isFocused } returns false
|
||||||
every { this@mockk.toAutofillView() } returns null
|
every { this@mockk.toAutofillView(parentWebsite = any()) } returns null
|
||||||
every { this@mockk.website } returns null
|
every { this@mockk.website } returns null
|
||||||
}
|
}
|
||||||
// `invalidChildViewNode` simulates the OS assigning a node's idPackage to "android", which
|
// `invalidChildViewNode` simulates the OS assigning a node's idPackage to "android", which
|
||||||
@ -194,7 +196,7 @@ class AutofillParserTests {
|
|||||||
every { this@mockk.childCount } returns 0
|
every { this@mockk.childCount } returns 0
|
||||||
every { this@mockk.idPackage } returns ID_PACKAGE_ANDROID
|
every { this@mockk.idPackage } returns ID_PACKAGE_ANDROID
|
||||||
every { this@mockk.isFocused } returns false
|
every { this@mockk.isFocused } returns false
|
||||||
every { this@mockk.toAutofillView() } returns null
|
every { this@mockk.toAutofillView(parentWebsite = any()) } returns null
|
||||||
every { this@mockk.website } returns null
|
every { this@mockk.website } returns null
|
||||||
}
|
}
|
||||||
val parentAutofillHint = View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR
|
val parentAutofillHint = View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR
|
||||||
@ -207,6 +209,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -214,7 +217,7 @@ class AutofillParserTests {
|
|||||||
every { this@mockk.autofillHints } returns arrayOf(parentAutofillHint)
|
every { this@mockk.autofillHints } returns arrayOf(parentAutofillHint)
|
||||||
every { this@mockk.autofillId } returns parentAutofillId
|
every { this@mockk.autofillId } returns parentAutofillId
|
||||||
every { this@mockk.idPackage } returns null
|
every { this@mockk.idPackage } returns null
|
||||||
every { this@mockk.toAutofillView() } returns parentAutofillView
|
every { this@mockk.toAutofillView(parentWebsite = any()) } returns parentAutofillView
|
||||||
every { this@mockk.childCount } returns 2
|
every { this@mockk.childCount } returns 2
|
||||||
every { this@mockk.getChildAt(0) } returns childViewNode
|
every { this@mockk.getChildAt(0) } returns childViewNode
|
||||||
every { this@mockk.getChildAt(1) } returns invalidChildViewNode
|
every { this@mockk.getChildAt(1) } returns invalidChildViewNode
|
||||||
@ -255,10 +258,10 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
verify(exactly = 0) {
|
verify(exactly = 0) {
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(ID_PACKAGE_ANDROID)
|
any<AutofillView>().buildUriOrNull(ID_PACKAGE_ANDROID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,6 +277,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -285,6 +289,7 @@ class AutofillParserTests {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Card(
|
val autofillPartition = AutofillPartition.Card(
|
||||||
@ -298,8 +303,8 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -319,7 +324,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,6 +340,7 @@ class AutofillParserTests {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -346,6 +352,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Login(
|
val autofillPartition = AutofillPartition.Login(
|
||||||
@ -359,8 +366,8 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -380,7 +387,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,6 +405,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = true,
|
hasPasswordTerms = true,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val loginAutofillView: AutofillView.Login = AutofillView.Login.Password(
|
val loginAutofillView: AutofillView.Login = AutofillView.Login.Password(
|
||||||
@ -414,7 +422,7 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { loginViewNode.toAutofillView() } returns unusedAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns unusedAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -434,7 +442,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,6 +489,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val loginUsernameAutofillView: AutofillView.Login = AutofillView.Login.Username(
|
val loginUsernameAutofillView: AutofillView.Login = AutofillView.Login.Username(
|
||||||
@ -491,6 +500,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val loginPasswordAutofillView: AutofillView.Login = AutofillView.Login.Password(
|
val loginPasswordAutofillView: AutofillView.Login = AutofillView.Login.Password(
|
||||||
@ -501,6 +511,7 @@ class AutofillParserTests {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Login(
|
val autofillPartition = AutofillPartition.Login(
|
||||||
@ -514,9 +525,13 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { rootViewNode.toAutofillView() } returns null
|
every { rootViewNode.toAutofillView(parentWebsite = any()) } returns null
|
||||||
every { hiddenUserNameViewNode.toAutofillView() } returns unusedAutofillView
|
every {
|
||||||
every { passwordViewNode.toAutofillView() } returns loginPasswordAutofillView
|
hiddenUserNameViewNode.toAutofillView(parentWebsite = any())
|
||||||
|
} returns unusedAutofillView
|
||||||
|
every {
|
||||||
|
passwordViewNode.toAutofillView(parentWebsite = any())
|
||||||
|
} returns loginPasswordAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -536,7 +551,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -552,6 +567,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -563,6 +579,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Card(
|
val autofillPartition = AutofillPartition.Card(
|
||||||
@ -576,8 +593,8 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -597,7 +614,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,6 +631,7 @@ class AutofillParserTests {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -625,6 +643,7 @@ class AutofillParserTests {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Card(
|
val autofillPartition = AutofillPartition.Card(
|
||||||
@ -638,8 +657,8 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -659,7 +678,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = true,
|
isInlineAutofillEnabled = true,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -676,6 +695,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -687,6 +707,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Card(
|
val autofillPartition = AutofillPartition.Card(
|
||||||
@ -700,8 +721,8 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -721,7 +742,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = false,
|
isInlineAutofillEnabled = false,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -738,6 +759,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -749,6 +771,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val autofillPartition = AutofillPartition.Card(
|
val autofillPartition = AutofillPartition.Card(
|
||||||
@ -762,8 +785,8 @@ class AutofillParserTests {
|
|||||||
partition = autofillPartition,
|
partition = autofillPartition,
|
||||||
uri = URI,
|
uri = URI,
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -783,7 +806,7 @@ class AutofillParserTests {
|
|||||||
isInlineAutofillEnabled = false,
|
isInlineAutofillEnabled = false,
|
||||||
)
|
)
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,6 +822,7 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
monthValue = null,
|
monthValue = null,
|
||||||
)
|
)
|
||||||
@ -810,22 +834,21 @@ class AutofillParserTests {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = URI,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
val remoteBlockList = listOf(
|
val remoteBlockList = listOf(
|
||||||
"blockListedUri.com",
|
"blockListedUri.com",
|
||||||
"blockListedAgainUri.com",
|
"blockListedAgainUri.com",
|
||||||
)
|
)
|
||||||
every { cardViewNode.toAutofillView() } returns cardAutofillView
|
every { cardViewNode.toAutofillView(parentWebsite = any()) } returns cardAutofillView
|
||||||
every { loginViewNode.toAutofillView() } returns loginAutofillView
|
every { loginViewNode.toAutofillView(parentWebsite = any()) } returns loginAutofillView
|
||||||
every { settingsRepository.blockedAutofillUris } returns remoteBlockList
|
every { settingsRepository.blockedAutofillUris } returns remoteBlockList
|
||||||
|
|
||||||
// A function for asserting that a block listed URI results in an unfillable request.
|
// A function for asserting that a block listed URI results in an unfillable request.
|
||||||
fun testBlockListedUri(blockListedUri: String) {
|
fun testBlockListedUri(blockListedUri: String) {
|
||||||
// Setup
|
// Setup
|
||||||
every {
|
every { any<AutofillView>().buildUriOrNull(PACKAGE_NAME) } returns blockListedUri
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
|
||||||
} returns blockListedUri
|
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = parser.parse(
|
val actual = parser.parse(
|
||||||
@ -844,7 +867,7 @@ class AutofillParserTests {
|
|||||||
// Verify all tests
|
// Verify all tests
|
||||||
verify(exactly = BLOCK_LISTED_URIS.size + remoteBlockList.size) {
|
verify(exactly = BLOCK_LISTED_URIS.size + remoteBlockList.size) {
|
||||||
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
any<List<ViewNodeTraversalData>>().buildPackageNameOrNull(assistStructure)
|
||||||
any<List<ViewNodeTraversalData>>().buildUriOrNull(PACKAGE_NAME)
|
any<AutofillView>().buildUriOrNull(PACKAGE_NAME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -604,6 +604,7 @@ private const val LOGIN_NAME = "John's Login"
|
|||||||
private const val LOGIN_PASSWORD = "Password123"
|
private const val LOGIN_PASSWORD = "Password123"
|
||||||
private const val LOGIN_SUBTITLE = "John Doe"
|
private const val LOGIN_SUBTITLE = "John Doe"
|
||||||
private const val LOGIN_USERNAME = "John-Bitwarden"
|
private const val LOGIN_USERNAME = "John-Bitwarden"
|
||||||
|
private const val URI: String = "androidapp://com.x8bit.bitwarden"
|
||||||
private val LOGIN_AUTOFILL_CIPHER_WITH_TOTP = AutofillCipher.Login(
|
private val LOGIN_AUTOFILL_CIPHER_WITH_TOTP = AutofillCipher.Login(
|
||||||
cipherId = LOGIN_WITH_TOTP_CIPHER_ID,
|
cipherId = LOGIN_WITH_TOTP_CIPHER_ID,
|
||||||
isTotpEnabled = true,
|
isTotpEnabled = true,
|
||||||
@ -611,6 +612,7 @@ private val LOGIN_AUTOFILL_CIPHER_WITH_TOTP = AutofillCipher.Login(
|
|||||||
password = LOGIN_PASSWORD,
|
password = LOGIN_PASSWORD,
|
||||||
subtitle = LOGIN_SUBTITLE,
|
subtitle = LOGIN_SUBTITLE,
|
||||||
username = LOGIN_USERNAME,
|
username = LOGIN_USERNAME,
|
||||||
|
website = URI,
|
||||||
)
|
)
|
||||||
private val LOGIN_AUTOFILL_CIPHER_WITHOUT_TOTP = AutofillCipher.Login(
|
private val LOGIN_AUTOFILL_CIPHER_WITHOUT_TOTP = AutofillCipher.Login(
|
||||||
cipherId = LOGIN_WITHOUT_TOTP_CIPHER_ID,
|
cipherId = LOGIN_WITHOUT_TOTP_CIPHER_ID,
|
||||||
@ -619,5 +621,5 @@ private val LOGIN_AUTOFILL_CIPHER_WITHOUT_TOTP = AutofillCipher.Login(
|
|||||||
password = LOGIN_PASSWORD,
|
password = LOGIN_PASSWORD,
|
||||||
subtitle = LOGIN_SUBTITLE,
|
subtitle = LOGIN_SUBTITLE,
|
||||||
username = LOGIN_USERNAME,
|
username = LOGIN_USERNAME,
|
||||||
|
website = URI,
|
||||||
)
|
)
|
||||||
private const val URI: String = "androidapp://com.x8bit.bitwarden"
|
|
||||||
|
|||||||
@ -16,6 +16,7 @@ class AutofillPartitionExtensionsTest {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
)
|
)
|
||||||
private val autofillDataValidText: AutofillView.Data = AutofillView.Data(
|
private val autofillDataValidText: AutofillView.Data = AutofillView.Data(
|
||||||
autofillId = mockk(),
|
autofillId = mockk(),
|
||||||
@ -24,6 +25,7 @@ class AutofillPartitionExtensionsTest {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = TEXT_VALUE,
|
textValue = TEXT_VALUE,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
//region Card tests
|
//region Card tests
|
||||||
|
|||||||
@ -28,6 +28,7 @@ class AutofillViewExtensionsTest {
|
|||||||
isFocused = false,
|
isFocused = false,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
@ -349,4 +350,47 @@ class AutofillViewExtensionsTest {
|
|||||||
// Verify
|
// Verify
|
||||||
assertNull(actual)
|
assertNull(actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `buildUriOrNull should return website URI when present`() {
|
||||||
|
// Setup
|
||||||
|
val autofillViewData = autofillViewData.copy(website = WEBSITE)
|
||||||
|
val autofillView = AutofillView.Login.Username(data = autofillViewData)
|
||||||
|
|
||||||
|
// Test
|
||||||
|
val actual = autofillView.buildUriOrNull(packageName = PACKAGE_NAME)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(WEBSITE, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `buildUriOrNull should return package name URI when website is null`() {
|
||||||
|
// Setup
|
||||||
|
val autofillViewData = autofillViewData.copy(website = null)
|
||||||
|
val autofillView = AutofillView.Login.Username(data = autofillViewData)
|
||||||
|
val expected = "androidapp://$PACKAGE_NAME"
|
||||||
|
|
||||||
|
// Test
|
||||||
|
val actual = autofillView.buildUriOrNull(packageName = PACKAGE_NAME)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `buildUriOrNull should return null when website and packageName are null`() {
|
||||||
|
// Setup
|
||||||
|
val autofillViewData = autofillViewData.copy(website = null)
|
||||||
|
val autofillView = AutofillView.Login.Username(data = autofillViewData)
|
||||||
|
|
||||||
|
// Test
|
||||||
|
val actual = autofillView.buildUriOrNull(packageName = null)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertNull(actual)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private const val PACKAGE_NAME: String = "com.google"
|
||||||
|
private const val WEBSITE: String = "https://www.google.com"
|
||||||
|
|||||||
@ -39,6 +39,7 @@ class CipherViewExtensionsTest {
|
|||||||
subtitle = "mockUsername-1",
|
subtitle = "mockUsername-1",
|
||||||
password = "mockPassword-1",
|
password = "mockPassword-1",
|
||||||
username = "mockUsername-1",
|
username = "mockUsername-1",
|
||||||
|
website = "uri",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
autofillCipherProvider.getLoginAutofillCiphers(uri = "uri"),
|
autofillCipherProvider.getLoginAutofillCiphers(uri = "uri"),
|
||||||
@ -71,6 +72,7 @@ class CipherViewExtensionsTest {
|
|||||||
subtitle = "mockUsername-1",
|
subtitle = "mockUsername-1",
|
||||||
password = "mockPassword-1",
|
password = "mockPassword-1",
|
||||||
username = "mockUsername-1",
|
username = "mockUsername-1",
|
||||||
|
website = "uri",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
autofillCipherProvider.getLoginAutofillCiphers(uri = "uri"),
|
autofillCipherProvider.getLoginAutofillCiphers(uri = "uri"),
|
||||||
|
|||||||
@ -75,6 +75,7 @@ class FilledDataExtensionsTest {
|
|||||||
isFocused = true,
|
isFocused = true,
|
||||||
textValue = null,
|
textValue = null,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = "uri",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -29,6 +29,7 @@ class ViewNodeExtensionsTest {
|
|||||||
isFocused = expectedIsFocused,
|
isFocused = expectedIsFocused,
|
||||||
textValue = TEXT_VALUE,
|
textValue = TEXT_VALUE,
|
||||||
hasPasswordTerms = false,
|
hasPasswordTerms = false,
|
||||||
|
website = null,
|
||||||
)
|
)
|
||||||
private val testAutofillValue: AutofillValue = mockk()
|
private val testAutofillValue: AutofillValue = mockk()
|
||||||
private val mockHtmlInfo: HtmlInfo = mockk {
|
private val mockHtmlInfo: HtmlInfo = mockk {
|
||||||
@ -46,10 +47,12 @@ class ViewNodeExtensionsTest {
|
|||||||
every { inputType } returns 1
|
every { inputType } returns 1
|
||||||
every { isFocused } returns expectedIsFocused
|
every { isFocused } returns expectedIsFocused
|
||||||
every { htmlInfo } returns mockHtmlInfo
|
every { htmlInfo } returns mockHtmlInfo
|
||||||
|
every { website } returns null
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
|
mockkStatic(AssistStructure.ViewNode::website)
|
||||||
mockkStatic(HtmlInfo::isInputField)
|
mockkStatic(HtmlInfo::isInputField)
|
||||||
mockkStatic(HtmlInfo::isPasswordField)
|
mockkStatic(HtmlInfo::isPasswordField)
|
||||||
mockkStatic(Int::isPasswordInputType)
|
mockkStatic(Int::isPasswordInputType)
|
||||||
@ -72,6 +75,7 @@ class ViewNodeExtensionsTest {
|
|||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
fun teardown() {
|
fun teardown() {
|
||||||
|
unmockkStatic(AssistStructure.ViewNode::website)
|
||||||
unmockkStatic(HtmlInfo::isInputField)
|
unmockkStatic(HtmlInfo::isInputField)
|
||||||
unmockkStatic(HtmlInfo::isPasswordField)
|
unmockkStatic(HtmlInfo::isPasswordField)
|
||||||
unmockkStatic(Int::isPasswordInputType)
|
unmockkStatic(Int::isPasswordInputType)
|
||||||
@ -93,7 +97,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -121,7 +125,7 @@ class ViewNodeExtensionsTest {
|
|||||||
} returns monthValue
|
} returns monthValue
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -137,7 +141,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_EXP_MONTH_HINTS
|
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_EXP_MONTH_HINTS
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -152,7 +156,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARD_EXP_MONTH_HINTS.forEach { idEntry ->
|
SUPPORTED_RAW_CARD_EXP_MONTH_HINTS.forEach { idEntry ->
|
||||||
every { viewNode.idEntry } returns idEntry
|
every { viewNode.idEntry } returns idEntry
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for idEntry: $idEntry")
|
assertEquals(expected, actual, "Failed for idEntry: $idEntry")
|
||||||
}
|
}
|
||||||
@ -167,7 +171,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
SUPPORTED_RAW_CARD_EXP_MONTH_HINTS.forEach { hint ->
|
SUPPORTED_RAW_CARD_EXP_MONTH_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,7 +186,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -197,7 +201,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARD_EXP_YEAR_HINTS.forEach { hint ->
|
SUPPORTED_RAW_CARD_EXP_YEAR_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
@ -213,7 +217,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_EXP_YEAR_HINTS
|
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_EXP_YEAR_HINTS
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -227,7 +231,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
||||||
every { mockHtmlInfo.isInputField } returns true
|
every { mockHtmlInfo.isInputField } returns true
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -241,7 +245,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARD_EXP_DATE_HINTS.forEach { hint ->
|
SUPPORTED_RAW_CARD_EXP_DATE_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
@ -256,7 +260,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_EXP_DATE_HINTS
|
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_EXP_DATE_HINTS
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -271,7 +275,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -286,7 +290,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARD_NUMBER_HINTS.forEach { hint ->
|
SUPPORTED_RAW_CARD_NUMBER_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
@ -300,7 +304,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_NUMBER_HINTS
|
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_NUMBER_HINTS
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -315,7 +319,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -330,7 +334,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARD_SECURITY_CODE_HINTS.forEach { hint ->
|
SUPPORTED_RAW_CARD_SECURITY_CODE_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
@ -344,7 +348,7 @@ class ViewNodeExtensionsTest {
|
|||||||
data = autofillViewData,
|
data = autofillViewData,
|
||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_SECURITY_CODE_HINTS
|
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARD_SECURITY_CODE_HINTS
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,7 +361,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARDHOLDER_NAME_HINTS.forEach { idEntry ->
|
SUPPORTED_RAW_CARDHOLDER_NAME_HINTS.forEach { idEntry ->
|
||||||
every { viewNode.idEntry } returns idEntry
|
every { viewNode.idEntry } returns idEntry
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for idEntry: $idEntry")
|
assertEquals(expected, actual, "Failed for idEntry: $idEntry")
|
||||||
}
|
}
|
||||||
@ -372,7 +376,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_CARDHOLDER_NAME_HINTS.forEach { hint ->
|
SUPPORTED_RAW_CARDHOLDER_NAME_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
@ -386,7 +390,7 @@ class ViewNodeExtensionsTest {
|
|||||||
data = autofillViewData,
|
data = autofillViewData,
|
||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARDHOLDER_NAME_HINTS
|
every { viewNode.htmlInfo.hints() } returns SUPPORTED_RAW_CARDHOLDER_NAME_HINTS
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,7 +403,7 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
every { viewNode.autofillHints } returns arrayOf(autofillHint)
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -413,7 +417,7 @@ class ViewNodeExtensionsTest {
|
|||||||
SUPPORTED_RAW_PASSWORD_HINTS.forEach { hint ->
|
SUPPORTED_RAW_PASSWORD_HINTS.forEach { hint ->
|
||||||
every { viewNode.hint } returns hint
|
every { viewNode.hint } returns hint
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual, "Failed for hint: $hint")
|
assertEquals(expected, actual, "Failed for hint: $hint")
|
||||||
}
|
}
|
||||||
@ -428,11 +432,50 @@ class ViewNodeExtensionsTest {
|
|||||||
)
|
)
|
||||||
every { viewNode.htmlInfo.isPasswordField() } returns true
|
every { viewNode.htmlInfo.isPasswordField() } returns true
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toAutofillView should return AutofillView Login Username with internal website`() {
|
||||||
|
// Setup
|
||||||
|
val website = "website"
|
||||||
|
val expected = AutofillView.Login.Username(
|
||||||
|
data = autofillViewData.copy(website = website),
|
||||||
|
)
|
||||||
|
setupUnsupportedInputFieldViewNode()
|
||||||
|
every { viewNode.website } returns website
|
||||||
|
every { viewNode.className } returns "android.widget.EditText"
|
||||||
|
every { any<Int>().isPasswordInputType } returns false
|
||||||
|
every { any<Int>().isUsernameInputType } returns true
|
||||||
|
|
||||||
|
// Test
|
||||||
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `toAutofillView should return AutofillView Login Username with external website`() {
|
||||||
|
// Setup
|
||||||
|
val website = "website"
|
||||||
|
val expected = AutofillView.Login.Username(
|
||||||
|
data = autofillViewData.copy(website = website),
|
||||||
|
)
|
||||||
|
setupUnsupportedInputFieldViewNode()
|
||||||
|
every { viewNode.className } returns "android.widget.EditText"
|
||||||
|
every { any<Int>().isPasswordInputType } returns false
|
||||||
|
every { any<Int>().isUsernameInputType } returns true
|
||||||
|
|
||||||
|
// Test
|
||||||
|
val actual = viewNode.toAutofillView(parentWebsite = website)
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MaxLineLength")
|
@Suppress("MaxLineLength")
|
||||||
@Test
|
@Test
|
||||||
fun `toAutofillView should return AutofillView Login Username when is EditText and isUsernameField`() {
|
fun `toAutofillView should return AutofillView Login Username when is EditText and isUsernameField`() {
|
||||||
@ -446,7 +489,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { any<Int>().isUsernameInputType } returns true
|
every { any<Int>().isUsernameInputType } returns true
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -465,7 +508,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { any<Int>().isUsernameInputType } returns true
|
every { any<Int>().isUsernameInputType } returns true
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -480,7 +523,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { viewNode.htmlInfo.isInputField } returns false
|
every { viewNode.htmlInfo.isInputField } returns false
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertNull(actual)
|
assertNull(actual)
|
||||||
@ -494,7 +537,7 @@ class ViewNodeExtensionsTest {
|
|||||||
data = autofillViewData,
|
data = autofillViewData,
|
||||||
)
|
)
|
||||||
|
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@ -511,7 +554,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { viewNode.autofillHints } returns arrayOf(autofillHintOne, autofillHintTwo)
|
every { viewNode.autofillHints } returns arrayOf(autofillHintOne, autofillHintTwo)
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
val actual = viewNode.toAutofillView()
|
val actual = viewNode.toAutofillView(parentWebsite = null)
|
||||||
|
|
||||||
// Verify
|
// Verify
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
@ -1207,6 +1250,7 @@ class ViewNodeExtensionsTest {
|
|||||||
every { any<Int>().isPasswordInputType } returns false
|
every { any<Int>().isPasswordInputType } returns false
|
||||||
every { any<Int>().isUsernameInputType } returns false
|
every { any<Int>().isUsernameInputType } returns false
|
||||||
every { viewNode.htmlInfo.hints() } returns emptyList()
|
every { viewNode.htmlInfo.hints() } returns emptyList()
|
||||||
|
every { viewNode.website } returns null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData
|
|||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.Assertions.assertNull
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class ViewNodeTraversalDataExtensionsTest {
|
class ViewNodeTraversalDataExtensionsTest {
|
||||||
@ -15,64 +14,6 @@ class ViewNodeTraversalDataExtensionsTest {
|
|||||||
every { this@mockk.getWindowNodeAt(0) } returns windowNode
|
every { this@mockk.getWindowNodeAt(0) } returns windowNode
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `buildUriOrNull should return website URI when present`() {
|
|
||||||
// Setup
|
|
||||||
val viewNodeTraversalData = ViewNodeTraversalData(
|
|
||||||
autofillViews = emptyList(),
|
|
||||||
idPackage = null,
|
|
||||||
ignoreAutofillIds = emptyList(),
|
|
||||||
website = WEBSITE,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test
|
|
||||||
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
|
|
||||||
packageName = PACKAGE_NAME,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Verify
|
|
||||||
assertEquals(WEBSITE, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `buildUriOrNull should return package name URI when website is null`() {
|
|
||||||
// Setup
|
|
||||||
val viewNodeTraversalData = ViewNodeTraversalData(
|
|
||||||
autofillViews = emptyList(),
|
|
||||||
idPackage = null,
|
|
||||||
ignoreAutofillIds = emptyList(),
|
|
||||||
website = null,
|
|
||||||
)
|
|
||||||
val expected = "androidapp://$PACKAGE_NAME"
|
|
||||||
|
|
||||||
// Test
|
|
||||||
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
|
|
||||||
packageName = PACKAGE_NAME,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Verify
|
|
||||||
assertEquals(expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `buildUriOrNull should return null when website and packageName are null`() {
|
|
||||||
// Setup
|
|
||||||
val viewNodeTraversalData = ViewNodeTraversalData(
|
|
||||||
autofillViews = emptyList(),
|
|
||||||
idPackage = null,
|
|
||||||
ignoreAutofillIds = emptyList(),
|
|
||||||
website = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test
|
|
||||||
val actual = listOf(viewNodeTraversalData).buildUriOrNull(
|
|
||||||
packageName = null,
|
|
||||||
)
|
|
||||||
|
|
||||||
// Verify
|
|
||||||
assertNull(actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `buildPackageNameOrNull should return idPackage when available`() {
|
fun `buildPackageNameOrNull should return idPackage when available`() {
|
||||||
// Setup
|
// Setup
|
||||||
@ -80,7 +21,6 @@ class ViewNodeTraversalDataExtensionsTest {
|
|||||||
autofillViews = emptyList(),
|
autofillViews = emptyList(),
|
||||||
idPackage = ID_PACKAGE,
|
idPackage = ID_PACKAGE,
|
||||||
ignoreAutofillIds = emptyList(),
|
ignoreAutofillIds = emptyList(),
|
||||||
website = null,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
@ -99,7 +39,6 @@ class ViewNodeTraversalDataExtensionsTest {
|
|||||||
autofillViews = emptyList(),
|
autofillViews = emptyList(),
|
||||||
idPackage = null,
|
idPackage = null,
|
||||||
ignoreAutofillIds = emptyList(),
|
ignoreAutofillIds = emptyList(),
|
||||||
website = null,
|
|
||||||
)
|
)
|
||||||
val expected = "com.x8bit.bitwarden"
|
val expected = "com.x8bit.bitwarden"
|
||||||
every { windowNode.title } returns "com.x8bit.bitwarden/path.deeper.into.app"
|
every { windowNode.title } returns "com.x8bit.bitwarden/path.deeper.into.app"
|
||||||
@ -115,5 +54,3 @@ class ViewNodeTraversalDataExtensionsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val ID_PACKAGE: String = "com.x8bit.bitwarden"
|
private const val ID_PACKAGE: String = "com.x8bit.bitwarden"
|
||||||
private const val PACKAGE_NAME: String = "com.google"
|
|
||||||
private const val WEBSITE: String = "https://www.google.com"
|
|
||||||
|
|||||||
@ -12,4 +12,5 @@ fun createMockPasswordCredentialAutofillCipherLogin() = AutofillCipher.Login(
|
|||||||
password = "mock-password",
|
password = "mock-password",
|
||||||
username = "mock-username",
|
username = "mock-username",
|
||||||
subtitle = "Subtitle",
|
subtitle = "Subtitle",
|
||||||
|
website = "website",
|
||||||
)
|
)
|
||||||
|
|||||||
@ -87,6 +87,7 @@ class AutofillUtilsTest {
|
|||||||
password = "password",
|
password = "password",
|
||||||
username = "username",
|
username = "username",
|
||||||
subtitle = "Subtitle",
|
subtitle = "Subtitle",
|
||||||
|
website = "website",
|
||||||
),
|
),
|
||||||
second = AutofillAppInfo(
|
second = AutofillAppInfo(
|
||||||
context = context,
|
context = context,
|
||||||
@ -103,6 +104,7 @@ class AutofillUtilsTest {
|
|||||||
password = "password",
|
password = "password",
|
||||||
username = "username",
|
username = "username",
|
||||||
subtitle = "AmazonSubtitle",
|
subtitle = "AmazonSubtitle",
|
||||||
|
website = "website",
|
||||||
),
|
),
|
||||||
second = AutofillAppInfo(
|
second = AutofillAppInfo(
|
||||||
context = context,
|
context = context,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user