Skip to content

Commit a40a234

Browse files
committed
[PM-32808] test: Cover Driver's License view screen and null-guard branches
Backfills screen-level Compose tests for the new Driver's License view content and exercises the null/blank guards in the ViewModel copy handlers so the behavior is locked in alongside the happy paths.
1 parent 1bc6a61 commit a40a234

2 files changed

Lines changed: 404 additions & 0 deletions

File tree

app/src/test/kotlin/com/x8bit/bitwarden/ui/vault/feature/item/VaultItemScreenTest.kt

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3520,6 +3520,236 @@ class VaultItemScreenTest : BitwardenComposeTest() {
35203520
}
35213521

35223522
//endregion bank account
3523+
3524+
//region drivers license
3525+
3526+
@Test
3527+
fun `in drivers license state, all fields should be displayed when populated`() {
3528+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3529+
3530+
composeTestRule.onNodeWithTextAfterScroll("the first name").assertIsDisplayed()
3531+
composeTestRule.onNodeWithTextAfterScroll("the middle name").assertIsDisplayed()
3532+
composeTestRule.onNodeWithTextAfterScroll("the last name").assertIsDisplayed()
3533+
composeTestRule.onNodeWithTextAfterScroll("the date of birth").assertIsDisplayed()
3534+
composeTestRule.onNodeWithTextAfterScroll("the issuing country").assertIsDisplayed()
3535+
composeTestRule.onNodeWithTextAfterScroll("the issuing state").assertIsDisplayed()
3536+
composeTestRule.onNodeWithTextAfterScroll("the issuing authority").assertIsDisplayed()
3537+
composeTestRule.onNodeWithTextAfterScroll("the issue date").assertIsDisplayed()
3538+
composeTestRule.onNodeWithTextAfterScroll("the expiration date").assertIsDisplayed()
3539+
composeTestRule.onNodeWithTextAfterScroll("the license class").assertIsDisplayed()
3540+
}
3541+
3542+
@Test
3543+
fun `in drivers license state, on copy first name click should send CopyFirstNameClick`() {
3544+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3545+
composeTestRule
3546+
.onNodeWithContentDescriptionAfterScroll("Copy first name")
3547+
.performClick()
3548+
3549+
verify(exactly = 1) {
3550+
viewModel.trySendAction(
3551+
VaultItemAction.ItemType.DriversLicense.CopyFirstNameClick,
3552+
)
3553+
}
3554+
}
3555+
3556+
@Test
3557+
fun `in drivers license state, on copy middle name click should send CopyMiddleNameClick`() {
3558+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3559+
composeTestRule
3560+
.onNode(hasScrollToNodeAction())
3561+
.performScrollToNode(hasTestTag("DriversLicenseCopyMiddleNameButton"))
3562+
composeTestRule
3563+
.onNodeWithTag("DriversLicenseCopyMiddleNameButton")
3564+
.performSemanticsAction(SemanticsActions.OnClick)
3565+
3566+
verify(exactly = 1) {
3567+
viewModel.trySendAction(
3568+
VaultItemAction.ItemType.DriversLicense.CopyMiddleNameClick,
3569+
)
3570+
}
3571+
}
3572+
3573+
@Test
3574+
fun `in drivers license state, on copy last name click should send CopyLastNameClick`() {
3575+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3576+
composeTestRule
3577+
.onNodeWithContentDescriptionAfterScroll("Copy last name")
3578+
.performClick()
3579+
3580+
verify(exactly = 1) {
3581+
viewModel.trySendAction(
3582+
VaultItemAction.ItemType.DriversLicense.CopyLastNameClick,
3583+
)
3584+
}
3585+
}
3586+
3587+
@Suppress("MaxLineLength")
3588+
@Test
3589+
fun `in drivers license state, on copy license number click should send CopyLicenseNumberClick`() {
3590+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3591+
composeTestRule
3592+
.onNodeWithContentDescriptionAfterScroll("Copy license number")
3593+
.performClick()
3594+
3595+
verify(exactly = 1) {
3596+
viewModel.trySendAction(
3597+
VaultItemAction.ItemType.DriversLicense.CopyLicenseNumberClick,
3598+
)
3599+
}
3600+
}
3601+
3602+
@Test
3603+
fun `in drivers license state, firstName should be displayed according to state`() {
3604+
val firstName = "the first name"
3605+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3606+
composeTestRule.onNodeWithTextAfterScroll(firstName).assertIsDisplayed()
3607+
3608+
mutableStateFlow.update { currentState ->
3609+
updateDriversLicenseType(currentState) { copy(firstName = null) }
3610+
}
3611+
3612+
composeTestRule.assertScrollableNodeDoesNotExist(firstName)
3613+
}
3614+
3615+
@Test
3616+
fun `in drivers license state, middleName should be displayed according to state`() {
3617+
val middleName = "the middle name"
3618+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3619+
composeTestRule.onNodeWithTextAfterScroll(middleName).assertIsDisplayed()
3620+
3621+
mutableStateFlow.update { currentState ->
3622+
updateDriversLicenseType(currentState) { copy(middleName = null) }
3623+
}
3624+
3625+
composeTestRule.assertScrollableNodeDoesNotExist(middleName)
3626+
}
3627+
3628+
@Test
3629+
fun `in drivers license state, lastName should be displayed according to state`() {
3630+
val lastName = "the last name"
3631+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3632+
composeTestRule.onNodeWithTextAfterScroll(lastName).assertIsDisplayed()
3633+
3634+
mutableStateFlow.update { currentState ->
3635+
updateDriversLicenseType(currentState) { copy(lastName = null) }
3636+
}
3637+
3638+
composeTestRule.assertScrollableNodeDoesNotExist(lastName)
3639+
}
3640+
3641+
@Test
3642+
fun `in drivers license state, licenseNumber should be displayed according to state`() {
3643+
val licenseNumberLabel = "License number"
3644+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3645+
composeTestRule.onNodeWithTextAfterScroll(licenseNumberLabel).assertIsDisplayed()
3646+
3647+
mutableStateFlow.update { currentState ->
3648+
updateDriversLicenseType(currentState) { copy(licenseNumber = null) }
3649+
}
3650+
3651+
composeTestRule.assertScrollableNodeDoesNotExist(licenseNumberLabel)
3652+
}
3653+
3654+
@Test
3655+
fun `in drivers license state, dateOfBirth should be displayed according to state`() {
3656+
val dateOfBirth = "the date of birth"
3657+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3658+
composeTestRule.onNodeWithTextAfterScroll(dateOfBirth).assertIsDisplayed()
3659+
3660+
mutableStateFlow.update { currentState ->
3661+
updateDriversLicenseType(currentState) { copy(dateOfBirth = null) }
3662+
}
3663+
3664+
composeTestRule.assertScrollableNodeDoesNotExist(dateOfBirth)
3665+
}
3666+
3667+
@Test
3668+
fun `in drivers license state, issuingCountry should be displayed according to state`() {
3669+
val issuingCountry = "the issuing country"
3670+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3671+
composeTestRule.onNodeWithTextAfterScroll(issuingCountry).assertIsDisplayed()
3672+
3673+
mutableStateFlow.update { currentState ->
3674+
updateDriversLicenseType(currentState) { copy(issuingCountry = null) }
3675+
}
3676+
3677+
composeTestRule.assertScrollableNodeDoesNotExist(issuingCountry)
3678+
}
3679+
3680+
@Test
3681+
fun `in drivers license state, issuingState should be displayed according to state`() {
3682+
val issuingState = "the issuing state"
3683+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3684+
composeTestRule.onNodeWithTextAfterScroll(issuingState).assertIsDisplayed()
3685+
3686+
mutableStateFlow.update { currentState ->
3687+
updateDriversLicenseType(currentState) { copy(issuingState = null) }
3688+
}
3689+
3690+
composeTestRule.assertScrollableNodeDoesNotExist(issuingState)
3691+
}
3692+
3693+
@Test
3694+
fun `in drivers license state, issuingAuthority should be displayed according to state`() {
3695+
val issuingAuthority = "the issuing authority"
3696+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3697+
composeTestRule.onNodeWithTextAfterScroll(issuingAuthority).assertIsDisplayed()
3698+
3699+
mutableStateFlow.update { currentState ->
3700+
updateDriversLicenseType(currentState) { copy(issuingAuthority = null) }
3701+
}
3702+
3703+
composeTestRule.assertScrollableNodeDoesNotExist(issuingAuthority)
3704+
}
3705+
3706+
@Test
3707+
fun `in drivers license state, issueDate should be displayed according to state`() {
3708+
val issueDate = "the issue date"
3709+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3710+
composeTestRule.onNodeWithTextAfterScroll(issueDate).assertIsDisplayed()
3711+
3712+
mutableStateFlow.update { currentState ->
3713+
updateDriversLicenseType(currentState) { copy(issueDate = null) }
3714+
}
3715+
3716+
composeTestRule.assertScrollableNodeDoesNotExist(issueDate)
3717+
}
3718+
3719+
@Test
3720+
fun `in drivers license state, expirationDate should be displayed according to state`() {
3721+
val expirationDate = "the expiration date"
3722+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3723+
composeTestRule.onNodeWithTextAfterScroll(expirationDate).assertIsDisplayed()
3724+
3725+
mutableStateFlow.update { currentState ->
3726+
updateDriversLicenseType(currentState) { copy(expirationDate = null) }
3727+
}
3728+
3729+
composeTestRule.assertScrollableNodeDoesNotExist(expirationDate)
3730+
}
3731+
3732+
@Test
3733+
fun `in drivers license state, licenseClass should be displayed according to state`() {
3734+
val licenseClass = "the license class"
3735+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3736+
composeTestRule.onNodeWithTextAfterScroll(licenseClass).assertIsDisplayed()
3737+
3738+
mutableStateFlow.update { currentState ->
3739+
updateDriversLicenseType(currentState) { copy(licenseClass = null) }
3740+
}
3741+
3742+
composeTestRule.assertScrollableNodeDoesNotExist(licenseClass)
3743+
}
3744+
3745+
@Test
3746+
fun `in drivers license state, edit fab should be displayed`() {
3747+
mutableStateFlow.update { it.copy(viewState = DEFAULT_DRIVERS_LICENSE_VIEW_STATE) }
3748+
3749+
composeTestRule.onNodeWithContentDescription("Edit item").assertIsDisplayed()
3750+
}
3751+
3752+
//endregion drivers license
35233753
}
35243754

35253755
//region Helper functions
@@ -3616,6 +3846,29 @@ private fun updateBankAccountType(
36163846
return currentState.copy(viewState = updatedType)
36173847
}
36183848

3849+
private fun updateDriversLicenseType(
3850+
currentState: VaultItemState,
3851+
transform: VaultItemState.ViewState.Content.ItemType.DriversLicense.() ->
3852+
VaultItemState.ViewState.Content.ItemType.DriversLicense,
3853+
): VaultItemState {
3854+
val updatedType = when (val viewState = currentState.viewState) {
3855+
is VaultItemState.ViewState.Content -> {
3856+
when (val type = viewState.type) {
3857+
is VaultItemState.ViewState.Content.ItemType.DriversLicense -> {
3858+
viewState.copy(
3859+
type = type.transform(),
3860+
)
3861+
}
3862+
3863+
else -> viewState
3864+
}
3865+
}
3866+
3867+
else -> viewState
3868+
}
3869+
return currentState.copy(viewState = updatedType)
3870+
}
3871+
36193872
private fun updateCommonContent(
36203873
currentState: VaultItemState,
36213874
transform: VaultItemState.ViewState.Content.Common.()
@@ -3910,6 +4163,27 @@ private val DEFAULT_BANK_ACCOUNT_VIEW_STATE: VaultItemState.ViewState.Content =
39104163
type = DEFAULT_BANK_ACCOUNT,
39114164
)
39124165

4166+
private val DEFAULT_DRIVERS_LICENSE: VaultItemState.ViewState.Content.ItemType.DriversLicense =
4167+
VaultItemState.ViewState.Content.ItemType.DriversLicense(
4168+
firstName = "the first name",
4169+
middleName = "the middle name",
4170+
lastName = "the last name",
4171+
licenseNumber = "the license number",
4172+
dateOfBirth = "the date of birth",
4173+
issuingCountry = "the issuing country",
4174+
issuingState = "the issuing state",
4175+
issuingAuthority = "the issuing authority",
4176+
issueDate = "the issue date",
4177+
expirationDate = "the expiration date",
4178+
licenseClass = "the license class",
4179+
)
4180+
4181+
private val DEFAULT_DRIVERS_LICENSE_VIEW_STATE: VaultItemState.ViewState.Content =
4182+
VaultItemState.ViewState.Content(
4183+
common = DEFAULT_COMMON.copy(iconData = IconData.Local(BitwardenDrawable.ic_globe)),
4184+
type = DEFAULT_DRIVERS_LICENSE,
4185+
)
4186+
39134187
private val EMPTY_VIEW_STATES = listOf(
39144188
EMPTY_LOGIN_VIEW_STATE,
39154189
EMPTY_IDENTITY_VIEW_STATE,

0 commit comments

Comments
 (0)