diff --git a/app/src/main/java/org/mozilla/fenix/ext/String.kt b/app/src/main/java/org/mozilla/fenix/ext/String.kt index 64052c359..19c84249e 100644 --- a/app/src/main/java/org/mozilla/fenix/ext/String.kt +++ b/app/src/main/java/org/mozilla/fenix/ext/String.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix.ext import android.graphics.Bitmap import android.graphics.BitmapFactory import android.net.Uri +import android.text.Editable import android.util.Patterns import android.webkit.URLUtil import androidx.core.net.toUri @@ -114,6 +115,11 @@ fun String.simplifiedUrl(): String { return afterScheme } +/** + * Returns an [Editable] for the provided string. + */ +fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this) + suspend fun bitmapForUrl(url: String, client: Client): Bitmap? = withContext(Dispatchers.IO) { // Code below will cache it in Gecko's cache, which ensures that as long as we've fetched it once, // we will be able to display this avatar as long as the cache isn't purged (e.g. via 'clear user data'). diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorFragment.kt index 6069da763..7adbf3cd6 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorFragment.kt @@ -9,45 +9,58 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View -import android.widget.ArrayAdapter import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.fragment_credit_card_editor.* import mozilla.components.concept.storage.UpdatableCreditCardFields import mozilla.components.support.ktx.android.view.hideKeyboard import org.mozilla.fenix.R import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.showToolbar -import org.mozilla.fenix.settings.creditcards.controller.CreditCardEditorController import org.mozilla.fenix.settings.creditcards.controller.DefaultCreditCardEditorController -import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Locale +import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor +import org.mozilla.fenix.settings.creditcards.interactor.DefaultCreditCardEditorInteractor +import org.mozilla.fenix.settings.creditcards.view.CreditCardEditorView /** * Display a credit card editor for adding and editing a credit card. */ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor) { - private lateinit var controller: CreditCardEditorController + private val args by navArgs() + + /** + * Returns true if a credit card is being edited, and false otherwise. + */ + private val isEditing: Boolean + get() = args.creditCard != null + + private lateinit var interactor: CreditCardEditorInteractor override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - showToolbar(getString(R.string.credit_cards_add_card)) - setHasOptionsMenu(true) - setupButtonClickListeners() - setupExpiryMonthDropDown(view) - setupExpiryYearDropDown(view) + if (!isEditing) { + showToolbar(getString(R.string.credit_cards_add_card)) + } else { + showToolbar(getString(R.string.credit_cards_edit_card)) + } - controller = DefaultCreditCardEditorController( - storage = requireContext().components.core.autofillStorage, - lifecycleScope = lifecycleScope, - navController = findNavController() + interactor = DefaultCreditCardEditorInteractor( + controller = DefaultCreditCardEditorController( + storage = requireContext().components.core.autofillStorage, + lifecycleScope = lifecycleScope, + navController = findNavController() + ) ) + + val creditCardEditorState = + args.creditCard?.toCreditCardEditorState() ?: getInitialCreditCardEditorState() + CreditCardEditorView(view, interactor).bind(creditCardEditorState) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -62,56 +75,6 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor) else -> false } - /** - * Setup the all button click listeners in the credit card editor. - */ - private fun setupButtonClickListeners() { - cancel_button.setOnClickListener { - findNavController().popBackStack() - } - - save_button.setOnClickListener { - saveCreditCard() - } - } - - /** - * Setup the expiry month dropdown by formatting and populating it with the months in a calendar - * year. - */ - private fun setupExpiryMonthDropDown(view: View) { - val adapter = - ArrayAdapter(view.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)) - } - - expiry_month_drop_down.adapter = adapter - } - - /** - * Setup the expiry year dropdown with the latest 10 years. - */ - private fun setupExpiryYearDropDown(view: View) { - val adapter = - ArrayAdapter(view.context, android.R.layout.simple_spinner_dropdown_item) - - val calendar = Calendar.getInstance() - val currentYear = calendar.get(Calendar.YEAR) - - for (year in currentYear until currentYear + NUMBER_OF_YEARS_TO_SHOW) { - adapter.add(year.toString()) - } - - expiry_year_drop_down.adapter = adapter - } - /** * Helper function called by the the "Save" button and menu item to save a new credit card * from the entered credit card fields. @@ -119,7 +82,7 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor) private fun saveCreditCard() { view?.hideKeyboard() - controller.handleSaveCreditCard( + interactor.onSaveButtonClicked( UpdatableCreditCardFields( billingName = name_on_card_input.text.toString(), cardNumber = card_number_input.text.toString(), @@ -131,10 +94,7 @@ class CreditCardEditorFragment : Fragment(R.layout.fragment_credit_card_editor) } companion object { - // Number of months in a year (0-indexed). - private const val NUMBER_OF_MONTHS = 11 - // Number of years to show in the expiry year dropdown. - private const val NUMBER_OF_YEARS_TO_SHOW = 10 + const val NUMBER_OF_YEARS_TO_SHOW = 10 } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorState.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorState.kt new file mode 100644 index 000000000..f7c2ce1dc --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorState.kt @@ -0,0 +1,58 @@ +/* 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 org.mozilla.fenix.settings.creditcards.CreditCardEditorFragment.Companion.NUMBER_OF_YEARS_TO_SHOW +import java.util.Calendar + +/** + * The state for the [CreditCardEditorFragment]. + * + * @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 is being edited. + */ +data class CreditCardEditorState( + val billingName: String = "", + val cardNumber: String = "", + val expiryMonth: Int = 1, + val expiryYears: Pair, + val isEditing: Boolean = false +) + +/** + * Returns a [CreditCardEditorState] from the given [CreditCard]. + */ +fun CreditCard.toCreditCardEditorState(): CreditCardEditorState { + val startYear = expiryYear.toInt() + val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW + + return CreditCardEditorState( + 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/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardEditorController.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardEditorController.kt index 1f183c6ac..7094e7018 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardEditorController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardEditorController.kt @@ -19,10 +19,12 @@ import mozilla.components.service.sync.autofill.AutofillCreditCardsAddressesStor interface CreditCardEditorController { /** - * 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 [UpdatableCreditCardFields] record to add. + * @see [CreditCardEditorInteractor.onCancelButtonClicked] + */ + fun handleCancelButtonClicked() + + /** + * @see [CreditCardEditorInteractor.onSaveButtonClicked] */ fun handleSaveCreditCard(creditCardFields: UpdatableCreditCardFields) } @@ -43,6 +45,10 @@ class DefaultCreditCardEditorController( private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO ) : CreditCardEditorController { + override fun handleCancelButtonClicked() { + navController.popBackStack() + } + override fun handleSaveCreditCard(creditCardFields: UpdatableCreditCardFields) { lifecycleScope.launch(ioDispatcher) { storage.addCreditCard(creditCardFields) diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardsManagementController.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardsManagementController.kt index 77827c553..44fe0af85 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardsManagementController.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/controller/CreditCardsManagementController.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.settings.creditcards.controller import androidx.navigation.NavController +import mozilla.components.concept.storage.CreditCard import org.mozilla.fenix.ext.navigateBlockingForAsyncNavGraph import org.mozilla.fenix.settings.creditcards.CreditCardsManagementFragmentDirections @@ -17,7 +18,7 @@ interface CreditCardsManagementController { /** * @see [CreditCardsManagementInteractor.onSelectCreditCard] */ - fun handleCreditCardClicked() + fun handleCreditCardClicked(creditCard: CreditCard) } /** @@ -27,10 +28,12 @@ class DefaultCreditCardsManagementController( private val navController: NavController ) : CreditCardsManagementController { - override fun handleCreditCardClicked() { + override fun handleCreditCardClicked(creditCard: CreditCard) { navController.navigateBlockingForAsyncNavGraph( CreditCardsManagementFragmentDirections - .actionCreditCardsManagementFragmentToCreditCardEditorFragment() + .actionCreditCardsManagementFragmentToCreditCardEditorFragment( + creditCard = creditCard + ) ) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardEditorInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardEditorInteractor.kt new file mode 100644 index 000000000..a66551d24 --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardEditorInteractor.kt @@ -0,0 +1,47 @@ +/* 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.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() + + /** + * 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 [UpdatableCreditCardFields] record to add. + */ + fun onSaveButtonClicked(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 onSaveButtonClicked(creditCardFields: UpdatableCreditCardFields) { + controller.handleSaveCreditCard(creditCardFields) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardsManagementInteractor.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardsManagementInteractor.kt index 97e125cee..251d9de8c 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardsManagementInteractor.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/interactor/CreditCardsManagementInteractor.kt @@ -4,6 +4,7 @@ package org.mozilla.fenix.settings.creditcards.interactor +import mozilla.components.concept.storage.CreditCard import org.mozilla.fenix.settings.creditcards.controller.CreditCardsManagementController /** @@ -14,18 +15,23 @@ interface CreditCardsManagementInteractor { /** * Navigates to the credit card editor to edit the selected credit card. Called when a user * taps on a credit card item. + * + * @param creditCard The selected [CreditCard] to edit. */ - fun onSelectCreditCard() + fun onSelectCreditCard(creditCard: CreditCard) } /** - * The default implementation of [CreditCardEditorInteractor] + * The default implementation of [CreditCardsManagementInteractor]. + * + * @param controller An instance of [CreditCardsManagementController] which will be delegated for + * all user interactions. */ class DefaultCreditCardsManagementInteractor( private val controller: CreditCardsManagementController ) : CreditCardsManagementInteractor { - override fun onSelectCreditCard() { - controller.handleCreditCardClicked() + override fun onSelectCreditCard(creditCard: CreditCard) { + controller.handleCreditCardClicked(creditCard) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardEditorView.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardEditorView.kt new file mode 100644 index 000000000..64d51a3df --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardEditorView.kt @@ -0,0 +1,111 @@ +/* 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.R +import android.view.View +import android.widget.ArrayAdapter +import kotlinx.android.extensions.LayoutContainer +import kotlinx.android.synthetic.main.fragment_credit_card_editor.* +import mozilla.components.concept.storage.UpdatableCreditCardFields +import mozilla.components.support.ktx.android.view.hideKeyboard +import org.mozilla.fenix.ext.toEditable +import org.mozilla.fenix.settings.creditcards.CreditCardEditorState +import org.mozilla.fenix.settings.creditcards.interactor.CreditCardEditorInteractor +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( + override val containerView: View, + private val interactor: CreditCardEditorInteractor +) : LayoutContainer { + + /** + * Binds the given [CreditCardEditorState] in the [CreditCardEditorFragment]. + */ + fun bind(state: CreditCardEditorState) { + cancel_button.setOnClickListener { + interactor.onCancelButtonClicked() + } + + save_button.setOnClickListener { + saveCreditCard() + } + + card_number_input.text = state.cardNumber.toEditable() + name_on_card_input.text = state.billingName.toEditable() + + bindExpiryMonthDropDown(state.expiryMonth) + bindExpiryYearDropDown(state.expiryYears) + } + + /** + * 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(containerView.context, 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)) + } + + expiry_month_drop_down.adapter = adapter + expiry_month_drop_down.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) { + val adapter = + ArrayAdapter(containerView.context, R.layout.simple_spinner_dropdown_item) + val (startYear, endYear) = expiryYears + + for (year in startYear until endYear) { + adapter.add(year.toString()) + } + + expiry_year_drop_down.adapter = adapter + } + + /** + * Helper function called by the the "Save" button and menu item to save a new credit card + * from the entered credit card fields. + */ + private fun saveCreditCard() { + containerView.hideKeyboard() + + interactor.onSaveButtonClicked( + UpdatableCreditCardFields( + billingName = name_on_card_input.text.toString(), + cardNumber = card_number_input.text.toString(), + expiryMonth = (expiry_month_drop_down.selectedItemPosition + 1).toLong(), + expiryYear = expiry_year_drop_down.selectedItem.toString().toLong(), + cardType = "amex" + ) + ) + } + + companion object { + // Number of months in a year (0-indexed). + const val NUMBER_OF_MONTHS = 11 + } +} diff --git a/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardItemViewHolder.kt b/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardItemViewHolder.kt index 616d4a251..b5d18ebe0 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardItemViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/creditcards/view/CreditCardItemViewHolder.kt @@ -28,7 +28,7 @@ class CreditCardItemViewHolder( bindCreditCardExpiryDate(creditCard) itemView.setOnClickListener { - interactor.onSelectCreditCard() + interactor.onSelectCreditCard(creditCard) } } diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt index 9adcee40f..36b5844db 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/fragment/EditLoginFragment.kt @@ -30,6 +30,7 @@ import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.redirectToReAuth import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.ext.toEditable import org.mozilla.fenix.settings.logins.LoginsAction import org.mozilla.fenix.settings.logins.LoginsFragmentStore import org.mozilla.fenix.settings.logins.SavedLogin @@ -45,8 +46,6 @@ import org.mozilla.fenix.settings.logins.togglePasswordReveal @Suppress("TooManyFunctions", "NestedBlockDepth", "ForbiddenComment") class EditLoginFragment : Fragment(R.layout.fragment_edit_login) { - private fun String.toEditable(): Editable = Editable.Factory.getInstance().newEditable(this) - private val args by navArgs() private lateinit var loginsFragmentStore: LoginsFragmentStore private lateinit var interactor: EditLoginInteractor diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index 4bb00e985..39d39f045 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -1065,7 +1065,13 @@ + android:label="@string/credit_cards_add_card"> + + Manage saved cards Add card + + Edit card Card Number diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorStateTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorStateTest.kt new file mode 100644 index 000000000..5f976bfe2 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorStateTest.kt @@ -0,0 +1,60 @@ +/* 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 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 creditCard = CreditCard( + guid = "id", + billingName = "Banana Apple", + cardNumber = "4111111111111110", + expiryMonth = 5, + expiryYear = 2030, + cardType = "amex", + timeCreated = 1L, + timeLastUsed = 1L, + timeLastModified = 1L, + timesUsed = 1L + ) + + @Test + fun testToCreditCardEditorState() { + val state = creditCard.toCreditCardEditorState() + val startYear = creditCard.expiryYear.toInt() + val endYear = startYear + NUMBER_OF_YEARS_TO_SHOW + + with(state) { + assertEquals(creditCard.billingName, billingName) + assertEquals(creditCard.cardNumber, 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("", billingName) + assertEquals("", cardNumber) + assertEquals(1, expiryMonth) + assertEquals(Pair(startYear, endYear), expiryYears) + assertFalse(isEditing) + } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorViewTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorViewTest.kt new file mode 100644 index 000000000..ac88a4384 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardEditorViewTest.kt @@ -0,0 +1,126 @@ +/* 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.mockk +import io.mockk.verify +import kotlinx.android.synthetic.main.fragment_credit_card_editor.view.* +import mozilla.components.concept.storage.CreditCard +import mozilla.components.concept.storage.UpdatableCreditCardFields +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +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 java.util.Calendar + +@RunWith(FenixRobolectricTestRunner::class) +class CreditCardEditorViewTest { + + private lateinit var view: View + private lateinit var interactor: CreditCardEditorInteractor + private lateinit var creditCardEditorView: CreditCardEditorView + + private val creditCard = CreditCard( + guid = "id", + billingName = "Banana Apple", + cardNumber = "4111111111111110", + expiryMonth = 5, + expiryYear = 2030, + cardType = "amex", + timeCreated = 1L, + timeLastUsed = 1L, + timeLastModified = 1L, + timesUsed = 1L + ) + + @Before + fun setup() { + view = LayoutInflater.from(testContext).inflate(R.layout.fragment_credit_card_editor, null) + interactor = mockk(relaxed = true) + + creditCardEditorView = CreditCardEditorView(view, 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("", view.card_number_input.text.toString()) + assertEquals("", view.name_on_card_input.text.toString()) + + with(view.expiry_month_drop_down) { + assertEquals(12, count) + assertEquals("January (01)", selectedItem.toString()) + assertEquals("December (12)", getItemAtPosition(count - 1).toString()) + } + + with(view.expiry_year_drop_down) { + assertEquals(10, count) + assertEquals(startYear.toString(), selectedItem.toString()) + assertEquals(endYear.toString(), getItemAtPosition(count - 1).toString()) + } + } + + @Test + fun `GIVEN a credit card THEN credit card form inputs are displaying the provided credit card information`() { + creditCardEditorView.bind(creditCard.toCreditCardEditorState()) + + assertEquals(creditCard.cardNumber, view.card_number_input.text.toString()) + assertEquals(creditCard.billingName, view.name_on_card_input.text.toString()) + + with(view.expiry_month_drop_down) { + assertEquals(12, count) + assertEquals("May (05)", selectedItem.toString()) + } + + with(view.expiry_year_drop_down) { + 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 `WHEN the cancel button is clicked THEN interactor is called`() { + creditCardEditorView.bind(getInitialCreditCardEditorState()) + + view.cancel_button.performClick() + + verify { interactor.onCancelButtonClicked() } + } + + @Test + fun `GIVEN a credit card WHEN the save button is clicked THEN interactor is called`() { + creditCardEditorView.bind(creditCard.toCreditCardEditorState()) + + view.save_button.performClick() + + verify { + interactor.onSaveButtonClicked( + UpdatableCreditCardFields( + billingName = creditCard.billingName, + cardNumber = creditCard.cardNumber, + expiryMonth = creditCard.expiryMonth, + expiryYear = creditCard.expiryYear, + cardType = "amex" + ) + ) + } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardItemViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardItemViewHolderTest.kt index 546804102..81c6077ab 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardItemViewHolderTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/CreditCardItemViewHolderTest.kt @@ -57,6 +57,6 @@ class CreditCardItemViewHolderTest { CreditCardItemViewHolder(view, interactor).bind(creditCard) view.performClick() - verify { interactor.onSelectCreditCard() } + verify { interactor.onSelectCreditCard(creditCard) } } } diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorControllerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorControllerTest.kt index 440be4511..a47436a15 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorControllerTest.kt @@ -8,6 +8,7 @@ import androidx.navigation.NavController import io.mockk.coVerify import io.mockk.mockk import io.mockk.spyk +import io.mockk.verify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestCoroutineDispatcher import kotlinx.coroutines.test.TestCoroutineScope @@ -53,6 +54,15 @@ class DefaultCreditCardEditorControllerTest { testDispatcher.cleanupTestCoroutines() } + @Test + fun handleCancelButtonClicked() { + controller.handleCancelButtonClicked() + + verify { + navController.popBackStack() + } + } + @Test fun handleSaveCreditCard() = testCoroutineScope.runBlockingTest { val creditCardFields = UpdatableCreditCardFields( diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorInteractorTest.kt new file mode 100644 index 000000000..3a441651b --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardEditorInteractorTest.kt @@ -0,0 +1,44 @@ +/* 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.UpdatableCreditCardFields +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 onSaveButtonClicked() { + val creditCardFields = UpdatableCreditCardFields( + billingName = "Banana Apple", + cardNumber = "4111111111111112", + expiryMonth = 1, + expiryYear = 2030, + cardType = "discover" + ) + interactor.onSaveButtonClicked(creditCardFields) + verify { controller.handleSaveCreditCard(creditCardFields) } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementControllerTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementControllerTest.kt index 40097f675..e261741d1 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementControllerTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementControllerTest.kt @@ -8,6 +8,7 @@ import androidx.navigation.NavController import io.mockk.mockk import io.mockk.spyk import io.mockk.verify +import mozilla.components.concept.storage.CreditCard import org.junit.Before import org.junit.Rule import org.junit.Test @@ -34,12 +35,27 @@ class DefaultCreditCardsManagementControllerTest { @Test fun handleCreditCardClicked() { - controller.handleCreditCardClicked() + 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.handleCreditCardClicked(creditCard) verify { navController.navigate( CreditCardsManagementFragmentDirections - .actionCreditCardsManagementFragmentToCreditCardEditorFragment() + .actionCreditCardsManagementFragmentToCreditCardEditorFragment( + creditCard = creditCard + ) ) } } diff --git a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementInteractorTest.kt b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementInteractorTest.kt index 46173ad53..c6883f7f0 100644 --- a/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementInteractorTest.kt +++ b/app/src/test/java/org/mozilla/fenix/settings/creditcards/DefaultCreditCardsManagementInteractorTest.kt @@ -6,6 +6,7 @@ package org.mozilla.fenix.settings.creditcards import io.mockk.mockk import io.mockk.verify +import mozilla.components.concept.storage.CreditCard import org.junit.Before import org.junit.Test import org.mozilla.fenix.settings.creditcards.controller.CreditCardsManagementController @@ -24,7 +25,8 @@ class DefaultCreditCardsManagementInteractorTest { @Test fun onSelectCreditCard() { - interactor.onSelectCreditCard() - verify { controller.handleCreditCardClicked() } + val creditCard: CreditCard = mockk(relaxed = true) + interactor.onSelectCreditCard(creditCard) + verify { controller.handleCreditCardClicked(creditCard) } } }