diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/AutofillView.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/AutofillView.kt index 933215716b..68067a4c94 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/AutofillView.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/AutofillView.kt @@ -11,17 +11,11 @@ sealed class AutofillView { * The data important to a given [AutofillView]. * * @param autofillId The [AutofillId] associated with this view. - * @param idPackage The package id for this view, if there is one. * @param isFocused Whether the view is currently focused. - * @param webDomain The web domain for this view, if there is one. (example: m.facebook.com) - * @param webScheme The web scheme for this view, if there is one. (example: https) */ data class Data( val autofillId: AutofillId, - val idPackage: String?, val isFocused: Boolean, - val webDomain: String?, - val webScheme: String?, ) /** diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/ViewNodeTraversalData.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/ViewNodeTraversalData.kt index b76aeca544..7b52316f90 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/ViewNodeTraversalData.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/model/ViewNodeTraversalData.kt @@ -4,8 +4,15 @@ import android.view.autofill.AutofillId /** * A convenience data structure for view node traversal. + * + * @param autofillViews The list of views we care about for autofilling. + * @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 website The website that is being displayed in the app, given there is one. */ data class ViewNodeTraversalData( val autofillViews: List, + val idPackage: String?, val ignoreAutofillIds: List, + val website: String?, ) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt index 5bfff6c152..89220b6441 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserImpl.kt @@ -12,6 +12,7 @@ import com.x8bit.bitwarden.data.autofill.util.buildUriOrNull import com.x8bit.bitwarden.data.autofill.util.getInlinePresentationSpecs import com.x8bit.bitwarden.data.autofill.util.getMaxInlineSuggestionsCount import com.x8bit.bitwarden.data.autofill.util.toAutofillView +import com.x8bit.bitwarden.data.autofill.util.website import com.x8bit.bitwarden.data.platform.repository.SettingsRepository /** @@ -119,6 +120,8 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData { // Set up mutable lists for collecting valid AutofillViews and ignorable view ids. val mutableAutofillViewList: MutableList = mutableListOf() val mutableIgnoreAutofillIdList: MutableList = mutableListOf() + var idPackage: String? = this.idPackage + var website: String? = this.website // 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`. @@ -134,6 +137,15 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData { .let { viewNodeTraversalData -> viewNodeTraversalData.autofillViews.forEach(mutableAutofillViewList::add) viewNodeTraversalData.ignoreAutofillIds.forEach(mutableIgnoreAutofillIdList::add) + + // Get the first non-null idPackage. + if (idPackage.isNullOrBlank()) { + idPackage = viewNodeTraversalData.idPackage + } + // Get the first non-null website. + if (website == null) { + website = viewNodeTraversalData.website + } } } @@ -141,6 +153,8 @@ private fun AssistStructure.ViewNode.traverse(): ViewNodeTraversalData { // descendant's. return ViewNodeTraversalData( autofillViews = mutableAutofillViewList, + idPackage = idPackage, ignoreAutofillIds = mutableIgnoreAutofillIdList, + website = website, ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensions.kt index b049b7ac9c..552e1604ba 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensions.kt @@ -3,12 +3,18 @@ package com.x8bit.bitwarden.data.autofill.util import android.app.assist.AssistStructure import android.view.View import com.x8bit.bitwarden.data.autofill.model.AutofillView +import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank /** * The class name of the android edit text field. */ private const val ANDROID_EDIT_TEXT_CLASS_NAME: String = "android.widget.EditText" +/** + * The default web URI scheme. + */ +private const val DEFAULT_SCHEME: String = "https" + /** * The set of raw autofill hints that should be ignored. */ @@ -72,10 +78,7 @@ fun AssistStructure.ViewNode.toAutofillView(): AutofillView? = if (supportedHint != null || this.isInputField) { val autofillViewData = AutofillView.Data( autofillId = nonNullAutofillId, - idPackage = idPackage, isFocused = isFocused, - webDomain = webDomain, - webScheme = webScheme, ) buildAutofillView( autofillViewData = autofillViewData, @@ -164,3 +167,22 @@ fun AssistStructure.ViewNode.isUsernameField( inputType.isUsernameInputType || idEntry?.containsAnyTerms(SUPPORTED_RAW_USERNAME_HINTS) == true || hint?.containsAnyTerms(SUPPORTED_RAW_USERNAME_HINTS) == true + +/** + * The website that this [AssistStructure.ViewNode] is a part of representing. + */ +val AssistStructure.ViewNode.website: String? + get() = this + .webDomain + .takeUnless { it?.isBlank() == true } + ?.let { webDomain -> + val webScheme = this + .webScheme + .orNullIfBlank() + ?: DEFAULT_SCHEME + + buildUri( + domain = webDomain, + scheme = webScheme, + ) + } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensions.kt b/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensions.kt index 5f02ee8c45..edd4f6779e 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensions.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensions.kt @@ -9,11 +9,6 @@ import com.x8bit.bitwarden.ui.platform.base.util.orNullIfBlank */ private const val ANDROID_APP_SCHEME: String = "androidapp" -/** - * The default web URI scheme. - */ -private const val DEFAULT_SCHEME: String = "https" - /** * Try and build a URI. The try progression looks like this: * 1. Try searching traversal data for website URIs. @@ -25,13 +20,17 @@ fun List.buildUriOrNull( assistStructure: AssistStructure, ): String? { // Search list of [ViewNodeTraversalData] for a website URI. - buildWebsiteUriOrNull() + this + .firstOrNull { it.website != null } + ?.website ?.let { websiteUri -> return websiteUri } // Search list of [ViewNodeTraversalData] for a valid package name. - buildPackageNameOrNull() + this + .firstOrNull { it.idPackage != null } + ?.idPackage ?.let { packageName -> return buildUri( domain = packageName, @@ -53,7 +52,7 @@ fun List.buildUriOrNull( /** * Combine [domain] and [scheme] into a URI. */ -private fun buildUri( +fun buildUri( domain: String, scheme: String, ): String = "$scheme://$domain" @@ -74,29 +73,3 @@ private fun AssistStructure.buildPackageNameOrNull(): String? = if (windowNodeCo } else { null } - -/** - * Search each [ViewNodeTraversalData.autofillViews] list for a valid package id. If one is found - * return it and terminate the search. - */ -private fun List.buildPackageNameOrNull(): String? = - flatMap { it.autofillViews } - .firstOrNull { !it.data.idPackage.isNullOrEmpty() } - ?.data - ?.idPackage - -/** - * Search each [ViewNodeTraversalData.autofillViews] list for a valid web domain. If one is found, - * combine it with its scheme and return it. - */ -private fun List.buildWebsiteUriOrNull(): String? = - flatMap { it.autofillViews } - .firstOrNull { !it.data.webDomain.isNullOrEmpty() } - ?.let { autofillView -> - val webDomain = requireNotNull(autofillView.data.webDomain) - val webScheme = autofillView.data.webScheme.orNullIfBlank() ?: DEFAULT_SCHEME - buildUri( - domain = webDomain, - scheme = webScheme, - ) - } diff --git a/app/src/main/res/xml-v30/autofill_service_configuration.xml b/app/src/main/res/xml-v30/autofill_service_configuration.xml index 55cfd6821e..0ef0f187c5 100644 --- a/app/src/main/res/xml-v30/autofill_service_configuration.xml +++ b/app/src/main/res/xml-v30/autofill_service_configuration.xml @@ -1,3 +1,278 @@ + android:supportsInlineSuggestions="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/autofill_service_configuration.xml b/app/src/main/res/xml/autofill_service_configuration.xml index f9c2255297..db7e58ea0a 100644 --- a/app/src/main/res/xml/autofill_service_configuration.xml +++ b/app/src/main/res/xml/autofill_service_configuration.xml @@ -1,2 +1,277 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt index a2132190c6..cb33ff16d1 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FillResponseBuilderTest.kt @@ -111,10 +111,7 @@ class FillResponseBuilderTest { AutofillView.Login.Username( data = AutofillView.Data( autofillId = mockk(), - idPackage = null, isFocused = true, - webDomain = null, - webScheme = null, ), ), ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FilledDataBuilderTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FilledDataBuilderTest.kt index dd51c51f85..42e7bd855c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FilledDataBuilderTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/builder/FilledDataBuilderTest.kt @@ -35,10 +35,7 @@ class FilledDataBuilderTest { private val autofillId: AutofillId = mockk() private val autofillViewData = AutofillView.Data( autofillId = autofillId, - idPackage = null, isFocused = false, - webDomain = null, - webScheme = null, ) @BeforeEach diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt index f40ad2b36e..7a5bdeb071 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/parser/AutofillParserTests.kt @@ -15,6 +15,7 @@ import com.x8bit.bitwarden.data.autofill.util.buildUriOrNull import com.x8bit.bitwarden.data.autofill.util.getInlinePresentationSpecs import com.x8bit.bitwarden.data.autofill.util.getMaxInlineSuggestionsCount import com.x8bit.bitwarden.data.autofill.util.toAutofillView +import com.x8bit.bitwarden.data.autofill.util.website import com.x8bit.bitwarden.data.platform.repository.SettingsRepository import io.mockk.every import io.mockk.mockk @@ -30,13 +31,6 @@ class AutofillParserTests { private lateinit var parser: AutofillParser private val autofillAppInfo: AutofillAppInfo = mockk() - private val autofillViewData = AutofillView.Data( - autofillId = mockk(), - isFocused = true, - idPackage = null, - webDomain = null, - webScheme = null, - ) private val assistStructure: AssistStructure = mockk() private val cardAutofillHint = View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR private val cardAutofillId: AutofillId = mockk() @@ -44,6 +38,7 @@ class AutofillParserTests { every { this@mockk.autofillHints } returns arrayOf(cardAutofillHint) every { this@mockk.autofillId } returns cardAutofillId every { this@mockk.childCount } returns 0 + every { this@mockk.idPackage } returns ID_PACKAGE } private val loginAutofillHint = View.AUTOFILL_HINT_USERNAME private val loginAutofillId: AutofillId = mockk() @@ -51,6 +46,7 @@ class AutofillParserTests { every { this@mockk.autofillHints } returns arrayOf(loginAutofillHint) every { this@mockk.autofillId } returns loginAutofillId every { this@mockk.childCount } returns 0 + every { this@mockk.idPackage } returns ID_PACKAGE } private val cardWindowNode: AssistStructure.WindowNode = mockk { every { this@mockk.rootViewNode } returns cardViewNode @@ -74,11 +70,14 @@ class AutofillParserTests { @BeforeEach fun setup() { mockkStatic(AssistStructure.ViewNode::toAutofillView) + mockkStatic(AssistStructure.ViewNode::website) mockkStatic( FillRequest::getMaxInlineSuggestionsCount, FillRequest::getInlinePresentationSpecs, ) mockkStatic(List::buildUriOrNull) + every { cardViewNode.website } returns WEBSITE + every { loginViewNode.website } returns WEBSITE every { fillRequest.getInlinePresentationSpecs( autofillAppInfo = autofillAppInfo, @@ -112,6 +111,7 @@ class AutofillParserTests { @AfterEach fun teardown() { unmockkStatic(AssistStructure.ViewNode::toAutofillView) + unmockkStatic(AssistStructure.ViewNode::website) unmockkStatic( FillRequest::getMaxInlineSuggestionsCount, FillRequest::getInlinePresentationSpecs, @@ -160,13 +160,15 @@ class AutofillParserTests { every { this@mockk.autofillHints } returns arrayOf(childAutofillHint) every { this@mockk.autofillId } returns childAutofillId every { this@mockk.childCount } returns 0 + every { this@mockk.idPackage } returns null every { this@mockk.isFocused } returns false every { this@mockk.toAutofillView() } returns null + every { this@mockk.website } returns null } val parentAutofillHint = View.AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR val parentAutofillId: AutofillId = mockk() val parentAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = parentAutofillId, isFocused = true, ), @@ -174,9 +176,11 @@ class AutofillParserTests { val parentViewNode: AssistStructure.ViewNode = mockk { every { this@mockk.autofillHints } returns arrayOf(parentAutofillHint) every { this@mockk.autofillId } returns parentAutofillId + every { this@mockk.idPackage } returns null every { this@mockk.toAutofillView() } returns parentAutofillView every { this@mockk.childCount } returns 1 every { this@mockk.getChildAt(0) } returns childViewNode + every { this@mockk.website } returns null } val windowNode: AssistStructure.WindowNode = mockk { every { this@mockk.rootViewNode } returns parentViewNode @@ -220,13 +224,13 @@ class AutofillParserTests { // Setup setupAssistStructureWithAllAutofillViewTypes() val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = cardAutofillId, isFocused = true, ), ) val loginAutofillView: AutofillView.Login = AutofillView.Login.Username( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = loginAutofillId, isFocused = false, ), @@ -270,13 +274,13 @@ class AutofillParserTests { // Setup setupAssistStructureWithAllAutofillViewTypes() val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = cardAutofillId, isFocused = false, ), ) val loginAutofillView: AutofillView.Login = AutofillView.Login.Username( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = loginAutofillId, isFocused = true, ), @@ -320,13 +324,13 @@ class AutofillParserTests { // Setup setupAssistStructureWithAllAutofillViewTypes() val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = cardAutofillId, isFocused = true, ), ) val loginAutofillView: AutofillView.Login = AutofillView.Login.Username( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = loginAutofillId, isFocused = true, ), @@ -371,13 +375,13 @@ class AutofillParserTests { mockIsInlineAutofillEnabled = false setupAssistStructureWithAllAutofillViewTypes() val cardAutofillView: AutofillView.Card = AutofillView.Card.ExpirationMonth( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = cardAutofillId, isFocused = true, ), ) val loginAutofillView: AutofillView.Login = AutofillView.Login.Username( - data = autofillViewData.copy( + data = AutofillView.Data( autofillId = loginAutofillId, isFocused = true, ), @@ -427,5 +431,7 @@ class AutofillParserTests { } } +private const val ID_PACKAGE: String = "com.x8bit.bitwarden" private const val MAX_INLINE_SUGGESTION_COUNT: Int = 42 private const val URI: String = "androidapp://com.x8bit.bitwarden" +private const val WEBSITE: String = "https://www.google.com" diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/AutofillViewExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/AutofillViewExtensionsTest.kt index b0bd075834..abc50be58c 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/AutofillViewExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/AutofillViewExtensionsTest.kt @@ -19,10 +19,7 @@ class AutofillViewExtensionsTest { private val autofillValue: AutofillValue = mockk() private val autofillViewData = AutofillView.Data( autofillId = autofillId, - idPackage = null, isFocused = false, - webDomain = null, - webScheme = null, ) @BeforeEach diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt index 0a369ab88a..21c82b30cc 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/FilledDataExtensionsTest.kt @@ -69,10 +69,7 @@ class FilledDataExtensionsTest { AutofillView.Login.Username( data = AutofillView.Data( autofillId = autofillId, - idPackage = null, isFocused = true, - webDomain = null, - webScheme = null, ), ), ), diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensionsTest.kt index 1e54d3f921..709e508955 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeExtensionsTest.kt @@ -22,20 +22,14 @@ class ViewNodeExtensionsTest { private val expectedIsFocused = true private val autofillViewData = AutofillView.Data( autofillId = expectedAutofillId, - idPackage = ID_PACKAGE, isFocused = expectedIsFocused, - webDomain = WEB_DOMAIN, - webScheme = WEB_SCHEME, ) private val viewNode: AssistStructure.ViewNode = mockk { every { this@mockk.autofillId } returns expectedAutofillId every { this@mockk.childCount } returns 0 every { this@mockk.inputType } returns 1 - every { this@mockk.idPackage } returns ID_PACKAGE every { this@mockk.isFocused } returns expectedIsFocused - every { this@mockk.webDomain } returns WEB_DOMAIN - every { this@mockk.webScheme } returns WEB_SCHEME } @BeforeEach @@ -375,6 +369,80 @@ class ViewNodeExtensionsTest { } } + @Test + fun `website should return URI if domain and scheme are valid`() { + // Setup + val webDomain = "www.google.com" + val webScheme = "http" + val expected = "http://www.google.com" + every { viewNode.webDomain } returns webDomain + every { viewNode.webScheme } returns webScheme + + // Test + val actual = viewNode.website + + // Verify + assertEquals(expected, actual) + } + + @Test + fun `website should return URI with default scheme if domain is valid and scheme is null`() { + // Setup + val webDomain = "www.google.com" + val webScheme = null + val expected = "https://www.google.com" + every { viewNode.webDomain } returns webDomain + every { viewNode.webScheme } returns webScheme + + // Test + val actual = viewNode.website + + // Verify + assertEquals(expected, actual) + } + + @Test + fun `website should return URI with default scheme if domain is valid and scheme is blank`() { + // Setup + val webDomain = "www.google.com" + val webScheme = " " + val expected = "https://www.google.com" + every { viewNode.webDomain } returns webDomain + every { viewNode.webScheme } returns webScheme + + // Test + val actual = viewNode.website + + // Verify + assertEquals(expected, actual) + } + + @Test + fun `website should return null when domain is null`() { + // Setup + val webDomain = null + every { viewNode.webDomain } returns webDomain + + // Test + val actual = viewNode.website + + // Verify + assertNull(actual) + } + + @Test + fun `website should return null when domain is blank`() { + // Setup + val webDomain = " " + every { viewNode.webDomain } returns webDomain + + // Test + val actual = viewNode.website + + // Verify + assertNull(actual) + } + /** * Set up [viewNode] to be an input field but not supported. */ @@ -391,9 +459,6 @@ class ViewNodeExtensionsTest { } private const val ANDROID_EDIT_TEXT_CLASS_NAME: String = "android.widget.EditText" -private const val ID_PACKAGE: String = "ID_PACKAGE" -private const val WEB_DOMAIN: String = "WEB_DOMAIN" -private const val WEB_SCHEME: String = "WEB_SCHEME" private val IGNORED_RAW_HINTS: List = listOf( "search", "find", diff --git a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensionsTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensionsTest.kt index 4449667554..99e7c1b1ea 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensionsTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/autofill/util/ViewNodeTraversalDataExtensionsTest.kt @@ -6,7 +6,6 @@ import com.x8bit.bitwarden.data.autofill.model.ViewNodeTraversalData import io.mockk.every import io.mockk.mockk import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertNull import org.junit.jupiter.api.Test class ViewNodeTraversalDataExtensionsTest { @@ -17,28 +16,18 @@ class ViewNodeTraversalDataExtensionsTest { } private val autofillViewData = AutofillView.Data( autofillId = mockk(), - idPackage = null, isFocused = false, - webDomain = null, - webScheme = null, ) @Test - fun `buildUriOrNull should return URI when contains valid domain and scheme`() { + fun `buildUriOrNull should return website URI when present`() { // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - webDomain = WEB_DOMAIN, - webScheme = WEB_SCHEME, - ), - ) val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), + autofillViews = emptyList(), + idPackage = null, ignoreAutofillIds = emptyList(), + website = WEBSITE, ) - val expected = "$WEB_SCHEME://$WEB_DOMAIN" // Test val actual = listOf(viewNodeTraversalData).buildUriOrNull( @@ -46,77 +35,17 @@ class ViewNodeTraversalDataExtensionsTest { ) // Verify - assertEquals(expected, actual) + assertEquals(WEBSITE, actual) } @Test - fun `buildUriOrNull should return URI with default scheme when domain valid and scheme null`() { + fun `buildUriOrNull should return idPackage URI when WEBSITE is null`() { // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - webDomain = WEB_DOMAIN, - webScheme = null, - ), - ) val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), - ignoreAutofillIds = emptyList(), - ) - val expected = "https://$WEB_DOMAIN" - - // Test - val actual = listOf(viewNodeTraversalData).buildUriOrNull( - assistStructure = assistStructure, - ) - - // Verify - assertEquals(expected, actual) - } - - @Suppress("MaxLineLength") - @Test - fun `buildUriOrNull should return URI with default scheme when domain valid and scheme empty`() { - // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - webDomain = WEB_DOMAIN, - webScheme = "", - ), - ) - val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), - ignoreAutofillIds = emptyList(), - ) - val expected = "https://$WEB_DOMAIN" - - // Test - val actual = listOf(viewNodeTraversalData).buildUriOrNull( - assistStructure = assistStructure, - ) - - // Verify - assertEquals(expected, actual) - } - - @Test - fun `buildUriOrNull should return idPackage URI when domain is null`() { - // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - idPackage = ID_PACKAGE, - webDomain = null, - webScheme = null, - ), - ) - val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), + autofillViews = emptyList(), + idPackage = ID_PACKAGE, ignoreAutofillIds = emptyList(), + website = null, ) val expected = "androidapp://$ID_PACKAGE" @@ -130,47 +59,13 @@ class ViewNodeTraversalDataExtensionsTest { } @Test - fun `buildUriOrNull should return idPackage URI when domain is empty`() { + fun `buildUriOrNull should return title URI when website and idPackage are null`() { // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - idPackage = ID_PACKAGE, - webDomain = "", - webScheme = "", - ), - ) val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), - ignoreAutofillIds = emptyList(), - ) - val expected = "androidapp://$ID_PACKAGE" - - // Test - val actual = listOf(viewNodeTraversalData).buildUriOrNull( - assistStructure = assistStructure, - ) - - // Verify - assertEquals(expected, actual) - } - - @Test - fun `buildUriOrNull should return title URI when domain and idPackage are null`() { - // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - idPackage = null, - webDomain = null, - webScheme = null, - ), - ) - val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), + autofillViews = emptyList(), + idPackage = null, ignoreAutofillIds = emptyList(), + website = null, ) val expected = "androidapp://com.x8bit.bitwarden" every { windowNode.title } returns "com.x8bit.bitwarden/path.deeper.into.app" @@ -184,91 +79,8 @@ class ViewNodeTraversalDataExtensionsTest { assertEquals(expected, actual) } - @Test - fun `buildUriOrNull should return title URI when domain and idPackage are empty`() { - // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - idPackage = "", - webDomain = "", - webScheme = null, - ), - ) - val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), - ignoreAutofillIds = emptyList(), - ) - val expected = "androidapp://com.x8bit.bitwarden" - every { windowNode.title } returns "com.x8bit.bitwarden/path.deeper.into.app" - - // Test - val actual = listOf(viewNodeTraversalData).buildUriOrNull( - assistStructure = assistStructure, - ) - - // Verify - assertEquals(expected, actual) - } - - @Test - fun `buildUriOrNull should return null when title, domain, and idPackage are null`() { - // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - idPackage = null, - webDomain = null, - webScheme = null, - ), - ) - val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), - ignoreAutofillIds = emptyList(), - ) - every { windowNode.title } returns null - - // Test - val actual = listOf(viewNodeTraversalData).buildUriOrNull( - assistStructure = assistStructure, - ) - - // Verify - assertNull(actual) - } - - @Test - fun `buildUriOrNull should return null when title, domain, and idPackage are empty`() { - // Setup - val autofillView = AutofillView.Card.Number( - data = autofillViewData.copy( - idPackage = "", - webDomain = "", - webScheme = null, - ), - ) - val viewNodeTraversalData = ViewNodeTraversalData( - autofillViews = listOf( - autofillView, - ), - ignoreAutofillIds = emptyList(), - ) - every { windowNode.title } returns "" - - // Test - val actual = listOf(viewNodeTraversalData).buildUriOrNull( - assistStructure = assistStructure, - ) - - // Verify - assertNull(actual) - } - companion object { private const val ID_PACKAGE: String = "com.x8bit.bitwarden" - private const val WEB_DOMAIN: String = "www.google.com" - private const val WEB_SCHEME: String = "https" + private const val WEBSITE: String = "https://www.google.com" } }