commit 802c6f88c0d70f12baa719be3000bd72827db6e9 parent 308e56942794255aa8832bc60789b09ae6dee2e9 Author: Segun Famisa <sfamisa@mozilla.com> Date: Mon, 24 Nov 2025 13:17:18 +0000 Bug 1998200 - Remove old XML based logic and code for credit card editor feature r=android-reviewers,boek Differential Revision: https://phabricator.services.mozilla.com/D273211 Diffstat:
11 files changed, 0 insertions(+), 1321 deletions(-)
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorState.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorState.kt @@ -1,65 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards - -import mozilla.components.concept.storage.CreditCard -import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage -import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.NUMBER_OF_YEARS_TO_SHOW -import java.util.Calendar - -/** - * 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 cardNumber The credit card number to display. - * @property expiryMonth The selected credit card expiry month. - * @property expiryYears The range of expiry years to display. - * @property isEditing Whether or not the credit card is being edited. - */ -data class CreditCardEditorState( - val guid: String = "", - val billingName: String = "", - val cardNumber: String = "", - val expiryMonth: Int = 1, - val expiryYears: Pair<Int, Int>, - val isEditing: Boolean = false, -) - -/** - * Returns a [CreditCardEditorState] from the given [CreditCard]. - */ -suspend fun CreditCard.toCreditCardEditorState(storage: AutofillCreditCardsAddressesStorage): CreditCardEditorState { - val crypto = storage.getCreditCardCrypto() - val key = crypto.getOrGenerateKey() - val cardNumber = crypto.decrypt(key, encryptedCardNumber)?.number ?: "" - val startYear = expiryYear.toInt() - val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW - - return CreditCardEditorState( - guid = guid, - billingName = billingName, - cardNumber = cardNumber, - expiryMonth = expiryMonth.toInt(), - expiryYears = Pair(startYear, endYear), - isEditing = true, - ) -} - -/** - * Returns the initial credit editor state if no credit card is provided. - * - * @return an empty [CreditCardEditorState] with a range of expiry years based on the latest - * 10 years. - */ -fun getInitialCreditCardEditorState(): CreditCardEditorState { - val calendar = Calendar.getInstance() - val startYear = calendar.get(Calendar.YEAR) - val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW - - return CreditCardEditorState( - expiryYears = Pair(startYear, endYear), - ) -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardEditorController.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardEditorController.kt @@ -1,106 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards.controller - -import android.content.DialogInterface -import androidx.navigation.NavController -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import mozilla.components.concept.storage.NewCreditCardFields -import mozilla.components.concept.storage.UpdatableCreditCardFields -import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage -import mozilla.telemetry.glean.private.NoExtras -import org.mozilla.fenix.GleanMetrics.CreditCards -import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment -import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor - -/** - * [CreditCardEditorFragment] controller. An interface that handles the view manipulation of the - * credit card editor. - */ -interface CreditCardEditorController { - - /** - * @see [CreditCardEditorInteractor.onCancelButtonClicked] - */ - fun handleCancelButtonClicked() - - /** - * @see [CreditCardEditorInteractor.onDeleteCardButtonClicked] - */ - fun handleDeleteCreditCard(guid: String) - - /** - * @see [CreditCardEditorInteractor.onSaveCreditCard] - */ - fun handleSaveCreditCard(creditCardFields: NewCreditCardFields) - - /** - * @see [CreditCardEditorInteractor.onUpdateCreditCard] - */ - fun handleUpdateCreditCard(guid: String, creditCardFields: UpdatableCreditCardFields) -} - -/** - * The default implementation of [CreditCardEditorController]. - * - * @param storage An instance of the [AutofillCreditCardsAddressesStorage] for adding and retrieving - * credit cards. - * @param lifecycleScope [CoroutineScope] scope to launch coroutines. - * @param navController [NavController] used for navigation. - * @param ioDispatcher [CoroutineDispatcher] used for executing async tasks. Defaults to [Dispatchers.IO]. - * @param showDeleteDialog [DialogInterface.OnClickListener] used to display a confirmation dialog - * before removing credit card. - */ -class DefaultCreditCardEditorController( - private val storage: AutofillCreditCardsAddressesStorage, - private val lifecycleScope: CoroutineScope, - private val navController: NavController, - private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO, - private val showDeleteDialog: (DialogInterface.OnClickListener) -> Unit, -) : CreditCardEditorController { - - override fun handleCancelButtonClicked() { - navController.popBackStack() - } - - override fun handleDeleteCreditCard(guid: String) { - showDeleteDialog { dialog, _ -> - lifecycleScope.launch(ioDispatcher) { - storage.deleteCreditCard(guid) - - lifecycleScope.launch(Dispatchers.Main) { - navController.popBackStack() - } - CreditCards.deleted.add() - } - dialog.dismiss() - } - } - - override fun handleSaveCreditCard(creditCardFields: NewCreditCardFields) { - lifecycleScope.launch(ioDispatcher) { - storage.addCreditCard(creditCardFields) - - lifecycleScope.launch(Dispatchers.Main) { - navController.popBackStack() - } - CreditCards.saved.add() - } - } - - override fun handleUpdateCreditCard(guid: String, creditCardFields: UpdatableCreditCardFields) { - lifecycleScope.launch(ioDispatcher) { - storage.updateCreditCard(guid, creditCardFields) - - lifecycleScope.launch(Dispatchers.Main) { - navController.popBackStack() - } - CreditCards.modified.record(NoExtras()) - } - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardEditorInteractor.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardEditorInteractor.kt @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards.interactor - -import mozilla.components.concept.storage.NewCreditCardFields -import mozilla.components.concept.storage.UpdatableCreditCardFields -import org.mozilla.fenix.settings.creditcards.controller.CreditCardEditorController - -/** - * Interface for the credit card editor Interactor. - */ -interface CreditCardEditorInteractor { - - /** - * Navigates back to the credit card preference settings. Called when a user taps on the - * "Cancel" button. - */ - 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 - * taps on the save menu item or "Save" button. - * - * @param creditCardFields A [NewCreditCardFields] record to add. - */ - fun onSaveCreditCard(creditCardFields: NewCreditCardFields) - - /** - * Updates the provided credit card with the new credit card fields. Called when a user - * taps on the save menu item or "Save" button when editing an existing credit card. - * - * @param guid Unique identifier for the desired credit card. - * @param creditCardFields The credit card fields to update. - */ - fun onUpdateCreditCard(guid: String, creditCardFields: UpdatableCreditCardFields) -} - -/** - * The default implementation of [CreditCardEditorInteractor]. - * - * @param controller An instance of [CreditCardEditorController] which will be delegated for all - * user interactions. - */ -class DefaultCreditCardEditorInteractor( - private val controller: CreditCardEditorController, -) : CreditCardEditorInteractor { - - override fun onCancelButtonClicked() { - controller.handleCancelButtonClicked() - } - - override fun onDeleteCardButtonClicked(guid: String) { - controller.handleDeleteCreditCard(guid) - } - - override fun onSaveCreditCard(creditCardFields: NewCreditCardFields) { - controller.handleSaveCreditCard(creditCardFields) - } - - override fun onUpdateCreditCard(guid: String, creditCardFields: UpdatableCreditCardFields) { - controller.handleUpdateCreditCard(guid, creditCardFields) - } -} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardEditorView.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardEditorView.kt @@ -1,199 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards.view - -import android.content.res.ColorStateList -import android.view.View -import android.widget.ArrayAdapter -import androidx.annotation.VisibleForTesting -import mozilla.components.concept.storage.CreditCardNumber -import mozilla.components.concept.storage.NewCreditCardFields -import mozilla.components.concept.storage.UpdatableCreditCardFields -import mozilla.components.support.ktx.android.content.getColorFromAttr -import mozilla.components.support.ktx.android.view.hideKeyboard -import mozilla.components.support.utils.creditCardIIN -import org.mozilla.fenix.R -import org.mozilla.fenix.databinding.FragmentCreditCardEditorBinding -import org.mozilla.fenix.ext.toEditable -import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment -import org.mozilla.fenix.settings.creditcards.CreditCardEditorState -import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor -import org.mozilla.fenix.settings.creditcards.last4Digits -import org.mozilla.fenix.settings.creditcards.toCreditCardNumber -import org.mozilla.fenix.settings.creditcards.validateCreditCardNumber -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Locale - -/** - * Shows a credit card editor for adding or updating a credit card. - */ -class CreditCardEditorView( - private val binding: FragmentCreditCardEditorBinding, - private val interactor: CreditCardEditorInteractor, -) { - - /** - * Binds the given [CreditCardEditorState] in the [CreditCardEditorFragment]. - */ - fun bind(state: CreditCardEditorState) { - if (state.isEditing) { - binding.deleteButton.apply { - visibility = View.VISIBLE - - setOnClickListener { - interactor.onDeleteCardButtonClicked(state.guid) - } - } - } - - binding.cancelButton.setOnClickListener { - interactor.onCancelButtonClicked() - } - - binding.saveButton.setOnClickListener { - saveCreditCard(state) - } - - binding.cardNumberInput.text = state.cardNumber.toEditable() - binding.nameOnCardInput.text = state.billingName.toEditable() - - binding.cardNumberLayout.setErrorTextColor( - ColorStateList.valueOf( - binding.root.context.getColorFromAttr(R.attr.textCritical), - ), - ) - binding.nameOnCardLayout.setErrorTextColor( - ColorStateList.valueOf( - binding.root.context.getColorFromAttr(R.attr.textCritical), - ), - ) - - bindExpiryMonthDropDown(state.expiryMonth) - bindExpiryYearDropDown(state.expiryYears) - } - - /** - * Saves a new credit card or updates an existing one with data from the user input. - * - * @param state The state of the [CreditCardEditorFragment] containing the edited credit card - * information. - */ - internal fun saveCreditCard(state: CreditCardEditorState) { - binding.root.hideKeyboard() - - if (validateForm()) { - val cardNumber = binding.cardNumberInput.text.toString().toCreditCardNumber() - - if (state.isEditing) { - val fields = UpdatableCreditCardFields( - billingName = binding.nameOnCardInput.text.toString(), - cardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = cardNumber.last4Digits(), - expiryMonth = (binding.expiryMonthDropDown.selectedItemPosition + 1).toLong(), - expiryYear = binding.expiryYearDropDown.selectedItem.toString().toLong(), - cardType = cardNumber.creditCardIIN()?.creditCardIssuerNetwork?.name ?: "", - ) - interactor.onUpdateCreditCard(state.guid, fields) - } else { - val fields = NewCreditCardFields( - billingName = binding.nameOnCardInput.text.toString(), - plaintextCardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = cardNumber.last4Digits(), - expiryMonth = (binding.expiryMonthDropDown.selectedItemPosition + 1).toLong(), - expiryYear = binding.expiryYearDropDown.selectedItem.toString().toLong(), - cardType = cardNumber.creditCardIIN()?.creditCardIssuerNetwork?.name ?: "", - ) - interactor.onSaveCreditCard(fields) - } - } - } - - /** - * Validates the credit card information entered by the user. - * - * @return true if the credit card information is valid, false otherwise. - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - internal fun validateForm(): Boolean { - var isValid = true - - if (binding.cardNumberInput.text.toString().validateCreditCardNumber()) { - binding.cardNumberLayout.error = null - binding.cardNumberTitle.setTextColor(binding.root.context.getColorFromAttr(R.attr.textPrimary)) - } else { - isValid = false - - binding.cardNumberLayout.error = - binding.root.context.getString(R.string.credit_cards_number_validation_error_message_2) - binding.cardNumberTitle.setTextColor(binding.root.context.getColorFromAttr(R.attr.textCritical)) - } - - if (binding.nameOnCardInput.text.toString().isNotBlank()) { - binding.nameOnCardLayout.error = null - binding.nameOnCardTitle.setTextColor(binding.root.context.getColorFromAttr(R.attr.textPrimary)) - } else { - isValid = false - - binding.nameOnCardLayout.error = - binding.root.context.getString(R.string.credit_cards_name_on_card_validation_error_message_2) - binding.nameOnCardTitle.setTextColor(binding.root.context.getColorFromAttr(R.attr.textCritical)) - } - - return isValid - } - - /** - * Setup the expiry month dropdown by formatting and populating it with the months in a calendar - * year, and set the selection to the provided expiry month. - * - * @param expiryMonth The selected credit card expiry month to display. - */ - private fun bindExpiryMonthDropDown(expiryMonth: Int) { - val adapter = - ArrayAdapter<String>( - binding.root.context, - android.R.layout.simple_spinner_dropdown_item, - ) - val dateFormat = SimpleDateFormat("MMMM (MM)", Locale.getDefault()) - - val calendar = Calendar.getInstance() - calendar.set(Calendar.DAY_OF_MONTH, 1) - - for (month in 0..NUMBER_OF_MONTHS) { - calendar.set(Calendar.MONTH, month) - adapter.add(dateFormat.format(calendar.time)) - } - - binding.expiryMonthDropDown.adapter = adapter - binding.expiryMonthDropDown.setSelection(expiryMonth - 1) - } - - /** - * Setup the expiry year dropdown with the range specified by the provided expiryYears - * - * @param expiryYears A range specifying the start and end year to display in the expiry year - * dropdown. - */ - private fun bindExpiryYearDropDown(expiryYears: Pair<Int, Int>) { - val adapter = - ArrayAdapter<String>( - binding.root.context, - android.R.layout.simple_spinner_dropdown_item, - ) - val (startYear, endYear) = expiryYears - - for (year in startYear until endYear) { - adapter.add(year.toString()) - } - - binding.expiryYearDropDown.adapter = adapter - } - - companion object { - // Number of months in a year (0-indexed). - const val NUMBER_OF_MONTHS = 11 - } -} diff --git a/mobile/android/fenix/app/src/main/res/layout/fragment_credit_card_editor.xml b/mobile/android/fenix/app/src/main/res/layout/fragment_credit_card_editor.xml @@ -1,216 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="16dp"> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical"> - - <!-- Card Number --> - <TextView - android:id="@+id/card_number_title" - style="@style/CaptionTextStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center_vertical" - android:letterSpacing="0.05" - android:paddingStart="3dp" - android:paddingEnd="0dp" - android:text="@string/credit_cards_card_number" - android:textColor="?attr/textPrimary" - android:labelFor="@id/card_number_input" /> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/card_number_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textColor="?attr/textPrimary" - app:errorEnabled="true" - app:hintEnabled="false"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/card_number_input" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:digits="0123456789- " - android:ellipsize="end" - android:fontFamily="sans-serif" - android:imeOptions="flagNoExtractUi" - android:inputType="phone" - android:letterSpacing="0.01" - android:lineSpacingExtra="8sp" - android:maxLines="1" - android:singleLine="true" - android:textColor="?attr/textPrimary" - android:textSize="16sp" /> - - </com.google.android.material.textfield.TextInputLayout> - - <!-- Name on Card --> - <TextView - android:id="@+id/name_on_card_title" - style="@style/CaptionTextStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:gravity="center_vertical" - android:letterSpacing="0.05" - android:paddingStart="3dp" - android:paddingEnd="0dp" - android:text="@string/credit_cards_name_on_card" - android:textColor="?attr/textPrimary" - android:labelFor="@id/name_on_card_input" /> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/name_on_card_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="11dp" - android:textColor="?attr/textPrimary" - app:hintEnabled="false"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/name_on_card_input" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:ellipsize="end" - android:fontFamily="sans-serif" - android:imeOptions="flagNoExtractUi" - android:letterSpacing="0.01" - android:lineSpacingExtra="8sp" - android:maxLines="1" - android:singleLine="true" - android:textColor="?attr/textPrimary" - android:textSize="16sp" /> - - </com.google.android.material.textfield.TextInputLayout> - - <!-- Expiration Date --> - <TextView - android:id="@+id/expiration_date_title" - style="@style/CaptionTextStyle" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:gravity="center_vertical" - android:letterSpacing="0.05" - android:paddingStart="3dp" - android:paddingEnd="0dp" - android:text="@string/credit_cards_expiration_date" - android:textColor="?attr/textPrimary" /> - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:baselineAligned="false" - android:orientation="horizontal"> - - <LinearLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginEnd="24dp" - android:layout_weight="1" - android:orientation="vertical"> - - <TextView - android:layout_width="0dp" - android:layout_height="0dp" - android:labelFor="@id/expiry_month_drop_down" - android:text="@string/credit_cards_expiration_date_month" /> - - <androidx.appcompat.widget.AppCompatSpinner - android:id="@+id/expiry_month_drop_down" - android:layout_width="match_parent" - android:layout_height="wrap_content" - tools:listitem="@android:layout/simple_list_item_1" /> - - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?attr/textPrimary" /> - - </LinearLayout> - - <LinearLayout - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_weight="0.8" - android:orientation="vertical"> - - <TextView - android:layout_width="0dp" - android:layout_height="0dp" - android:labelFor="@id/expiry_year_drop_down" - android:text="@string/credit_cards_expiration_date_year" /> - - <androidx.appcompat.widget.AppCompatSpinner - android:id="@+id/expiry_year_drop_down" - android:layout_width="match_parent" - android:layout_height="wrap_content" - tools:listitem="@android:layout/simple_list_item_1" /> - - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?attr/textPrimary" /> - - </LinearLayout> - </LinearLayout> - - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingTop="16dp"> - - <com.google.android.material.button.MaterialButton - android:id="@+id/delete_button" - style="@style/Widget.MaterialComponents.Button.TextButton" - android:layout_width="wrap_content" - android:layout_height="0dp" - android:letterSpacing="0" - android:padding="10dp" - android:text="@string/credit_cards_delete_card_button" - android:textAlignment="center" - android:textAllCaps="false" - android:textColor="@color/fx_mobile_text_color_critical" - android:visibility="gone" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintStart_toStartOf="parent" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/cancel_button" - style="@style/Widget.MaterialComponents.Button.TextButton" - android:layout_width="wrap_content" - android:layout_height="0dp" - android:letterSpacing="0" - android:padding="10dp" - android:text="@string/credit_cards_cancel_button" - android:textAlignment="center" - android:textAllCaps="false" - android:textColor="?attr/textPrimary" - android:textStyle="bold" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toStartOf="@id/save_button" /> - - <com.google.android.material.button.MaterialButton - android:id="@+id/save_button" - style="@style/NeutralButton" - android:layout_width="wrap_content" - android:paddingStart="12dp" - android:paddingEnd="12dp" - android:text="@string/credit_cards_save_button" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintEnd_toEndOf="parent" /> - - </androidx.constraintlayout.widget.ConstraintLayout> - - </LinearLayout> -</ScrollView> diff --git a/mobile/android/fenix/app/src/main/res/menu/credit_card_editor.xml b/mobile/android/fenix/app/src/main/res/menu/credit_card_editor.xml @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- This Source Code Form is subject to the terms of the Mozilla Public - - License, v. 2.0. If a copy of the MPL was not distributed with this - - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> -<menu xmlns:android="http://schemas.android.com/apk/res/android" - 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="?attr/textPrimary" - app:showAsAction="ifRoom" /> - <item - android:id="@+id/save_credit_card_button" - android:icon="@drawable/mozac_ic_checkmark_24" - android:title="@string/credit_cards_menu_save" - app:iconTint="?attr/textPrimary" - app:showAsAction="ifRoom" /> -</menu> diff --git a/mobile/android/fenix/app/src/main/res/values/strings.xml b/mobile/android/fenix/app/src/main/res/values/strings.xml @@ -2535,8 +2535,6 @@ <string name="credit_cards_edit_card">Edit card</string> <!-- The header for the card number of a credit card --> <string name="credit_cards_card_number">Card Number</string> - <!-- The header for the expiration date of a credit card --> - <string name="credit_cards_expiration_date">Expiration Date</string> <!-- The label for the expiration date month of a credit card to be used by a11y services--> <string name="credit_cards_expiration_date_month">Expiration Date Month</string> <!-- The label for the expiration date year of a credit card to be used by a11y services--> diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorStateTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorStateTest.kt @@ -1,77 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards - -import io.mockk.every -import io.mockk.mockk -import kotlinx.coroutines.test.runTest -import mozilla.components.concept.storage.CreditCard -import mozilla.components.concept.storage.CreditCardNumber -import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage -import mozilla.components.service.sync.autofill.AutofillCrypto -import mozilla.components.support.utils.CreditCardNetworkType -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Test -import org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.NUMBER_OF_YEARS_TO_SHOW -import java.util.Calendar - -class CreditCardEditorStateTest { - - private val cardNumber = "4111111111111110" - private val creditCard = CreditCard( - guid = "id", - billingName = "Banana Apple", - encryptedCardNumber = CreditCardNumber.Encrypted(cardNumber), - cardNumberLast4 = "1110", - expiryMonth = 5, - expiryYear = 2030, - cardType = CreditCardNetworkType.AMEX.cardName, - timeCreated = 1L, - timeLastUsed = 1L, - timeLastModified = 1L, - timesUsed = 1L, - ) - - @Test - fun testToCreditCardEditorState() = runTest { - val storage: AutofillCreditCardsAddressesStorage = mockk(relaxed = true) - val crypto: AutofillCrypto = mockk(relaxed = true) - - every { storage.getCreditCardCrypto() } returns crypto - every { crypto.decrypt(any(), any()) } returns CreditCardNumber.Plaintext(cardNumber) - - val state = creditCard.toCreditCardEditorState(storage) - val startYear = creditCard.expiryYear.toInt() - val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW - - with(state) { - assertEquals(creditCard.guid, guid) - assertEquals(creditCard.billingName, billingName) - assertEquals(creditCard.encryptedCardNumber.number, cardNumber) - assertEquals(creditCard.expiryMonth.toInt(), expiryMonth) - assertEquals(Pair(startYear, endYear), expiryYears) - assertTrue(isEditing) - } - } - - @Test - fun testGetInitialCreditCardEditorState() { - val state = getInitialCreditCardEditorState() - val calendar = Calendar.getInstance() - val startYear = calendar.get(Calendar.YEAR) - val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW - - with(state) { - assertEquals("", guid) - assertEquals("", billingName) - assertEquals("", cardNumber) - assertEquals(1, expiryMonth) - assertEquals(Pair(startYear, endYear), expiryYears) - assertFalse(isEditing) - } - } -} diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorViewTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorViewTest.kt @@ -1,346 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards - -import android.view.LayoutInflater -import android.view.View -import io.mockk.every -import io.mockk.mockk -import io.mockk.spyk -import io.mockk.verify -import kotlinx.coroutines.test.runTest -import mozilla.components.concept.storage.CreditCard -import mozilla.components.concept.storage.CreditCardNumber -import mozilla.components.concept.storage.NewCreditCardFields -import mozilla.components.concept.storage.UpdatableCreditCardFields -import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage -import mozilla.components.service.sync.autofill.AutofillCrypto -import mozilla.components.support.ktx.android.content.getColorFromAttr -import mozilla.components.support.test.robolectric.testContext -import mozilla.components.support.utils.CreditCardNetworkType -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.fenix.R -import org.mozilla.fenix.databinding.FragmentCreditCardEditorBinding -import org.mozilla.fenix.ext.toEditable -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.view.CreditCardEditorView -import org.robolectric.RobolectricTestRunner -import java.util.Calendar - -@RunWith(RobolectricTestRunner::class) -class CreditCardEditorViewTest { - - private lateinit var view: View - private lateinit var interactor: CreditCardEditorInteractor - private lateinit var creditCardEditorView: CreditCardEditorView - private lateinit var storage: AutofillCreditCardsAddressesStorage - private lateinit var crypto: AutofillCrypto - private lateinit var fragmentCreditCardEditorBinding: FragmentCreditCardEditorBinding - - private val cardNumber = "4111111111111111" - private val creditCard = CreditCard( - guid = "id", - billingName = "Banana Apple", - encryptedCardNumber = CreditCardNumber.Encrypted(cardNumber), - cardNumberLast4 = "1111", - expiryMonth = 5, - expiryYear = 2030, - cardType = CreditCardNetworkType.VISA.cardName, - timeCreated = 1L, - timeLastUsed = 1L, - timeLastModified = 1L, - timesUsed = 1L, - ) - - @Before - fun setup() { - view = LayoutInflater.from(testContext).inflate(R.layout.fragment_credit_card_editor, null) - fragmentCreditCardEditorBinding = FragmentCreditCardEditorBinding.bind(view) - interactor = mockk(relaxed = true) - storage = mockk(relaxed = true) - crypto = mockk(relaxed = true) - - every { storage.getCreditCardCrypto() } returns crypto - every { crypto.decrypt(any(), any()) } returns CreditCardNumber.Plaintext(cardNumber) - - creditCardEditorView = spyk(CreditCardEditorView(fragmentCreditCardEditorBinding, interactor)) - } - - @Test - fun `GIVEN the initial credit card editor state THEN credit card form inputs are in initial state`() { - creditCardEditorView.bind(getInitialCreditCardEditorState()) - - val calendar = Calendar.getInstance() - val startYear = calendar.get(Calendar.YEAR) - val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW - 1 - - assertEquals("", fragmentCreditCardEditorBinding.cardNumberInput.text.toString()) - assertEquals("", fragmentCreditCardEditorBinding.nameOnCardInput.text.toString()) - - with(fragmentCreditCardEditorBinding.expiryMonthDropDown) { - assertEquals(12, count) - assertEquals("January (01)", selectedItem.toString()) - assertEquals("December (12)", getItemAtPosition(count - 1).toString()) - } - - with(fragmentCreditCardEditorBinding.expiryYearDropDown) { - assertEquals(10, count) - assertEquals(startYear.toString(), selectedItem.toString()) - assertEquals(endYear.toString(), getItemAtPosition(count - 1).toString()) - } - - assertEquals(View.GONE, fragmentCreditCardEditorBinding.deleteButton.visibility) - } - - @Test - fun `GIVEN a credit card THEN credit card form inputs are displaying the provided credit card information`() = runTest { - creditCardEditorView.bind(creditCard.toCreditCardEditorState(storage)) - - assertEquals(cardNumber, fragmentCreditCardEditorBinding.cardNumberInput.text.toString()) - assertEquals(creditCard.billingName, fragmentCreditCardEditorBinding.nameOnCardInput.text.toString()) - - with(fragmentCreditCardEditorBinding.expiryMonthDropDown) { - assertEquals(12, count) - assertEquals("May (05)", selectedItem.toString()) - } - - with(fragmentCreditCardEditorBinding.expiryYearDropDown) { - val endYear = creditCard.expiryYear + NUMBER_OF_YEARS_TO_SHOW - 1 - - assertEquals(10, count) - assertEquals(creditCard.expiryYear.toString(), selectedItem.toString()) - assertEquals(endYear.toString(), getItemAtPosition(count - 1).toString()) - } - } - - @Test - fun `GIVEN a credit card WHEN the delete card button is clicked THEN interactor is called`() = runTest { - creditCardEditorView.bind(creditCard.toCreditCardEditorState(storage)) - - assertEquals(View.VISIBLE, fragmentCreditCardEditorBinding.deleteButton.visibility) - - fragmentCreditCardEditorBinding.deleteButton.performClick() - - verify { interactor.onDeleteCardButtonClicked(creditCard.guid) } - } - - @Test - fun `WHEN the cancel button is clicked THEN interactor is called`() { - creditCardEditorView.bind(getInitialCreditCardEditorState()) - - fragmentCreditCardEditorBinding.cancelButton.performClick() - - verify { interactor.onCancelButtonClicked() } - } - - @Test - fun `GIVEN invalid credit card number WHEN the save button is clicked THEN interactor is not called`() { - creditCardEditorView.bind(getInitialCreditCardEditorState()) - - val calendar = Calendar.getInstance() - - var billingName = "Banana Apple" - val cardNumber = "2221000000000000" - val expiryMonth = 5 - val expiryYear = calendar.get(Calendar.YEAR) - - fragmentCreditCardEditorBinding.cardNumberInput.text = cardNumber.toEditable() - fragmentCreditCardEditorBinding.nameOnCardInput.text = billingName.toEditable() - fragmentCreditCardEditorBinding.expiryMonthDropDown.setSelection(expiryMonth - 1) - - fragmentCreditCardEditorBinding.saveButton.performClick() - - verify { - creditCardEditorView.validateForm() - } - - assertFalse(creditCardEditorView.validateForm()) - assertNotNull(fragmentCreditCardEditorBinding.cardNumberLayout.error) - assertEquals( - fragmentCreditCardEditorBinding.cardNumberLayout.errorCurrentTextColors, - fragmentCreditCardEditorBinding.root.context.getColorFromAttr(R.attr.textCritical), - ) - - verify(exactly = 0) { - interactor.onSaveCreditCard( - NewCreditCardFields( - billingName = billingName, - plaintextCardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = "0000", - expiryMonth = expiryMonth.toLong(), - expiryYear = expiryYear.toLong(), - cardType = CreditCardNetworkType.MASTERCARD.cardName, - ), - ) - } - - billingName = "" - fragmentCreditCardEditorBinding.nameOnCardInput.text = billingName.toEditable() - - fragmentCreditCardEditorBinding.saveButton.performClick() - - assertFalse(creditCardEditorView.validateForm()) - assertNotNull(fragmentCreditCardEditorBinding.cardNumberLayout.error) - assertEquals( - fragmentCreditCardEditorBinding.cardNumberLayout.errorCurrentTextColors, - fragmentCreditCardEditorBinding.root.context.getColorFromAttr(R.attr.textCritical), - ) - - verify(exactly = 0) { - interactor.onSaveCreditCard( - NewCreditCardFields( - billingName = billingName, - plaintextCardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = "0000", - expiryMonth = expiryMonth.toLong(), - expiryYear = expiryYear.toLong(), - cardType = CreditCardNetworkType.MASTERCARD.cardName, - ), - ) - } - } - - @Test - fun `GIVEN invalid credit card values WHEN valid values are entered and the save button is clicked THEN error messages are cleared`() { - creditCardEditorView.bind(getInitialCreditCardEditorState()) - - var billingName = "" - var cardNumber = "1234567891234567" - val expiryMonth = 5 - - fragmentCreditCardEditorBinding.cardNumberInput.text = cardNumber.toEditable() - fragmentCreditCardEditorBinding.nameOnCardInput.text = billingName.toEditable() - fragmentCreditCardEditorBinding.expiryMonthDropDown.setSelection(expiryMonth - 1) - - fragmentCreditCardEditorBinding.saveButton.performClick() - - verify { - creditCardEditorView.validateForm() - } - - billingName = "Banana Apple" - cardNumber = "2720994326581252" - fragmentCreditCardEditorBinding.nameOnCardInput.text = billingName.toEditable() - fragmentCreditCardEditorBinding.cardNumberInput.text = cardNumber.toEditable() - - fragmentCreditCardEditorBinding.saveButton.performClick() - - verify { - creditCardEditorView.validateForm() - } - - assertTrue(creditCardEditorView.validateForm()) - assertNull(fragmentCreditCardEditorBinding.cardNumberLayout.error) - assertNull(fragmentCreditCardEditorBinding.nameOnCardLayout.error) - } - - @Test - fun `GIVEN invalid name on card WHEN the save button is clicked THEN interactor is not called`() { - creditCardEditorView.bind(getInitialCreditCardEditorState()) - - val calendar = Calendar.getInstance() - - val billingName = " " - val cardNumber = "2221000000000000" - val expiryMonth = 5 - val expiryYear = calendar.get(Calendar.YEAR) - - fragmentCreditCardEditorBinding.cardNumberInput.text = cardNumber.toEditable() - fragmentCreditCardEditorBinding.nameOnCardInput.text = billingName.toEditable() - fragmentCreditCardEditorBinding.expiryMonthDropDown.setSelection(expiryMonth - 1) - - fragmentCreditCardEditorBinding.saveButton.performClick() - - verify { - creditCardEditorView.validateForm() - } - - assertFalse(creditCardEditorView.validateForm()) - assertNotNull(fragmentCreditCardEditorBinding.nameOnCardLayout.error) - assertEquals( - fragmentCreditCardEditorBinding.nameOnCardLayout.errorCurrentTextColors, - fragmentCreditCardEditorBinding.root.context.getColorFromAttr(R.attr.textCritical), - ) - - verify(exactly = 0) { - interactor.onSaveCreditCard( - NewCreditCardFields( - billingName = billingName, - plaintextCardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = "0000", - expiryMonth = expiryMonth.toLong(), - expiryYear = expiryYear.toLong(), - cardType = CreditCardNetworkType.MASTERCARD.cardName, - ), - ) - } - } - - @Test - fun `GIVEN valid credit card number WHEN the save button is clicked THEN interactor is called`() { - creditCardEditorView.bind(getInitialCreditCardEditorState()) - - val calendar = Calendar.getInstance() - - val billingName = "Banana Apple" - val cardNumber = "2720994326581252" - val expiryMonth = 5 - val expiryYear = calendar.get(Calendar.YEAR) - - fragmentCreditCardEditorBinding.cardNumberInput.text = cardNumber.toEditable() - fragmentCreditCardEditorBinding.nameOnCardInput.text = billingName.toEditable() - fragmentCreditCardEditorBinding.expiryMonthDropDown.setSelection(expiryMonth - 1) - - fragmentCreditCardEditorBinding.saveButton.performClick() - - verify { - creditCardEditorView.validateForm() - } - - assertTrue(creditCardEditorView.validateForm()) - - verify { - interactor.onSaveCreditCard( - NewCreditCardFields( - billingName = billingName, - plaintextCardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = "1252", - expiryMonth = expiryMonth.toLong(), - expiryYear = expiryYear.toLong(), - cardType = CreditCardNetworkType.MASTERCARD.cardName, - ), - ) - } - } - - @Test - fun `GIVEN a valid credit card WHEN the save button is clicked THEN interactor is called`() = runTest { - creditCardEditorView.bind(creditCard.toCreditCardEditorState(storage)) - - fragmentCreditCardEditorBinding.saveButton.performClick() - - verify { - interactor.onUpdateCreditCard( - guid = creditCard.guid, - creditCardFields = UpdatableCreditCardFields( - billingName = creditCard.billingName, - cardNumber = CreditCardNumber.Plaintext(cardNumber), - cardNumberLast4 = creditCard.cardNumberLast4, - expiryMonth = creditCard.expiryMonth, - expiryYear = creditCard.expiryYear, - cardType = creditCard.cardType, - ), - ) - } - } -} diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorControllerTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorControllerTest.kt @@ -1,134 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards - -import android.content.DialogInterface -import androidx.navigation.NavController -import io.mockk.coVerify -import io.mockk.every -import io.mockk.mockk -import io.mockk.spyk -import io.mockk.verify -import mozilla.components.concept.storage.CreditCardNumber -import mozilla.components.concept.storage.NewCreditCardFields -import mozilla.components.concept.storage.UpdatableCreditCardFields -import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStorage -import mozilla.components.support.test.robolectric.testContext -import mozilla.components.support.test.rule.MainCoroutineRule -import mozilla.components.support.test.rule.runTestOnMain -import mozilla.components.support.utils.CreditCardNetworkType -import org.junit.Assert.assertNotNull -import org.junit.Assert.assertNull -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mozilla.fenix.GleanMetrics.CreditCards -import org.mozilla.fenix.helpers.FenixGleanTestRule -import org.mozilla.fenix.settings.creditcards.controller.DefaultCreditCardEditorController -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) // for gleanTestRule -class DefaultCreditCardEditorControllerTest { - - @get:Rule - val gleanTestRule = FenixGleanTestRule(testContext) - - private val storage: AutofillCreditCardsAddressesStorage = mockk(relaxed = true) - private val navController: NavController = mockk(relaxed = true) - private val showDeleteDialog = mockk<(DialogInterface.OnClickListener) -> Unit>() - - private lateinit var controller: DefaultCreditCardEditorController - - @get:Rule - val coroutinesTestRule = MainCoroutineRule() - private val testDispatcher = coroutinesTestRule.testDispatcher - private val testCoroutineScope = coroutinesTestRule.scope - - @Before - fun setup() { - every { showDeleteDialog(any()) } answers { - firstArg<DialogInterface.OnClickListener>().onClick( - mockk(relaxed = true), - mockk(relaxed = true), - ) - } - controller = spyk( - DefaultCreditCardEditorController( - storage = storage, - lifecycleScope = testCoroutineScope, - navController = navController, - ioDispatcher = testDispatcher, - showDeleteDialog = showDeleteDialog, - ), - ) - } - - @Test - fun handleCancelButtonClicked() { - controller.handleCancelButtonClicked() - - verify { - navController.popBackStack() - } - } - - @Test - fun handleDeleteCreditCard() = runTestOnMain { - val creditCardId = "id" - assertNull(CreditCards.deleted.testGetValue()) - - controller.handleDeleteCreditCard(creditCardId) - - coVerify { - storage.deleteCreditCard(creditCardId) - navController.popBackStack() - } - assertNotNull(CreditCards.deleted.testGetValue()) - } - - @Test - fun handleSaveCreditCard() = runTestOnMain { - val creditCardFields = NewCreditCardFields( - billingName = "Banana Apple", - plaintextCardNumber = CreditCardNumber.Plaintext("4111111111111112"), - cardNumberLast4 = "1112", - expiryMonth = 1, - expiryYear = 2030, - cardType = CreditCardNetworkType.DISCOVER.cardName, - ) - assertNull(CreditCards.saved.testGetValue()) - - controller.handleSaveCreditCard(creditCardFields) - - coVerify { - storage.addCreditCard(creditCardFields) - navController.popBackStack() - } - assertNotNull(CreditCards.saved.testGetValue()) - } - - @Test - fun handleUpdateCreditCard() = runTestOnMain { - val creditCardId = "id" - val creditCardFields = UpdatableCreditCardFields( - billingName = "Banana Apple", - cardNumber = CreditCardNumber.Plaintext("4111111111111112"), - cardNumberLast4 = "1112", - expiryMonth = 1, - expiryYear = 2034, - cardType = CreditCardNetworkType.DISCOVER.cardName, - ) - assertNull(CreditCards.modified.testGetValue()) - - controller.handleUpdateCreditCard(creditCardId, creditCardFields) - - coVerify { - storage.updateCreditCard(creditCardId, creditCardFields) - navController.popBackStack() - } - assertNotNull(CreditCards.modified.testGetValue()) - } -} diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorInteractorTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorInteractorTest.kt @@ -1,83 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.fenix.settings.creditcards - -import io.mockk.mockk -import io.mockk.verify -import mozilla.components.concept.storage.CreditCard -import mozilla.components.concept.storage.CreditCardNumber -import mozilla.components.concept.storage.NewCreditCardFields -import mozilla.components.concept.storage.UpdatableCreditCardFields -import mozilla.components.support.utils.CreditCardNetworkType -import org.junit.Before -import org.junit.Test -import org.mozilla.fenix.settings.creditcards.controller.CreditCardEditorController -import org.mozilla.fenix.settings.creditcards.interactor.DefaultCreditCardEditorInteractor - -class DefaultCreditCardEditorInteractorTest { - - private val controller: CreditCardEditorController = mockk(relaxed = true) - - private lateinit var interactor: DefaultCreditCardEditorInteractor - - @Before - fun setup() { - interactor = DefaultCreditCardEditorInteractor(controller) - } - - @Test - fun onCancelButtonClicked() { - interactor.onCancelButtonClicked() - verify { controller.handleCancelButtonClicked() } - } - - @Test - fun onDeleteCardButtonClicked() { - val creditCard = CreditCard( - guid = "id", - billingName = "Banana Apple", - encryptedCardNumber = CreditCardNumber.Encrypted("4111111111111110"), - cardNumberLast4 = "1110", - expiryMonth = 1, - expiryYear = 2030, - cardType = CreditCardNetworkType.AMEX.cardName, - timeCreated = 1L, - timeLastUsed = 1L, - timeLastModified = 1L, - timesUsed = 1L, - ) - interactor.onDeleteCardButtonClicked(creditCard.guid) - verify { controller.handleDeleteCreditCard(creditCard.guid) } - } - - @Test - fun onSaveButtonClicked() { - val creditCardFields = NewCreditCardFields( - billingName = "Banana Apple", - plaintextCardNumber = CreditCardNumber.Plaintext("4111111111111112"), - cardNumberLast4 = "1112", - expiryMonth = 1, - expiryYear = 2030, - cardType = CreditCardNetworkType.DISCOVER.cardName, - ) - interactor.onSaveCreditCard(creditCardFields) - verify { controller.handleSaveCreditCard(creditCardFields) } - } - - @Test - fun onUpdateCreditCard() { - val guid = "id" - val creditCardFields = UpdatableCreditCardFields( - billingName = "Banana Apple", - cardNumber = CreditCardNumber.Encrypted("4111111111111112"), - cardNumberLast4 = "1112", - expiryMonth = 1, - expiryYear = 2034, - cardType = CreditCardNetworkType.DISCOVER.cardName, - ) - interactor.onUpdateCreditCard(guid, creditCardFields) - verify { controller.handleUpdateCreditCard(guid, creditCardFields) } - } -}