mirror of
https://github.com/bitwarden/android.git
synced 2025-02-17 12:30:00 +03:00
BIT-1315: Add dummy data fulfillment (#602)
This commit is contained in:
parent
5d73f97831
commit
67d7b7a9f5
16 changed files with 522 additions and 50 deletions
|
@ -1,37 +1,52 @@
|
|||
package com.x8bit.bitwarden.data.autofill.builder
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillPartition
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillRequest
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledData
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledPartition
|
||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
|
||||
import com.x8bit.bitwarden.data.autofill.util.buildFilledItemOrNull
|
||||
|
||||
/**
|
||||
* The default [FilledDataBuilder]. This converts parsed autofill data into filled data that is
|
||||
* ready to be loaded into an autofill response.
|
||||
*/
|
||||
class FilledDataBuilderImpl : FilledDataBuilder {
|
||||
class FilledDataBuilderImpl(
|
||||
private val autofillCipherProvider: AutofillCipherProvider,
|
||||
) : FilledDataBuilder {
|
||||
override suspend fun build(autofillRequest: AutofillRequest.Fillable): FilledData {
|
||||
// TODO: determine whether or not the vault is locked (BIT-1296)
|
||||
|
||||
val filledPartitions = when (autofillRequest.partition) {
|
||||
is AutofillPartition.Card -> {
|
||||
// TODO: perform fulfillment with dummy data (BIT-1315)
|
||||
listOf(
|
||||
fillCardPartition(
|
||||
autofillViews = autofillRequest.partition.views,
|
||||
),
|
||||
)
|
||||
autofillCipherProvider
|
||||
.getCardAutofillCiphers()
|
||||
.map { autofillCipher ->
|
||||
fillCardPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
autofillViews = autofillRequest.partition.views,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
is AutofillPartition.Login -> {
|
||||
// TODO: perform fulfillment with dummy data (BIT-1315)
|
||||
listOf(
|
||||
fillLoginPartition(
|
||||
autofillViews = autofillRequest.partition.views,
|
||||
),
|
||||
)
|
||||
autofillRequest
|
||||
.uri
|
||||
?.let { nonNullUri ->
|
||||
autofillCipherProvider
|
||||
.getLoginAutofillCiphers(
|
||||
uri = nonNullUri,
|
||||
)
|
||||
.map { autofillCipher ->
|
||||
fillLoginPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
autofillViews = autofillRequest.partition.views,
|
||||
)
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,37 +57,56 @@ class FilledDataBuilderImpl : FilledDataBuilder {
|
|||
}
|
||||
|
||||
/**
|
||||
* Construct a [FilledPartition] by fulfilling the card [autofillViews] with data.
|
||||
* Construct a [FilledPartition] by fulfilling the card [autofillViews] with data from the
|
||||
* card [autofillCipher].
|
||||
*/
|
||||
private fun fillCardPartition(
|
||||
autofillCipher: AutofillCipher.Card,
|
||||
autofillViews: List<AutofillView.Card>,
|
||||
): FilledPartition {
|
||||
val filledItems = autofillViews
|
||||
.map { autofillView ->
|
||||
FilledItem(
|
||||
autofillId = autofillView.data.autofillId,
|
||||
val value = when (autofillView) {
|
||||
is AutofillView.Card.ExpirationMonth -> autofillCipher.expirationMonth
|
||||
is AutofillView.Card.ExpirationYear -> autofillCipher.expirationYear
|
||||
is AutofillView.Card.Number -> autofillCipher.number
|
||||
is AutofillView.Card.SecurityCode -> autofillCipher.code
|
||||
}
|
||||
autofillView.buildFilledItemOrNull(
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
|
||||
return FilledPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
filledItems = filledItems,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a [FilledPartition] by fulfilling the login [autofillViews] with data.
|
||||
* Construct a [FilledPartition] by fulfilling the login [autofillViews] with data from the
|
||||
* login [autofillCipher].
|
||||
*/
|
||||
private fun fillLoginPartition(
|
||||
autofillCipher: AutofillCipher.Login,
|
||||
autofillViews: List<AutofillView.Login>,
|
||||
): FilledPartition {
|
||||
val filledItems = autofillViews
|
||||
.map { autofillView ->
|
||||
FilledItem(
|
||||
autofillId = autofillView.data.autofillId,
|
||||
val value = when (autofillView) {
|
||||
is AutofillView.Login.EmailAddress,
|
||||
is AutofillView.Login.Username,
|
||||
-> autofillCipher.username
|
||||
|
||||
is AutofillView.Login.Password -> autofillCipher.password
|
||||
}
|
||||
autofillView.buildFilledItemOrNull(
|
||||
value = value,
|
||||
)
|
||||
}
|
||||
|
||||
return FilledPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
filledItems = filledItems,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import com.x8bit.bitwarden.data.autofill.parser.AutofillParser
|
|||
import com.x8bit.bitwarden.data.autofill.parser.AutofillParserImpl
|
||||
import com.x8bit.bitwarden.data.autofill.processor.AutofillProcessor
|
||||
import com.x8bit.bitwarden.data.autofill.processor.AutofillProcessorImpl
|
||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
|
||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProviderImpl
|
||||
import com.x8bit.bitwarden.data.platform.manager.dispatcher.DispatcherManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
@ -23,6 +25,9 @@ object AutofillModule {
|
|||
@Provides
|
||||
fun providesAutofillParser(): AutofillParser = AutofillParserImpl()
|
||||
|
||||
@Provides
|
||||
fun providesAutofillCipherProvider(): AutofillCipherProvider = AutofillCipherProviderImpl()
|
||||
|
||||
@Provides
|
||||
fun providesAutofillProcessor(
|
||||
dispatcherManager: DispatcherManager,
|
||||
|
@ -38,7 +43,11 @@ object AutofillModule {
|
|||
)
|
||||
|
||||
@Provides
|
||||
fun providesFillDataBuilder(): FilledDataBuilder = FilledDataBuilderImpl()
|
||||
fun providesFillDataBuilder(
|
||||
autofillCipherProvider: AutofillCipherProvider,
|
||||
): FilledDataBuilder = FilledDataBuilderImpl(
|
||||
autofillCipherProvider = autofillCipherProvider,
|
||||
)
|
||||
|
||||
@Provides
|
||||
fun providesFillResponseBuilder(): FillResponseBuilder = FillResponseBuilderImpl()
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package com.x8bit.bitwarden.data.autofill.model
|
||||
|
||||
/**
|
||||
* A paired down model of the CipherView for use within the autofill feature.
|
||||
*/
|
||||
sealed class AutofillCipher {
|
||||
/**
|
||||
* The name of the cipher.
|
||||
*/
|
||||
abstract val name: String
|
||||
|
||||
/**
|
||||
* The subtitle for giving additional context to the cipher.
|
||||
*/
|
||||
abstract val subtitle: String
|
||||
|
||||
/**
|
||||
* The card [AutofillCipher] model. This contains all of the data for building fulfilling a card
|
||||
* partition.
|
||||
*/
|
||||
data class Card(
|
||||
override val name: String,
|
||||
override val subtitle: String,
|
||||
val cardholderName: String,
|
||||
val code: String,
|
||||
val expirationMonth: String,
|
||||
val expirationYear: String,
|
||||
val number: String,
|
||||
) : AutofillCipher()
|
||||
|
||||
/**
|
||||
* The card [AutofillCipher] model. This contains all of the data for building fulfilling a
|
||||
* login partition.
|
||||
*/
|
||||
data class Login(
|
||||
override val name: String,
|
||||
override val subtitle: String,
|
||||
val password: String,
|
||||
val username: String,
|
||||
) : AutofillCipher()
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.autofill.model
|
||||
|
||||
import android.view.autofill.AutofillId
|
||||
import android.view.autofill.AutofillValue
|
||||
|
||||
/**
|
||||
* A fulfilled autofill view. This contains everything required to build the autofill UI
|
||||
|
@ -8,4 +9,5 @@ import android.view.autofill.AutofillId
|
|||
*/
|
||||
data class FilledItem(
|
||||
val autofillId: AutofillId,
|
||||
val value: AutofillValue,
|
||||
)
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package com.x8bit.bitwarden.data.autofill.model
|
||||
|
||||
/**
|
||||
* All of the data required to build a `Dataset` for fulfilling a partition of data based on a
|
||||
* cipher.
|
||||
* All of the data required to build a `Dataset` for fulfilling a partition of data based on an
|
||||
* [AutofillCipher].
|
||||
*
|
||||
* @param autofillCipher The cipher used to fulfill these [filledItems].
|
||||
* @param filledItems A filled copy of each view from this partition.
|
||||
*/
|
||||
data class FilledPartition(
|
||||
val autofillCipher: AutofillCipher,
|
||||
val filledItems: List<FilledItem>,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package com.x8bit.bitwarden.data.autofill.provider
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
|
||||
|
||||
/**
|
||||
* A service for getting [AutofillCipher]s.
|
||||
*/
|
||||
interface AutofillCipherProvider {
|
||||
/**
|
||||
* Get all [AutofillCipher.Card]s for the current user.
|
||||
*/
|
||||
suspend fun getCardAutofillCiphers(): List<AutofillCipher.Card>
|
||||
|
||||
/**
|
||||
* Get all [AutofillCipher.Login]s for the current user.
|
||||
*/
|
||||
suspend fun getLoginAutofillCiphers(
|
||||
uri: String,
|
||||
): List<AutofillCipher.Login>
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package com.x8bit.bitwarden.data.autofill.provider
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
|
||||
|
||||
/**
|
||||
* The default [AutofillCipherProvider] implementation. This service is used for getting currrent
|
||||
* [AutofillCipher]s.
|
||||
*/
|
||||
class AutofillCipherProviderImpl : AutofillCipherProvider {
|
||||
override suspend fun getCardAutofillCiphers(): List<AutofillCipher.Card> {
|
||||
// TODO: fulfill with real ciphers (BIT-1294)
|
||||
return cardCiphers
|
||||
}
|
||||
|
||||
override suspend fun getLoginAutofillCiphers(
|
||||
uri: String,
|
||||
): List<AutofillCipher.Login> {
|
||||
// TODO: fulfill with real ciphers (BIT-1294)
|
||||
return loginCiphers
|
||||
}
|
||||
}
|
||||
|
||||
private val cardCiphers = listOf(
|
||||
AutofillCipher.Card(
|
||||
cardholderName = "John",
|
||||
code = "123",
|
||||
expirationMonth = "January",
|
||||
expirationYear = "1999",
|
||||
name = "John",
|
||||
number = "1234567890",
|
||||
subtitle = "123...",
|
||||
),
|
||||
AutofillCipher.Card(
|
||||
cardholderName = "Doe",
|
||||
code = "456",
|
||||
expirationMonth = "December",
|
||||
expirationYear = "2024",
|
||||
name = "Doe",
|
||||
number = "0987654321",
|
||||
subtitle = "098...",
|
||||
),
|
||||
)
|
||||
private val loginCiphers = listOf(
|
||||
AutofillCipher.Login(
|
||||
name = "Bitwarden1",
|
||||
password = "password123",
|
||||
subtitle = "John-Bitwarden",
|
||||
username = "John-Bitwarden",
|
||||
),
|
||||
AutofillCipher.Login(
|
||||
name = "Bitwarden2",
|
||||
password = "password123",
|
||||
subtitle = "Doe-Bitwarden",
|
||||
username = "Doe-Bitwarden",
|
||||
),
|
||||
)
|
|
@ -0,0 +1,17 @@
|
|||
package com.x8bit.bitwarden.data.autofill.util
|
||||
|
||||
import android.view.autofill.AutofillValue
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||
|
||||
/**
|
||||
* Convert this [AutofillView] into a [FilledItem].
|
||||
*/
|
||||
fun AutofillView.buildFilledItemOrNull(
|
||||
value: String,
|
||||
): FilledItem =
|
||||
// TODO: handle other autofill types (BIT-1457)
|
||||
FilledItem(
|
||||
autofillId = data.autofillId,
|
||||
value = AutofillValue.forText(value),
|
||||
)
|
|
@ -19,6 +19,7 @@ fun FilledItem.applyToDatasetPostTiramisu(
|
|||
datasetBuilder.setField(
|
||||
autofillId,
|
||||
Field.Builder()
|
||||
.setValue(value)
|
||||
.setPresentations(presentations)
|
||||
.build(),
|
||||
)
|
||||
|
@ -35,7 +36,7 @@ fun FilledItem.applyToDatasetPreTiramisu(
|
|||
) {
|
||||
datasetBuilder.setValue(
|
||||
autofillId,
|
||||
null,
|
||||
value,
|
||||
remoteViews,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.os.Build
|
|||
import android.service.autofill.Dataset
|
||||
import android.service.autofill.Presentations
|
||||
import android.widget.RemoteViews
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledPartition
|
||||
import com.x8bit.bitwarden.ui.autofill.buildAutofillRemoteViews
|
||||
|
@ -19,7 +18,7 @@ fun FilledPartition.buildDataset(
|
|||
): Dataset {
|
||||
val remoteViewsPlaceholder = buildAutofillRemoteViews(
|
||||
packageName = autofillAppInfo.packageName,
|
||||
title = autofillAppInfo.context.resources.getString(R.string.app_name),
|
||||
title = autofillCipher.name,
|
||||
)
|
||||
val datasetBuilder = Dataset.Builder()
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ class FillResponseBuilderTest {
|
|||
fun `build should return null when filledPartitions contains no views`() {
|
||||
// Test
|
||||
val filledPartitions = FilledPartition(
|
||||
autofillCipher = mockk(),
|
||||
filledItems = emptyList(),
|
||||
)
|
||||
val filledData = FilledData(
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
package com.x8bit.bitwarden.data.autofill.builder
|
||||
|
||||
import android.view.autofill.AutofillId
|
||||
import android.view.autofill.AutofillValue
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillPartition
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillRequest
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledData
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledPartition
|
||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
|
||||
import com.x8bit.bitwarden.data.autofill.util.buildFilledItemOrNull
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
@ -16,6 +27,8 @@ import org.junit.jupiter.api.Test
|
|||
class FilledDataBuilderTest {
|
||||
private lateinit var filledDataBuilder: FilledDataBuilder
|
||||
|
||||
private val autofillCipherProvider: AutofillCipherProvider = mockk()
|
||||
|
||||
private val autofillId: AutofillId = mockk()
|
||||
private val autofillViewData = AutofillView.Data(
|
||||
autofillId = autofillId,
|
||||
|
@ -27,17 +40,45 @@ class FilledDataBuilderTest {
|
|||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
filledDataBuilder = FilledDataBuilderImpl()
|
||||
mockkStatic(AutofillValue::forText)
|
||||
mockkStatic(AutofillView::buildFilledItemOrNull)
|
||||
filledDataBuilder = FilledDataBuilderImpl(
|
||||
autofillCipherProvider = autofillCipherProvider,
|
||||
)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun teardown() {
|
||||
unmockkStatic(AutofillValue::forText)
|
||||
unmockkStatic(AutofillView::buildFilledItemOrNull)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build should return filled data and ignored AutofillIds when Login`() = runTest {
|
||||
// Setup
|
||||
val autofillView = AutofillView.Login.Username(
|
||||
val password = "Password"
|
||||
val username = "johnDoe"
|
||||
val autofillCipher = AutofillCipher.Login(
|
||||
name = "Cipher One",
|
||||
password = password,
|
||||
username = username,
|
||||
subtitle = "Subtitle",
|
||||
)
|
||||
val autofillViewEmail = AutofillView.Login.EmailAddress(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewPassword = AutofillView.Login.Password(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewUsername = AutofillView.Login.Username(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillPartition = AutofillPartition.Login(
|
||||
views = listOf(autofillView),
|
||||
views = listOf(
|
||||
autofillViewEmail,
|
||||
autofillViewPassword,
|
||||
autofillViewUsername,
|
||||
),
|
||||
)
|
||||
val ignoreAutofillIds: List<AutofillId> = mockk()
|
||||
val autofillRequest = AutofillRequest.Fillable(
|
||||
|
@ -45,12 +86,15 @@ class FilledDataBuilderTest {
|
|||
partition = autofillPartition,
|
||||
uri = URI,
|
||||
)
|
||||
val filledItem = FilledItem(
|
||||
autofillId = autofillId,
|
||||
)
|
||||
val filledItemEmail: FilledItem = mockk()
|
||||
val filledItemPassword: FilledItem = mockk()
|
||||
val filledItemUsername: FilledItem = mockk()
|
||||
val filledPartition = FilledPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
filledItems = listOf(
|
||||
filledItem,
|
||||
filledItemEmail,
|
||||
filledItemPassword,
|
||||
filledItemUsername,
|
||||
),
|
||||
)
|
||||
val expected = FilledData(
|
||||
|
@ -59,6 +103,14 @@ class FilledDataBuilderTest {
|
|||
),
|
||||
ignoreAutofillIds = ignoreAutofillIds,
|
||||
)
|
||||
coEvery {
|
||||
autofillCipherProvider.getLoginAutofillCiphers(
|
||||
uri = URI,
|
||||
)
|
||||
} returns listOf(autofillCipher)
|
||||
every { autofillViewEmail.buildFilledItemOrNull(username) } returns filledItemEmail
|
||||
every { autofillViewPassword.buildFilledItemOrNull(password) } returns filledItemPassword
|
||||
every { autofillViewUsername.buildFilledItemOrNull(username) } returns filledItemUsername
|
||||
|
||||
// Test
|
||||
val actual = filledDataBuilder.build(
|
||||
|
@ -67,16 +119,93 @@ class FilledDataBuilderTest {
|
|||
|
||||
// Verify
|
||||
assertEquals(expected, actual)
|
||||
coVerify(exactly = 1) {
|
||||
autofillCipherProvider.getLoginAutofillCiphers(
|
||||
uri = URI,
|
||||
)
|
||||
}
|
||||
verify(exactly = 1) {
|
||||
autofillViewEmail.buildFilledItemOrNull(username)
|
||||
autofillViewPassword.buildFilledItemOrNull(password)
|
||||
autofillViewUsername.buildFilledItemOrNull(username)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build should return no partitions and ignored AutofillIds when Login and no URI`() =
|
||||
runTest {
|
||||
// Setup
|
||||
val autofillViewEmail = AutofillView.Login.EmailAddress(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewPassword = AutofillView.Login.Password(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewUsername = AutofillView.Login.Username(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillPartition = AutofillPartition.Login(
|
||||
views = listOf(
|
||||
autofillViewEmail,
|
||||
autofillViewPassword,
|
||||
autofillViewUsername,
|
||||
),
|
||||
)
|
||||
val ignoreAutofillIds: List<AutofillId> = mockk()
|
||||
val autofillRequest = AutofillRequest.Fillable(
|
||||
ignoreAutofillIds = ignoreAutofillIds,
|
||||
partition = autofillPartition,
|
||||
uri = null,
|
||||
)
|
||||
val expected = FilledData(
|
||||
filledPartitions = emptyList(),
|
||||
ignoreAutofillIds = ignoreAutofillIds,
|
||||
)
|
||||
|
||||
// Test
|
||||
val actual = filledDataBuilder.build(
|
||||
autofillRequest = autofillRequest,
|
||||
)
|
||||
|
||||
// Verify
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `build should return filled data and ignored AutofillIds when Card`() = runTest {
|
||||
// Setup
|
||||
val autofillView = AutofillView.Card.Number(
|
||||
val code = "123"
|
||||
val expirationMonth = "January"
|
||||
val expirationYear = "1999"
|
||||
val number = "1234567890"
|
||||
val autofillCipher = AutofillCipher.Card(
|
||||
cardholderName = "John",
|
||||
code = code,
|
||||
expirationMonth = expirationMonth,
|
||||
expirationYear = expirationYear,
|
||||
name = "Cipher One",
|
||||
number = number,
|
||||
subtitle = "Subtitle",
|
||||
)
|
||||
val autofillViewCode = AutofillView.Card.SecurityCode(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewExpirationMonth = AutofillView.Card.ExpirationMonth(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewExpirationYear = AutofillView.Card.ExpirationYear(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillViewNumber = AutofillView.Card.Number(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val autofillPartition = AutofillPartition.Card(
|
||||
views = listOf(autofillView),
|
||||
views = listOf(
|
||||
autofillViewCode,
|
||||
autofillViewExpirationMonth,
|
||||
autofillViewExpirationYear,
|
||||
autofillViewNumber,
|
||||
),
|
||||
)
|
||||
val ignoreAutofillIds: List<AutofillId> = mockk()
|
||||
val autofillRequest = AutofillRequest.Fillable(
|
||||
|
@ -84,12 +213,17 @@ class FilledDataBuilderTest {
|
|||
partition = autofillPartition,
|
||||
uri = URI,
|
||||
)
|
||||
val filledItem = FilledItem(
|
||||
autofillId = autofillId,
|
||||
)
|
||||
val filledItemCode: FilledItem = mockk()
|
||||
val filledItemExpirationMonth: FilledItem = mockk()
|
||||
val filledItemExpirationYear: FilledItem = mockk()
|
||||
val filledItemNumber: FilledItem = mockk()
|
||||
val filledPartition = FilledPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
filledItems = listOf(
|
||||
filledItem,
|
||||
filledItemCode,
|
||||
filledItemExpirationMonth,
|
||||
filledItemExpirationYear,
|
||||
filledItemNumber,
|
||||
),
|
||||
)
|
||||
val expected = FilledData(
|
||||
|
@ -98,6 +232,15 @@ class FilledDataBuilderTest {
|
|||
),
|
||||
ignoreAutofillIds = ignoreAutofillIds,
|
||||
)
|
||||
coEvery { autofillCipherProvider.getCardAutofillCiphers() } returns listOf(autofillCipher)
|
||||
every { autofillViewCode.buildFilledItemOrNull(code) } returns filledItemCode
|
||||
every {
|
||||
autofillViewExpirationMonth.buildFilledItemOrNull(expirationMonth)
|
||||
} returns filledItemExpirationMonth
|
||||
every {
|
||||
autofillViewExpirationYear.buildFilledItemOrNull(expirationYear)
|
||||
} returns filledItemExpirationYear
|
||||
every { autofillViewNumber.buildFilledItemOrNull(number) } returns filledItemNumber
|
||||
|
||||
// Test
|
||||
val actual = filledDataBuilder.build(
|
||||
|
@ -106,6 +249,13 @@ class FilledDataBuilderTest {
|
|||
|
||||
// Verify
|
||||
assertEquals(expected, actual)
|
||||
coVerify(exactly = 1) {
|
||||
autofillCipherProvider.getCardAutofillCiphers()
|
||||
autofillViewCode.buildFilledItemOrNull(code)
|
||||
autofillViewExpirationMonth.buildFilledItemOrNull(expirationMonth)
|
||||
autofillViewExpirationYear.buildFilledItemOrNull(expirationYear)
|
||||
autofillViewNumber.buildFilledItemOrNull(number)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package com.x8bit.bitwarden.data.autofill.processor
|
||||
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
|
||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProvider
|
||||
import com.x8bit.bitwarden.data.autofill.provider.AutofillCipherProviderImpl
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AutofillCipherProviderTest {
|
||||
private lateinit var autofillCipherProvider: AutofillCipherProvider
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
autofillCipherProvider = AutofillCipherProviderImpl()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getCardAutofillCiphers should return default list of card ciphers`() = runTest {
|
||||
// Test & Verify
|
||||
val actual = autofillCipherProvider.getCardAutofillCiphers()
|
||||
|
||||
assertEquals(CARD_CIPHERS, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getLoginAutofillCiphers should return default list of login ciphers`() = runTest {
|
||||
// Test & Verify
|
||||
val actual = autofillCipherProvider.getLoginAutofillCiphers(
|
||||
uri = URI,
|
||||
)
|
||||
|
||||
assertEquals(LOGIN_CIPHERS, actual)
|
||||
}
|
||||
}
|
||||
|
||||
private val CARD_CIPHERS = listOf(
|
||||
AutofillCipher.Card(
|
||||
cardholderName = "John",
|
||||
code = "123",
|
||||
expirationMonth = "January",
|
||||
expirationYear = "1999",
|
||||
name = "John",
|
||||
number = "1234567890",
|
||||
subtitle = "123...",
|
||||
),
|
||||
AutofillCipher.Card(
|
||||
cardholderName = "Doe",
|
||||
code = "456",
|
||||
expirationMonth = "December",
|
||||
expirationYear = "2024",
|
||||
name = "Doe",
|
||||
number = "0987654321",
|
||||
subtitle = "098...",
|
||||
),
|
||||
)
|
||||
private val LOGIN_CIPHERS = listOf(
|
||||
AutofillCipher.Login(
|
||||
name = "Bitwarden1",
|
||||
password = "password123",
|
||||
subtitle = "John-Bitwarden",
|
||||
username = "John-Bitwarden",
|
||||
),
|
||||
AutofillCipher.Login(
|
||||
name = "Bitwarden2",
|
||||
password = "password123",
|
||||
subtitle = "Doe-Bitwarden",
|
||||
username = "Doe-Bitwarden",
|
||||
),
|
||||
)
|
||||
private const val URI: String = "androidapp://com.x8bit.bitwarden"
|
|
@ -0,0 +1,62 @@
|
|||
package com.x8bit.bitwarden.data.autofill.util
|
||||
|
||||
import android.view.autofill.AutofillId
|
||||
import android.view.autofill.AutofillValue
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillView
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AutofillViewExtensionsTest {
|
||||
private val autofillId: AutofillId = mockk()
|
||||
private val autofillValue: AutofillValue = mockk()
|
||||
private val autofillViewData = AutofillView.Data(
|
||||
autofillId = autofillId,
|
||||
idPackage = null,
|
||||
isFocused = false,
|
||||
webDomain = null,
|
||||
webScheme = null,
|
||||
)
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
mockkStatic(AutofillValue::forText)
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun teardown() {
|
||||
unmockkStatic(AutofillValue::forText)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `buildFilledItem returns AutofillValue`() {
|
||||
// Setup
|
||||
val value = "2002421451023587L"
|
||||
val autofillView = AutofillView.Card.Number(
|
||||
data = autofillViewData,
|
||||
)
|
||||
val expected = FilledItem(
|
||||
autofillId = autofillId,
|
||||
value = autofillValue,
|
||||
)
|
||||
every { AutofillValue.forText(value) } returns autofillValue
|
||||
|
||||
// Test
|
||||
val actual = autofillView.buildFilledItemOrNull(
|
||||
value = value,
|
||||
)
|
||||
|
||||
// Verify
|
||||
assertEquals(expected, actual)
|
||||
verify(exactly = 1) {
|
||||
AutofillValue.forText(value)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import android.service.autofill.Dataset
|
|||
import android.service.autofill.Field
|
||||
import android.service.autofill.Presentations
|
||||
import android.view.autofill.AutofillId
|
||||
import android.view.autofill.AutofillValue
|
||||
import android.widget.RemoteViews
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||
import com.x8bit.bitwarden.data.util.mockBuilder
|
||||
|
@ -18,10 +19,12 @@ import org.junit.jupiter.api.Test
|
|||
|
||||
class FilledItemExtensionsTest {
|
||||
private val autofillId: AutofillId = mockk()
|
||||
private val autofillValue: AutofillValue = mockk()
|
||||
private val datasetBuilder: Dataset.Builder = mockk()
|
||||
private val field: Field = mockk()
|
||||
private val filledItem = FilledItem(
|
||||
autofillId = autofillId,
|
||||
value = autofillValue,
|
||||
)
|
||||
private val presentations: Presentations = mockk()
|
||||
private val remoteViews: RemoteViews = mockk()
|
||||
|
@ -44,7 +47,7 @@ class FilledItemExtensionsTest {
|
|||
every {
|
||||
datasetBuilder.setValue(
|
||||
autofillId,
|
||||
null,
|
||||
autofillValue,
|
||||
remoteViews,
|
||||
)
|
||||
} returns datasetBuilder
|
||||
|
@ -59,7 +62,7 @@ class FilledItemExtensionsTest {
|
|||
verify(exactly = 1) {
|
||||
datasetBuilder.setValue(
|
||||
autofillId,
|
||||
null,
|
||||
autofillValue,
|
||||
remoteViews,
|
||||
)
|
||||
}
|
||||
|
@ -68,6 +71,7 @@ class FilledItemExtensionsTest {
|
|||
@Test
|
||||
fun `applyToDatasetPostTiramisu should use setField to set presentations`() {
|
||||
// Setup
|
||||
mockBuilder<Field.Builder> { it.setValue(autofillValue) }
|
||||
mockBuilder<Field.Builder> { it.setPresentations(presentations) }
|
||||
every {
|
||||
datasetBuilder.setField(
|
||||
|
@ -84,6 +88,7 @@ class FilledItemExtensionsTest {
|
|||
|
||||
// Verify
|
||||
verify(exactly = 1) {
|
||||
anyConstructed<Field.Builder>().setValue(autofillValue)
|
||||
anyConstructed<Field.Builder>().setPresentations(presentations)
|
||||
datasetBuilder.setField(
|
||||
autofillId,
|
||||
|
|
|
@ -5,8 +5,8 @@ import android.content.res.Resources
|
|||
import android.service.autofill.Dataset
|
||||
import android.service.autofill.Presentations
|
||||
import android.widget.RemoteViews
|
||||
import com.x8bit.bitwarden.R
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillAppInfo
|
||||
import com.x8bit.bitwarden.data.autofill.model.AutofillCipher
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledItem
|
||||
import com.x8bit.bitwarden.data.autofill.model.FilledPartition
|
||||
import com.x8bit.bitwarden.data.util.mockBuilder
|
||||
|
@ -26,6 +26,9 @@ import org.junit.jupiter.api.BeforeEach
|
|||
import org.junit.jupiter.api.Test
|
||||
|
||||
class FilledPartitionExtensionsTest {
|
||||
private val autofillCipher: AutofillCipher = mockk {
|
||||
every { this@mockk.name } returns CIPHER_NAME
|
||||
}
|
||||
private val res: Resources = mockk()
|
||||
private val context: Context = mockk {
|
||||
every { this@mockk.resources } returns res
|
||||
|
@ -33,6 +36,7 @@ class FilledPartitionExtensionsTest {
|
|||
private val dataset: Dataset = mockk()
|
||||
private val filledItem: FilledItem = mockk()
|
||||
private val filledPartition = FilledPartition(
|
||||
autofillCipher = autofillCipher,
|
||||
filledItems = listOf(
|
||||
filledItem,
|
||||
),
|
||||
|
@ -67,12 +71,10 @@ class FilledPartitionExtensionsTest {
|
|||
packageName = PACKAGE_NAME,
|
||||
sdkInt = 34,
|
||||
)
|
||||
val title = "Bitwarden"
|
||||
every { res.getString(R.string.app_name) } returns title
|
||||
every {
|
||||
buildAutofillRemoteViews(
|
||||
packageName = PACKAGE_NAME,
|
||||
title = title,
|
||||
title = CIPHER_NAME,
|
||||
)
|
||||
} returns remoteViews
|
||||
mockBuilder<Presentations.Builder> { it.setMenuPresentation(remoteViews) }
|
||||
|
@ -94,7 +96,7 @@ class FilledPartitionExtensionsTest {
|
|||
verify(exactly = 1) {
|
||||
buildAutofillRemoteViews(
|
||||
packageName = PACKAGE_NAME,
|
||||
title = title,
|
||||
title = CIPHER_NAME,
|
||||
)
|
||||
anyConstructed<Presentations.Builder>().setMenuPresentation(remoteViews)
|
||||
anyConstructed<Presentations.Builder>().build()
|
||||
|
@ -114,12 +116,10 @@ class FilledPartitionExtensionsTest {
|
|||
packageName = PACKAGE_NAME,
|
||||
sdkInt = 18,
|
||||
)
|
||||
val title = "Bitwarden"
|
||||
every { res.getString(R.string.app_name) } returns title
|
||||
every {
|
||||
buildAutofillRemoteViews(
|
||||
packageName = PACKAGE_NAME,
|
||||
title = title,
|
||||
title = CIPHER_NAME,
|
||||
)
|
||||
} returns remoteViews
|
||||
every {
|
||||
|
@ -139,7 +139,7 @@ class FilledPartitionExtensionsTest {
|
|||
verify(exactly = 1) {
|
||||
buildAutofillRemoteViews(
|
||||
packageName = PACKAGE_NAME,
|
||||
title = title,
|
||||
title = CIPHER_NAME,
|
||||
)
|
||||
filledItem.applyToDatasetPreTiramisu(
|
||||
datasetBuilder = any(),
|
||||
|
@ -150,6 +150,7 @@ class FilledPartitionExtensionsTest {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val CIPHER_NAME: String = "Autofill Cipher"
|
||||
private const val PACKAGE_NAME: String = "com.x8bit.bitwarden"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue