For #18273 - [Edit card] Delete a saved credit card (#19029)

upstream-sync
Gabriel Luong 3 years ago committed by GitHub
parent d8660341a1
commit f7c56ee6fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -65,9 +65,15 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor)
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.credit_card_editor, menu) inflater.inflate(R.menu.credit_card_editor, menu)
menu.findItem(R.id.delete_credit_card_button).isVisible = isEditing
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
R.id.delete_credit_card_button -> {
args.creditCard?.let { interactor.onDeleteCardButtonClicked(it.guid) }
true
}
R.id.save_credit_card_button -> { R.id.save_credit_card_button -> {
saveCreditCard() saveCreditCard()
true true
@ -88,7 +94,7 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor)
cardNumber = card_number_input.text.toString(), cardNumber = card_number_input.text.toString(),
expiryMonth = (expiry_month_drop_down.selectedItemPosition + 1).toLong(), expiryMonth = (expiry_month_drop_down.selectedItemPosition + 1).toLong(),
expiryYear = expiry_year_drop_down.selectedItem.toString().toLong(), expiryYear = expiry_year_drop_down.selectedItem.toString().toLong(),
cardType = "amex" cardType = CARD_TYPE_PLACEHOLDER
) )
) )
} }
@ -96,5 +102,9 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor)
companion object { companion object {
// Number of years to show in the expiry year dropdown. // Number of years to show in the expiry year dropdown.
const val NUMBER_OF_YEARS_TO_SHOW = 10 const val NUMBER_OF_YEARS_TO_SHOW = 10
// Placeholder for the card type. This will be replaced when we can identify the card type.
// This is dependent on https://github.com/mozilla-mobile/android-components/issues/9813.
const val CARD_TYPE_PLACEHOLDER = ""
} }
} }

@ -11,13 +11,15 @@ import java.util.Calendar
/** /**
* The state for the [CreditCardEditorFragment]. * The state for the [CreditCardEditorFragment].
* *
* @property guid The unique identifier for the edited credit card.
* @property billingName The credit card billing name to display. * @property billingName The credit card billing name to display.
* @property cardNumber The credit card number to display. * @property cardNumber The credit card number to display.
* @property expiryMonth The selected credit card expiry month. * @property expiryMonth The selected credit card expiry month.
* @property expiryYears The range of expiry years to display. * @property expiryYears The range of expiry years to display.
* @property isEditing Whether or not the credit is being edited. * @property isEditing Whether or not the credit card is being edited.
*/ */
data class CreditCardEditorState( data class CreditCardEditorState(
val guid: String = "",
val billingName: String = "", val billingName: String = "",
val cardNumber: String = "", val cardNumber: String = "",
val expiryMonth: Int = 1, val expiryMonth: Int = 1,
@ -33,6 +35,7 @@ fun CreditCard.toCreditCardEditorState(): CreditCardEditorState {
val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW
return CreditCardEditorState( return CreditCardEditorState(
guid = guid,
billingName = billingName, billingName = billingName,
cardNumber = cardNumber, cardNumber = cardNumber,
expiryMonth = expiryMonth.toInt(), expiryMonth = expiryMonth.toInt(),

@ -23,6 +23,11 @@ interface CreditCardEditorController {
*/ */
fun handleCancelButtonClicked() fun handleCancelButtonClicked()
/**
* @see [CreditCardEditorInteractor.onDeleteCardButtonClicked]
*/
fun handleDeleteCreditCard(guid: String)
/** /**
* @see [CreditCardEditorInteractor.onSaveButtonClicked] * @see [CreditCardEditorInteractor.onSaveButtonClicked]
*/ */
@ -49,6 +54,16 @@ class DefaultCreditCardEditorController(
navController.popBackStack() navController.popBackStack()
} }
override fun handleDeleteCreditCard(guid: String) {
lifecycleScope.launch(ioDispatcher) {
storage.deleteCreditCard(guid)
lifecycleScope.launch(Dispatchers.Main) {
navController.popBackStack()
}
}
}
override fun handleSaveCreditCard(creditCardFields: UpdatableCreditCardFields) { override fun handleSaveCreditCard(creditCardFields: UpdatableCreditCardFields) {
lifecycleScope.launch(ioDispatcher) { lifecycleScope.launch(ioDispatcher) {
storage.addCreditCard(creditCardFields) storage.addCreditCard(creditCardFields)

@ -18,6 +18,14 @@ interface CreditCardEditorInteractor {
*/ */
fun onCancelButtonClicked() fun onCancelButtonClicked()
/**
* Deletes the provided credit card in the credit card storage. Called when a user
* taps on the delete menu item or "Delete card" button.
*
* @param guid Unique identifier for the credit card to be deleted.
*/
fun onDeleteCardButtonClicked(guid: String)
/** /**
* Saves the provided credit card field into the credit card storage. Called when a user * Saves the provided credit card field into the credit card storage. Called when a user
* taps on the save menu item or "Save" button. * taps on the save menu item or "Save" button.
@ -41,6 +49,10 @@ class DefaultCreditCardEditorInteractor(
controller.handleCancelButtonClicked() controller.handleCancelButtonClicked()
} }
override fun onDeleteCardButtonClicked(guid: String) {
controller.handleDeleteCreditCard(guid)
}
override fun onSaveButtonClicked(creditCardFields: UpdatableCreditCardFields) { override fun onSaveButtonClicked(creditCardFields: UpdatableCreditCardFields) {
controller.handleSaveCreditCard(creditCardFields) controller.handleSaveCreditCard(creditCardFields)
} }

@ -12,6 +12,7 @@ import kotlinx.android.synthetic.main.fragment_credit_card_editor.*
import mozilla.components.concept.storage.UpdatableCreditCardFields import mozilla.components.concept.storage.UpdatableCreditCardFields
import mozilla.components.support.ktx.android.view.hideKeyboard import mozilla.components.support.ktx.android.view.hideKeyboard
import org.mozilla.fenix.ext.toEditable import org.mozilla.fenix.ext.toEditable
import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.CARD_TYPE_PLACEHOLDER
import org.mozilla.fenix.settings.creditcards.CreditCardEditorState import org.mozilla.fenix.settings.creditcards.CreditCardEditorState
import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -30,6 +31,16 @@ class CreditCardEditorView(
* Binds the given [CreditCardEditorState] in the [CreditCardEditorFragment]. * Binds the given [CreditCardEditorState] in the [CreditCardEditorFragment].
*/ */
fun bind(state: CreditCardEditorState) { fun bind(state: CreditCardEditorState) {
if (state.isEditing) {
delete_button.apply {
visibility = View.VISIBLE
setOnClickListener {
interactor.onDeleteCardButtonClicked(state.guid)
}
}
}
cancel_button.setOnClickListener { cancel_button.setOnClickListener {
interactor.onCancelButtonClicked() interactor.onCancelButtonClicked()
} }
@ -99,7 +110,7 @@ class CreditCardEditorView(
cardNumber = card_number_input.text.toString(), cardNumber = card_number_input.text.toString(),
expiryMonth = (expiry_month_drop_down.selectedItemPosition + 1).toLong(), expiryMonth = (expiry_month_drop_down.selectedItemPosition + 1).toLong(),
expiryYear = expiry_year_drop_down.selectedItem.toString().toLong(), expiryYear = expiry_year_drop_down.selectedItem.toString().toLong(),
cardType = "amex" cardType = CARD_TYPE_PLACEHOLDER
) )
) )
} }

@ -4,6 +4,13 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/delete_credit_card_button"
android:icon="@drawable/ic_delete"
android:title="@string/credit_cards_menu_delete_card"
android:visible="false"
app:iconTint="?primaryText"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/save_credit_card_button" android:id="@+id/save_credit_card_button"
android:icon="@drawable/mozac_ic_check" android:icon="@drawable/mozac_ic_check"

@ -1542,6 +1542,8 @@
<string name="credit_cards_name_on_card">Name on Card</string> <string name="credit_cards_name_on_card">Name on Card</string>
<!-- The header for the nickname for a credit card --> <!-- The header for the nickname for a credit card -->
<string name="credit_cards_card_nickname">Card Nickname</string> <string name="credit_cards_card_nickname">Card Nickname</string>
<!-- The text for the "Delete card" menu item for deleting a credit card -->
<string name="credit_cards_menu_delete_card">Delete card</string>
<!-- The text for the "Delete card" button for deleting a credit card --> <!-- The text for the "Delete card" button for deleting a credit card -->
<string name="credit_cards_delete_card_button">Delete card</string> <string name="credit_cards_delete_card_button">Delete card</string>
<!-- The title for the "Save" menu item for saving a credit card --> <!-- The title for the "Save" menu item for saving a credit card -->

@ -34,6 +34,7 @@ class CreditCardEditorStateTest {
val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW
with(state) { with(state) {
assertEquals(creditCard.guid, guid)
assertEquals(creditCard.billingName, billingName) assertEquals(creditCard.billingName, billingName)
assertEquals(creditCard.cardNumber, cardNumber) assertEquals(creditCard.cardNumber, cardNumber)
assertEquals(creditCard.expiryMonth.toInt(), expiryMonth) assertEquals(creditCard.expiryMonth.toInt(), expiryMonth)
@ -50,6 +51,7 @@ class CreditCardEditorStateTest {
val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW
with(state) { with(state) {
assertEquals("", guid)
assertEquals("", billingName) assertEquals("", billingName)
assertEquals("", cardNumber) assertEquals("", cardNumber)
assertEquals(1, expiryMonth) assertEquals(1, expiryMonth)

@ -18,6 +18,7 @@ import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.CARD_TYPE_PLACEHOLDER
import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.NUMBER_OF_YEARS_TO_SHOW import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.NUMBER_OF_YEARS_TO_SHOW
import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor
import org.mozilla.fenix.settings.creditcards.view.CreditCardEditorView import org.mozilla.fenix.settings.creditcards.view.CreditCardEditorView
@ -73,6 +74,8 @@ class CreditCardEditorViewTest {
assertEquals(startYear.toString(), selectedItem.toString()) assertEquals(startYear.toString(), selectedItem.toString())
assertEquals(endYear.toString(), getItemAtPosition(count - 1).toString()) assertEquals(endYear.toString(), getItemAtPosition(count - 1).toString())
} }
assertEquals(View.GONE, view.delete_button.visibility)
} }
@Test @Test
@ -96,6 +99,17 @@ class CreditCardEditorViewTest {
} }
} }
@Test
fun `GIVEN a credit card WHEN the delete card button is clicked THEN interactor is called`() {
creditCardEditorView.bind(creditCard.toCreditCardEditorState())
assertEquals(View.VISIBLE, view.delete_button.visibility)
view.delete_button.performClick()
verify { interactor.onDeleteCardButtonClicked(creditCard.guid) }
}
@Test @Test
fun `WHEN the cancel button is clicked THEN interactor is called`() { fun `WHEN the cancel button is clicked THEN interactor is called`() {
creditCardEditorView.bind(getInitialCreditCardEditorState()) creditCardEditorView.bind(getInitialCreditCardEditorState())
@ -118,7 +132,7 @@ class CreditCardEditorViewTest {
cardNumber = creditCard.cardNumber, cardNumber = creditCard.cardNumber,
expiryMonth = creditCard.expiryMonth, expiryMonth = creditCard.expiryMonth,
expiryYear = creditCard.expiryYear, expiryYear = creditCard.expiryYear,
cardType = "amex" cardType = CARD_TYPE_PLACEHOLDER
) )
) )
} }

@ -13,6 +13,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.runBlockingTest import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.concept.storage.CreditCard
import mozilla.components.concept.storage.UpdatableCreditCardFields import mozilla.components.concept.storage.UpdatableCreditCardFields
import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage
import mozilla.components.support.test.rule.MainCoroutineRule import mozilla.components.support.test.rule.MainCoroutineRule
@ -63,6 +64,29 @@ class DefaultCreditCardEditorControllerTest {
} }
} }
@Test
fun handleDeleteCreditCard() = testCoroutineScope.runBlockingTest {
val creditCard = CreditCard(
guid = "id",
billingName = "Banana Apple",
cardNumber = "4111111111111110",
expiryMonth = 1,
expiryYear = 2030,
cardType = "amex",
timeCreated = 1L,
timeLastUsed = 1L,
timeLastModified = 1L,
timesUsed = 1L
)
controller.handleDeleteCreditCard(creditCard.guid)
coVerify {
storage.deleteCreditCard(creditCard.guid)
navController.popBackStack()
}
}
@Test @Test
fun handleSaveCreditCard() = testCoroutineScope.runBlockingTest { fun handleSaveCreditCard() = testCoroutineScope.runBlockingTest {
val creditCardFields = UpdatableCreditCardFields( val creditCardFields = UpdatableCreditCardFields(

@ -6,6 +6,7 @@ package org.mozilla.fenix.settings.creditcards
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import mozilla.components.concept.storage.CreditCard
import mozilla.components.concept.storage.UpdatableCreditCardFields import mozilla.components.concept.storage.UpdatableCreditCardFields
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -29,6 +30,24 @@ class DefaultCreditCardEditorInteractorTest {
verify { controller.handleCancelButtonClicked() } verify { controller.handleCancelButtonClicked() }
} }
@Test
fun onDeleteCardButtonClicked() {
val creditCard = CreditCard(
guid = "id",
billingName = "Banana Apple",
cardNumber = "4111111111111110",
expiryMonth = 1,
expiryYear = 2030,
cardType = "amex",
timeCreated = 1L,
timeLastUsed = 1L,
timeLastModified = 1L,
timesUsed = 1L
)
interactor.onDeleteCardButtonClicked(creditCard.guid)
verify { controller.handleDeleteCreditCard(creditCard.guid) }
}
@Test @Test
fun onSaveButtonClicked() { fun onSaveButtonClicked() {
val creditCardFields = UpdatableCreditCardFields( val creditCardFields = UpdatableCreditCardFields(

Loading…
Cancel
Save