diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt index 2be84db9500..588fa601d9c 100644 --- a/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/action/BrowserAction.kt @@ -382,7 +382,8 @@ sealed class ContentAction : BrowserAction() { /** * Removes the [PromptRequest] of the [ContentState] with the given [sessionId]. */ - data class ConsumePromptRequestAction(val sessionId: String) : ContentAction() + data class ConsumePromptRequestAction(val sessionId: String, val promptRequest: PromptRequest) : + ContentAction() /** * Adds a [FindResultState] to the [ContentState] with the given [sessionId]. diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt index c0ff9c86005..e51dd762ac2 100644 --- a/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/reducer/ContentStateReducer.kt @@ -78,10 +78,10 @@ internal object ContentStateReducer { it.copy(hitResult = null) } is ContentAction.UpdatePromptRequestAction -> updateContentState(state, action.sessionId) { - it.copy(promptRequest = action.promptRequest) + it.copy(promptRequests = it.promptRequests + action.promptRequest) } is ContentAction.ConsumePromptRequestAction -> updateContentState(state, action.sessionId) { - it.copy(promptRequest = null) + it.copy(promptRequests = it.promptRequests - action.promptRequest) } is ContentAction.AddFindResultAction -> updateContentState(state, action.sessionId) { it.copy(findResults = it.findResults + action.findResult) diff --git a/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt b/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt index a8c62c1ea4c..ea7b14534be 100644 --- a/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt +++ b/components/browser/state/src/main/java/mozilla/components/browser/state/state/ContentState.kt @@ -36,7 +36,7 @@ import mozilla.components.concept.engine.window.WindowRequest * @property download Last unhandled download request. * @property share Last unhandled request to share an internet resource that first needs to be downloaded. * @property hitResult the target of the latest long click operation. - * @property promptRequest the last received [PromptRequest]. + * @property promptRequests current[PromptRequest]s. * @property findResults the list of results of the latest "find in page" operation. * @property windowRequest the last received [WindowRequest]. * @property searchRequest the last received [SearchRequest] @@ -74,7 +74,7 @@ data class ContentState( val download: DownloadState? = null, val share: ShareInternetResourceState? = null, val hitResult: HitResult? = null, - val promptRequest: PromptRequest? = null, + val promptRequests: List = emptyList(), val findResults: List = emptyList(), val windowRequest: WindowRequest? = null, val searchRequest: SearchRequest? = null, diff --git a/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt b/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt index b47837f48c7..27687c11624 100644 --- a/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt +++ b/components/browser/state/src/test/java/mozilla/components/browser/state/action/ContentActionTest.kt @@ -470,8 +470,8 @@ class ContentActionTest { } @Test - fun `UpdatePromptRequestAction updates request`() { - assertNull(tab.content.promptRequest) + fun `UpdatePromptRequestAction updates requests`() { + assertTrue(tab.content.promptRequests.isEmpty()) val promptRequest1: PromptRequest = mock() @@ -479,7 +479,8 @@ class ContentActionTest { ContentAction.UpdatePromptRequestAction(tab.id, promptRequest1) ).joinBlocking() - assertEquals(promptRequest1, tab.content.promptRequest) + assertEquals(1, tab.content.promptRequests.size) + assertEquals(promptRequest1, tab.content.promptRequests[0]) val promptRequest2: PromptRequest = mock() @@ -487,7 +488,9 @@ class ContentActionTest { ContentAction.UpdatePromptRequestAction(tab.id, promptRequest2) ).joinBlocking() - assertEquals(promptRequest2, tab.content.promptRequest) + assertEquals(2, tab.content.promptRequests.size) + assertEquals(promptRequest1, tab.content.promptRequests[0]) + assertEquals(promptRequest2, tab.content.promptRequests[1]) } @Test @@ -498,13 +501,14 @@ class ContentActionTest { ContentAction.UpdatePromptRequestAction(tab.id, promptRequest) ).joinBlocking() - assertEquals(promptRequest, tab.content.promptRequest) + assertEquals(1, tab.content.promptRequests.size) + assertEquals(promptRequest, tab.content.promptRequests[0]) store.dispatch( - ContentAction.ConsumePromptRequestAction(tab.id) + ContentAction.ConsumePromptRequestAction(tab.id, promptRequest) ).joinBlocking() - assertNull(tab.content.promptRequest) + assertTrue(tab.content.promptRequests.isEmpty()) } @Test @@ -722,7 +726,7 @@ class ContentActionTest { @Test fun `UpdateAppIntentAction updates request`() { - assertNull(tab.content.promptRequest) + assertTrue(tab.content.promptRequests.isEmpty()) val appIntent1: AppIntentState = mock() diff --git a/components/concept/engine/src/main/java/mozilla/components/concept/engine/prompt/PromptRequest.kt b/components/concept/engine/src/main/java/mozilla/components/concept/engine/prompt/PromptRequest.kt index 49d36b0cb8d..7a42e1bb6e8 100644 --- a/components/concept/engine/src/main/java/mozilla/components/concept/engine/prompt/PromptRequest.kt +++ b/components/concept/engine/src/main/java/mozilla/components/concept/engine/prompt/PromptRequest.kt @@ -6,15 +6,22 @@ package mozilla.components.concept.engine.prompt import android.content.Context import android.net.Uri -import mozilla.components.concept.storage.Login import mozilla.components.concept.engine.prompt.PromptRequest.Authentication.Level import mozilla.components.concept.engine.prompt.PromptRequest.Authentication.Method import mozilla.components.concept.engine.prompt.PromptRequest.TimeSelection.Type +import mozilla.components.concept.storage.Login +import java.util.UUID /** * Value type that represents a request for showing a native dialog for prompt web content. + * + * @param shouldDismissOnLoad Whether or not the dialog should automatically be dismissed when a new page is loaded. + * @param uid [PromptRequest] unique identifier. Ignored when comparing if [PromptRequest] subclasses are equals. */ -sealed class PromptRequest { +sealed class PromptRequest( + val shouldDismissOnLoad: Boolean = true, + val uid: String = UUID.randomUUID().toString() +) { /** * Value type that represents a request for a single choice prompt. * @property choices All the possible options. diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptFeature.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptFeature.kt index 0f0522f8efc..49a6a7fe658 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptFeature.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptFeature.kt @@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.map import mozilla.components.browser.state.action.ContentAction import mozilla.components.browser.state.selector.findTabOrCustomTab import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab -import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.state.SessionState import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.prompt.Choice @@ -267,11 +266,11 @@ class PromptFeature private constructor( handlePromptScope = store.flowScoped { flow -> flow.map { state -> state.findTabOrCustomTabOrSelectedTab(customTabId) } .ifAnyChanged { - arrayOf(it?.content?.promptRequest, it?.content?.loading) + arrayOf(it?.content?.promptRequests, it?.content?.loading) } .collect { state -> state?.content?.let { - if (it.promptRequest != activePromptRequest) { + if (it.promptRequests.lastOrNull() != activePromptRequest) { // Dismiss any active select login or credit card prompt if it does // not match the current prompt request for the session. if (activePromptRequest is SelectLoginPrompt) { @@ -289,7 +288,7 @@ class PromptFeature private constructor( dismissSelectPrompts() } - activePromptRequest = it.promptRequest + activePromptRequest = it.promptRequests.lastOrNull() } } } @@ -305,10 +304,12 @@ class PromptFeature private constructor( dismissSelectPrompts() val prompt = activePrompt?.get() - if (prompt?.shouldDismissOnLoad() == true) { - prompt.dismiss() - } - activePrompt?.clear() + store.consumeAllSessionPrompts( + sessionId = prompt?.sessionId, + activePrompt, + predicate = { it.shouldDismissOnLoad }, + consume = { prompt?.dismiss() } + ) } } @@ -364,7 +365,7 @@ class PromptFeature private constructor( @VisibleForTesting(otherwise = PRIVATE) internal fun onPromptRequested(session: SessionState) { // Some requests are handle with intents - session.content.promptRequest?.let { promptRequest -> + session.content.promptRequests.lastOrNull()?.let { promptRequest -> when (promptRequest) { is File -> filePicker.handleFileRequest(promptRequest) is Share -> handleShareRequest(promptRequest, session) @@ -388,10 +389,11 @@ class PromptFeature private constructor( * value from the session indicated by [sessionId]. * * @param sessionId this is the id of the session which requested the prompt. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog was shown. * @param value an optional value provided by the dialog as a result of canceling the action. */ - override fun onCancel(sessionId: String, value: Any?) { - store.consumePromptFrom(sessionId, activePrompt) { + override fun onCancel(sessionId: String, promptRequestUID: String, value: Any?) { + store.consumePromptFrom(sessionId, promptRequestUID, activePrompt) { when (it) { is BeforeUnload -> it.onStay() is Popup -> { @@ -409,11 +411,12 @@ class PromptFeature private constructor( * the [PromptFeature] value from the [SessionState] indicated by [sessionId]. * * @param sessionId that requested to show the dialog. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog was shown. * @param value an optional value provided by the dialog as a result of confirming the action. */ @Suppress("UNCHECKED_CAST", "ComplexMethod") - override fun onConfirm(sessionId: String, value: Any?) { - store.consumePromptFrom(sessionId, activePrompt) { + override fun onConfirm(sessionId: String, promptRequestUID: String, value: Any?) { + store.consumePromptFrom(sessionId, promptRequestUID, activePrompt) { try { when (it) { is TimeSelection -> it.onConfirm(value as Date) @@ -479,9 +482,10 @@ class PromptFeature private constructor( * This consumes the [PromptFeature] value from the [SessionState] indicated by [sessionId]. * * @param sessionId that requested to show the dialog. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog was shown. */ - override fun onClear(sessionId: String) { - store.consumePromptFrom(sessionId, activePrompt) { + override fun onClear(sessionId: String, promptRequestUID: String) { + store.consumePromptFrom(sessionId, promptRequestUID, activePrompt) { when (it) { is TimeSelection -> it.onClear() } @@ -493,7 +497,7 @@ class PromptFeature private constructor( */ private fun reattachFragment(fragment: PromptDialogFragment) { val session = store.state.findTabOrCustomTab(fragment.sessionId) - if (session?.content?.promptRequest == null) { + if (session?.content?.promptRequests?.isEmpty() != false) { fragmentManager.beginTransaction() .remove(fragment) .commitAllowingStateLoss() @@ -508,8 +512,8 @@ class PromptFeature private constructor( shareDelegate.showShareSheet( context = container.context, shareData = promptRequest.data, - onDismiss = { onCancel(session.id) }, - onSuccess = { onConfirm(session.id, null) } + onDismiss = { onCancel(session.id, promptRequest.uid) }, + onSuccess = { onConfirm(session.id, promptRequest.uid, null) } ) } @@ -536,6 +540,8 @@ class PromptFeature private constructor( SaveLoginDialogFragment.newInstance( sessionId = session.id, + promptRequestUID = promptRequest.uid, + shouldDismissOnLoad = false, hint = promptRequest.hint, // For v1, we only handle a single login and drop all others on the floor login = promptRequest.logins[0], @@ -545,21 +551,26 @@ class PromptFeature private constructor( is SingleChoice -> ChoiceDialogFragment.newInstance( promptRequest.choices, - session.id, SINGLE_CHOICE_DIALOG_TYPE + session.id, + promptRequest.uid, + true, + SINGLE_CHOICE_DIALOG_TYPE ) is MultipleChoice -> ChoiceDialogFragment.newInstance( - promptRequest.choices, session.id, MULTIPLE_CHOICE_DIALOG_TYPE + promptRequest.choices, session.id, promptRequest.uid, true, MULTIPLE_CHOICE_DIALOG_TYPE ) is MenuChoice -> ChoiceDialogFragment.newInstance( - promptRequest.choices, session.id, MENU_CHOICE_DIALOG_TYPE + promptRequest.choices, session.id, promptRequest.uid, true, MENU_CHOICE_DIALOG_TYPE ) is Alert -> { with(promptRequest) { AlertDialogFragment.newInstance( session.id, + promptRequest.uid, + true, title, message, promptAbuserDetector.areDialogsBeingAbused() @@ -578,6 +589,8 @@ class PromptFeature private constructor( with(promptRequest) { TimePickerDialogFragment.newInstance( session.id, + promptRequest.uid, + true, initialDate, minimumDate, maximumDate, @@ -590,6 +603,8 @@ class PromptFeature private constructor( with(promptRequest) { TextPromptDialogFragment.newInstance( session.id, + promptRequest.uid, + true, title, inputLabel, inputValue, @@ -602,6 +617,8 @@ class PromptFeature private constructor( with(promptRequest) { AuthenticationDialogFragment.newInstance( session.id, + promptRequest.uid, + true, title, message, userName, @@ -614,6 +631,8 @@ class PromptFeature private constructor( is Color -> ColorPickerDialogFragment.newInstance( session.id, + promptRequest.uid, + true, promptRequest.defaultColor ) @@ -624,11 +643,13 @@ class PromptFeature private constructor( ConfirmDialogFragment.newInstance( sessionId = session.id, + promptRequest.uid, title = title, message = promptRequest.targetUri, positiveButtonText = positiveLabel, negativeButtonText = negativeLabel, - hasShownManyDialogs = promptAbuserDetector.areDialogsBeingAbused() + hasShownManyDialogs = promptAbuserDetector.areDialogsBeingAbused(), + shouldDismissOnLoad = true ) } is BeforeUnload -> { @@ -643,10 +664,12 @@ class PromptFeature private constructor( ConfirmDialogFragment.newInstance( sessionId = session.id, + promptRequest.uid, title = title, message = body, positiveButtonText = leaveLabel, - negativeButtonText = stayLabel + negativeButtonText = stayLabel, + shouldDismissOnLoad = true ) } @@ -665,6 +688,7 @@ class PromptFeature private constructor( MultiButtonDialogFragment.newInstance( session.id, + promptRequest.uid, title, message, promptAbuserDetector.areDialogsBeingAbused(), @@ -687,6 +711,8 @@ class PromptFeature private constructor( ConfirmDialogFragment.newInstance( sessionId = session.id, + promptRequestUID = promptRequest.uid, + shouldDismissOnLoad = true, title = title, message = message, positiveButtonText = positiveAction, @@ -704,7 +730,7 @@ class PromptFeature private constructor( activePrompt = WeakReference(dialog) } else { (promptRequest as Dismissible).onDismiss() - store.dispatch(ContentAction.ConsumePromptRequestAction(session.id)) + store.dispatch(ContentAction.ConsumePromptRequestAction(session.id, promptRequest)) } promptAbuserDetector.updateJSDialogAbusedState() } @@ -758,20 +784,80 @@ class PromptFeature private constructor( } } +/** + * Removes the [PromptRequest] indicated by [promptRequestUID] from the current Session if it it exists + * and offers a [consume] callback for other optional side effects. + * + * @param sessionId Session id of the tab or custom tab in which to try consuming [PromptRequests]. + * If the id is not provided or a tab with that id is not found the method will act on the current tab. + * @param promptRequestUID Id of the [PromptRequest] to be consumed. + * @param activePrompt The current active Prompt if known. If provided it will always be cleared, + * irrespective of if [PromptRequest] indicated by [promptRequestUID] is found and removed or not. + * @param consume callback with the [PromptRequest] if found, before being removed from the Session. + */ internal fun BrowserStore.consumePromptFrom( sessionId: String?, + promptRequestUID: String, activePrompt: WeakReference? = null, consume: (PromptRequest) -> Unit ) { - if (sessionId == null) { - state.selectedTab - } else { - state.findTabOrCustomTabOrSelectedTab(sessionId) - }?.let { tab -> + state.findTabOrCustomTabOrSelectedTab(sessionId)?.let { tab -> activePrompt?.clear() - tab.content.promptRequest?.let { + tab.content.promptRequests.firstOrNull { it.uid == promptRequestUID }?.let { consume(it) - dispatch(ContentAction.ConsumePromptRequestAction(tab.id)) + dispatch(ContentAction.ConsumePromptRequestAction(tab.id, it)) + } + } +} + +/** + * Removes the most recent [PromptRequest] of type [P] from the current Session if it it exists + * and offers a [consume] callback for other optional side effects. + * + * @param sessionId Session id of the tab or custom tab in which to try consuming [PromptRequests]. + * If the id is not provided or a tab with that id is not found the method will act on the current tab. + * @param activePrompt The current active Prompt if known. If provided it will always be cleared, + * irrespective of if [PromptRequest] indicated by [promptRequestUID] is found and removed or not. + * @param consume callback with the [PromptRequest] if found, before being removed from the Session. + */ +internal inline fun BrowserStore.consumePromptFrom( + sessionId: String?, + activePrompt: WeakReference? = null, + consume: (P) -> Unit +) { + state.findTabOrCustomTabOrSelectedTab(sessionId)?.let { tab -> + activePrompt?.clear() + tab.content.promptRequests.lastOrNull { it is P }?.let { + consume(it as P) + dispatch(ContentAction.ConsumePromptRequestAction(tab.id, it)) } } } + +/** + * Filters and removes all [PromptRequest]s from the current Session if it it exists + * and offers a [consume] callback for other optional side effects on each filtered [PromptRequest]. + * + * @param sessionId Session id of the tab or custom tab in which to try consuming [PromptRequests]. + * If the id is not provided or a tab with that id is not found the method will act on the current tab. + * @param activePrompt The current active Prompt if known. If provided it will always be cleared, + * irrespective of if [PromptRequest] indicated by [promptRequestUID] is found and removed or not. + * @param predicate function allowing matching only specific [PromptRequest]s from all contained in the Session. + * @param consume callback with the [PromptRequest] if found, before being removed from the Session. + */ +internal fun BrowserStore.consumeAllSessionPrompts( + sessionId: String?, + activePrompt: WeakReference? = null, + predicate: (PromptRequest) -> Boolean, + consume: (PromptRequest) -> Unit = { } +) { + state.findTabOrCustomTabOrSelectedTab(sessionId)?.let { tab -> + activePrompt?.clear() + tab.content.promptRequests + .filter { predicate(it) } + .forEach { + consume(it) + dispatch(ContentAction.ConsumePromptRequestAction(tab.id, it)) + } + } +} diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptMiddleware.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptMiddleware.kt index 4bd05ee5467..d99eb548dd4 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptMiddleware.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/PromptMiddleware.kt @@ -43,7 +43,7 @@ class PromptMiddleware : Middleware { ): Boolean { if (action.promptRequest is PromptRequest.Popup) { context.state.findTab(action.sessionId)?.let { - if (it.content.promptRequest is PromptRequest.Popup) { + if (it.content.promptRequests.lastOrNull { prompt -> prompt is PromptRequest.Popup } != null) { scope.launch { (action.promptRequest as PromptRequest.Popup).onDeny() } diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/creditcard/CreditCardPicker.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/creditcard/CreditCardPicker.kt index 2314a16072a..442c1e55b33 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/creditcard/CreditCardPicker.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/creditcard/CreditCardPicker.kt @@ -39,8 +39,8 @@ class CreditCardPicker( } override fun onOptionSelect(option: CreditCard) { - store.consumePromptFrom(sessionId) { - if (it is PromptRequest.SelectCreditCard) it.onConfirm(option) + store.consumePromptFrom(sessionId) { + it.onConfirm(option) } creditCardSelectBar.hidePrompt() @@ -62,8 +62,8 @@ class CreditCardPicker( return } - store.consumePromptFrom(sessionId) { - if (it is PromptRequest.SelectCreditCard) it.onDismiss() + store.consumePromptFrom(sessionId) { + it.onDismiss() } } catch (e: RuntimeException) { Logger.error("Can't dismiss this select credit card prompt", e) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AlertDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AlertDialogFragment.kt index 566ddb186dc..5ad4728caab 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AlertDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AlertDialogFragment.kt @@ -28,14 +28,14 @@ internal class AlertDialogFragment : AbstractPromptTextDialogFragment() { override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } private fun onPositiveClickAction() { if (!userSelectionNoMoreDialogs) { - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } else { - feature?.onConfirm(sessionId, userSelectionNoMoreDialogs) + feature?.onConfirm(sessionId, promptRequestUID, userSelectionNoMoreDialogs) } } @@ -43,14 +43,20 @@ internal class AlertDialogFragment : AbstractPromptTextDialogFragment() { /** * A builder method for creating a [AlertDialogFragment] * @param sessionId to create the dialog. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog is shown. + * @param shouldDismissOnLoad whether or not the dialog should automatically be dismissed + * when a new page is loaded. * @param title the title of the dialog. * @param message the message of the dialog. * @param hasShownManyDialogs tells if this [sessionId] has shown many dialogs * in a short period of time, if is true a checkbox will be part of the dialog, for the user * to choose if wants to prevent this [sessionId] continuing showing dialogs. */ + @Suppress("LongParameterList") fun newInstance( sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, title: String, message: String, hasShownManyDialogs: Boolean @@ -61,6 +67,8 @@ internal class AlertDialogFragment : AbstractPromptTextDialogFragment() { with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putString(KEY_TITLE, title) putString(KEY_MESSAGE, message) putBoolean(KEY_MANY_ALERTS, hasShownManyDialogs) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragment.kt index a1718c4d5d0..b549a57b7d4 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragment.kt @@ -57,7 +57,7 @@ internal class AuthenticationDialogFragment : PromptDialogFragment() { .setMessage(message) .setCancelable(true) .setNegativeButton(R.string.mozac_feature_prompts_cancel) { _, _ -> - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } .setPositiveButton(android.R.string.ok) { _, _ -> onPositiveClickAction() @@ -67,11 +67,11 @@ internal class AuthenticationDialogFragment : PromptDialogFragment() { override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } private fun onPositiveClickAction() { - feature?.onConfirm(sessionId, username to password) + feature?.onConfirm(sessionId, promptRequestUID, username to password) } @SuppressLint("InflateParams") @@ -129,6 +129,9 @@ internal class AuthenticationDialogFragment : PromptDialogFragment() { /** * A builder method for creating a [AuthenticationDialogFragment] * @param sessionId the id of the session for which this dialog will be created. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog is shown. + * @param shouldDismissOnLoad whether or not the dialog should automatically be dismissed + * when a new page is loaded. * @param title the title of the dialog. * @param message the text that will go below title. * @param username the default value of the username text field. @@ -138,6 +141,8 @@ internal class AuthenticationDialogFragment : PromptDialogFragment() { @Suppress("LongParameterList") fun newInstance( sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, title: String, message: String, username: String, @@ -151,6 +156,8 @@ internal class AuthenticationDialogFragment : PromptDialogFragment() { with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putString(KEY_TITLE, title) putString(KEY_MESSAGE, message) putBoolean(KEY_ONLY_SHOW_PASSWORD, onlyShowPassword) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragment.kt index 9339ecadfb9..bd2431711ef 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragment.kt @@ -55,6 +55,8 @@ internal class ChoiceDialogFragment : PromptDialogFragment() { fun newInstance( choices: Array, sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, dialogType: Int ): ChoiceDialogFragment { val fragment = ChoiceDialogFragment() @@ -63,6 +65,8 @@ internal class ChoiceDialogFragment : PromptDialogFragment() { with(arguments) { putParcelableArray(KEY_CHOICES, choices) putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putInt(KEY_DIALOG_TYPE, dialogType) } @@ -90,13 +94,13 @@ internal class ChoiceDialogFragment : PromptDialogFragment() { } fun onSelect(selectedChoice: Choice) { - feature?.onConfirm(sessionId, selectedChoice) + feature?.onConfirm(sessionId, promptRequestUID, selectedChoice) dismiss() } override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } private fun createSingleChoiceDialog(): AlertDialog { @@ -106,7 +110,7 @@ internal class ChoiceDialogFragment : PromptDialogFragment() { return builder.setView(view) .setOnDismissListener { - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) }.create() } @@ -117,12 +121,12 @@ internal class ChoiceDialogFragment : PromptDialogFragment() { return builder.setView(view) .setNegativeButton(R.string.mozac_feature_prompts_cancel) { _, _ -> - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } .setPositiveButton(R.string.mozac_feature_prompts_ok) { _, _ -> - feature?.onConfirm(sessionId, mapSelectChoice.keys.toTypedArray()) + feature?.onConfirm(sessionId, promptRequestUID, mapSelectChoice.keys.toTypedArray()) }.setOnDismissListener { - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) }.create() } } diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragment.kt index 4f62b42ce54..3a06d4b9b3d 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragment.kt @@ -55,8 +55,9 @@ internal class ColorPickerDialogFragment : PromptDialogFragment(), DialogInterfa override fun onClick(dialog: DialogInterface?, which: Int) { when (which) { - DialogInterface.BUTTON_POSITIVE -> feature?.onConfirm(sessionId, selectedColor.toHexColor()) - DialogInterface.BUTTON_NEGATIVE -> feature?.onCancel(sessionId) + DialogInterface.BUTTON_POSITIVE -> + feature?.onConfirm(sessionId, promptRequestUID, selectedColor.toHexColor()) + DialogInterface.BUTTON_NEGATIVE -> feature?.onCancel(sessionId, promptRequestUID) } } @@ -124,11 +125,18 @@ internal class ColorPickerDialogFragment : PromptDialogFragment(), DialogInterfa companion object { - fun newInstance(sessionId: String, defaultColor: String) = ColorPickerDialogFragment().apply { - arguments = (arguments ?: Bundle()).apply { - putString(KEY_SESSION_ID, sessionId) - putInt(KEY_SELECTED_COLOR, defaultColor.toColor()) - } + fun newInstance( + sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, + defaultColor: String + ) = ColorPickerDialogFragment().apply { + arguments = (arguments ?: Bundle()).apply { + putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) + putInt(KEY_SELECTED_COLOR, defaultColor.toColor()) + } } } } diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragment.kt index b55b864e018..1eaf01a8c91 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragment.kt @@ -31,7 +31,7 @@ internal class ConfirmDialogFragment : AbstractPromptTextDialogFragment() { .setCancelable(false) .setTitle(title) .setNegativeButton(negativeButtonText) { _, _ -> - feature?.onCancel(sessionId, userSelectionNoMoreDialogs) + feature?.onCancel(sessionId, promptRequestUID, userSelectionNoMoreDialogs) } .setPositiveButton(positiveButtonText) { _, _ -> onPositiveClickAction() @@ -42,17 +42,19 @@ internal class ConfirmDialogFragment : AbstractPromptTextDialogFragment() { override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - feature?.onCancel(sessionId, userSelectionNoMoreDialogs) + feature?.onCancel(sessionId, promptRequestUID, userSelectionNoMoreDialogs) } private fun onPositiveClickAction() { - feature?.onConfirm(sessionId, userSelectionNoMoreDialogs) + feature?.onConfirm(sessionId, promptRequestUID, userSelectionNoMoreDialogs) } companion object { @Suppress("LongParameterList") fun newInstance( sessionId: String? = null, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, title: String, message: String, positiveButtonText: String, @@ -65,6 +67,8 @@ internal class ConfirmDialogFragment : AbstractPromptTextDialogFragment() { with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putString(KEY_TITLE, title) putString(KEY_MESSAGE, message) putString(KEY_POSITIVE_BUTTON, positiveButtonText) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragment.kt index fc5bf46780a..47e4b796319 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragment.kt @@ -9,7 +9,6 @@ import android.content.DialogInterface import android.os.Bundle import androidx.appcompat.app.AlertDialog -private const val KEY_SHOULD_DISMISS_ON_LOAD = "KEY_SHOULD_DISMISS_ON_LOAD" private const val KEY_POSITIVE_BUTTON_TITLE = "KEY_POSITIVE_BUTTON_TITLE" private const val KEY_NEGATIVE_BUTTON_TITLE = "KEY_NEGATIVE_BUTTON_TITLE" private const val KEY_NEUTRAL_BUTTON_TITLE = "KEY_NEUTRAL_BUTTON_TITLE" @@ -26,8 +25,6 @@ internal class MultiButtonDialogFragment : AbstractPromptTextDialogFragment() { internal val neutralButtonTitle: String? by lazy { safeArguments.getString(KEY_NEUTRAL_BUTTON_TITLE) } - override fun shouldDismissOnLoad() = safeArguments.getBoolean(KEY_SHOULD_DISMISS_ON_LOAD, true) - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val builder = AlertDialog.Builder(requireContext()) .setTitle(title) @@ -39,23 +36,23 @@ internal class MultiButtonDialogFragment : AbstractPromptTextDialogFragment() { override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } private fun AlertDialog.Builder.setupButtons(): AlertDialog.Builder { if (!positiveButtonTitle.isNullOrBlank()) { setPositiveButton(positiveButtonTitle) { _, _ -> - feature?.onConfirm(sessionId, userSelectionNoMoreDialogs to ButtonType.POSITIVE) + feature?.onConfirm(sessionId, promptRequestUID, userSelectionNoMoreDialogs to ButtonType.POSITIVE) } } if (!negativeButtonTitle.isNullOrBlank()) { setNegativeButton(negativeButtonTitle) { _, _ -> - feature?.onConfirm(sessionId, userSelectionNoMoreDialogs to ButtonType.NEGATIVE) + feature?.onConfirm(sessionId, promptRequestUID, userSelectionNoMoreDialogs to ButtonType.NEGATIVE) } } if (!neutralButtonTitle.isNullOrBlank()) { setNeutralButton(neutralButtonTitle) { _, _ -> - feature?.onConfirm(sessionId, userSelectionNoMoreDialogs to ButtonType.NEUTRAL) + feature?.onConfirm(sessionId, promptRequestUID, userSelectionNoMoreDialogs to ButtonType.NEUTRAL) } } return this @@ -65,6 +62,7 @@ internal class MultiButtonDialogFragment : AbstractPromptTextDialogFragment() { @Suppress("LongParameterList") fun newInstance( sessionId: String, + promptRequestUID: String, title: String, message: String, hasShownManyDialogs: Boolean, @@ -79,6 +77,7 @@ internal class MultiButtonDialogFragment : AbstractPromptTextDialogFragment() { with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) putString(KEY_TITLE, title) putString(KEY_MESSAGE, message) putBoolean(KEY_MANY_ALERTS, hasShownManyDialogs) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/PromptDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/PromptDialogFragment.kt index 5e5b49be1ed..bfd22f222b0 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/PromptDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/PromptDialogFragment.kt @@ -5,27 +5,33 @@ package mozilla.components.feature.prompts.dialog import androidx.fragment.app.DialogFragment +import mozilla.components.concept.engine.prompt.PromptRequest import mozilla.components.concept.storage.LoginValidationDelegate import mozilla.components.feature.prompts.login.LoginExceptions internal const val KEY_SESSION_ID = "KEY_SESSION_ID" internal const val KEY_TITLE = "KEY_TITLE" internal const val KEY_MESSAGE = "KEY_MESSAGE" +internal const val KEY_PROMPT_UID = "KEY_PROMPT_UID" +internal const val KEY_SHOULD_DISMISS_ON_LOAD = "KEY_SHOULD_DISMISS_ON_LOAD" /** * An abstract representation for all different types of prompt dialogs. * for handling [PromptFeature] dialogs. */ internal abstract class PromptDialogFragment : DialogFragment() { + var feature: Prompter? = null + + internal val sessionId: String by lazy { requireNotNull(arguments).getString(KEY_SESSION_ID)!! } + + internal val promptRequestUID: String by lazy { requireNotNull(arguments).getString(KEY_PROMPT_UID)!! } /** * Whether or not the dialog should automatically be dismissed when a new page is loaded. */ - open fun shouldDismissOnLoad(): Boolean = true - - var feature: Prompter? = null - - internal val sessionId: String by lazy { requireNotNull(arguments).getString(KEY_SESSION_ID)!! } + internal val shouldDismissOnLoad: Boolean by lazy { + safeArguments.getBoolean(KEY_SHOULD_DISMISS_ON_LOAD, true) + } internal val title: String by lazy { safeArguments.getString(KEY_TITLE)!! } @@ -49,28 +55,31 @@ internal interface Prompter { val loginExceptionStorage: LoginExceptions? /** - * Invoked when a dialog is dismissed. This consumes the [PromptFeature] - * value from the session indicated by [sessionId]. + * Invoked when a dialog is dismissed. This consumes the [PromptRequest] indicated by [promptRequestUID] + * from the session indicated by [sessionId]. * * @param sessionId this is the id of the session which requested the prompt. + * @param promptRequestUID id of the [PromptRequest] for which this dialog was shown. * @param value an optional value provided by the dialog as a result of cancelling the action. */ - fun onCancel(sessionId: String, value: Any? = null) + fun onCancel(sessionId: String, promptRequestUID: String, value: Any? = null) /** - * Invoked when the user confirms the action on the dialog. This consumes - * the [PromptFeature] value from the session indicated by [sessionId]. + * Invoked when the user confirms the action on the dialog. This consumes the [PromptRequest] indicated + * by [promptRequestUID] from the session indicated by [sessionId]. * * @param sessionId that requested to show the dialog. + * @param promptRequestUID id of the [PromptRequest] for which this dialog was shown. * @param value an optional value provided by the dialog as a result of confirming the action. */ - fun onConfirm(sessionId: String, value: Any?) + fun onConfirm(sessionId: String, promptRequestUID: String, value: Any?) /** * Invoked when the user is requesting to clear the selected value from the dialog. * This consumes the [PromptFeature] value from the session indicated by [sessionId]. * * @param sessionId that requested to show the dialog. + * @param promptRequestUID id of the [PromptRequest] for which this dialog was shown. */ - fun onClear(sessionId: String) + fun onClear(sessionId: String, promptRequestUID: String) } diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragment.kt index c6eb5683c10..2ba852fcae0 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragment.kt @@ -91,8 +91,6 @@ internal class SaveLoginDialogFragment : PromptDialogFragment() { // from different threads, so we are using a copy-on-write list. private var potentialDupesList: CopyOnWriteArrayList? = null - override fun shouldDismissOnLoad(): Boolean = false - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return BottomSheetDialog(requireContext(), R.style.MozDialogStyle).apply { setCancelable(true) @@ -128,7 +126,7 @@ internal class SaveLoginDialogFragment : PromptDialogFragment() { */ CoroutineScope(IO).launch { if (feature?.loginExceptionStorage?.isLoginExceptionByOrigin(origin) == true) { - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) dismiss() } } @@ -155,7 +153,7 @@ internal class SaveLoginDialogFragment : PromptDialogFragment() { feature?.loginExceptionStorage?.addLoginException(origin) } } - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) dismiss() } } @@ -166,13 +164,13 @@ internal class SaveLoginDialogFragment : PromptDialogFragment() { override fun onCancel(dialog: DialogInterface) { super.onCancel(dialog) - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) emitCancelFact() } private fun onPositiveClickAction() { feature?.onConfirm( - sessionId, Login( + sessionId, promptRequestUID, Login( guid = guid, origin = origin, formActionOrigin = formActionOrigin, @@ -391,11 +389,17 @@ internal class SaveLoginDialogFragment : PromptDialogFragment() { /** * A builder method for creating a [SaveLoginDialogFragment] * @param sessionId the id of the session for which this dialog will be created. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog is shown. + * @param shouldDismissOnLoad whether or not the dialog should automatically be dismissed + * when a new page is loaded. * @param hint a value that helps to determine the appropriate prompting behavior. * @param login represents login information on a given domain. * */ + @Suppress("LongParameterList") fun newInstance( sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, hint: Int, login: Login, icon: Bitmap? = null @@ -406,6 +410,8 @@ internal class SaveLoginDialogFragment : PromptDialogFragment() { with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putInt(KEY_LOGIN_HINT, hint) putString(KEY_LOGIN_USERNAME, login.username) putString(KEY_LOGIN_PASSWORD, login.password) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragment.kt index e2f6cf27027..04d5db53982 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragment.kt @@ -54,11 +54,11 @@ internal class TextPromptDialogFragment : AbstractPromptTextDialogFragment(), Te } override fun onCancel(dialog: DialogInterface) { - feature?.onCancel(sessionId) + feature?.onCancel(sessionId, promptRequestUID) } private fun onPositiveClickAction() { - feature?.onConfirm(sessionId, userSelectionNoMoreDialogs to userSelectionEditText) + feature?.onConfirm(sessionId, promptRequestUID, userSelectionNoMoreDialogs to userSelectionEditText) } @SuppressLint("InflateParams") @@ -90,6 +90,9 @@ internal class TextPromptDialogFragment : AbstractPromptTextDialogFragment(), Te /** * A builder method for creating a [TextPromptDialogFragment] * @param sessionId to create the dialog. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog is shown. + * @param shouldDismissOnLoad whether or not the dialog should automatically be dismissed + * when a new page is loaded. * @param title the title of the dialog. * @param inputLabel * @param defaultInputValue @@ -97,8 +100,11 @@ internal class TextPromptDialogFragment : AbstractPromptTextDialogFragment(), Te * in a short period of time, if is true a checkbox will be part of the dialog, for the user * to choose if wants to prevent this [sessionId] continuing showing dialogs. */ + @Suppress("LongParameterList") fun newInstance( sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, title: String, inputLabel: String, defaultInputValue: String, @@ -110,6 +116,8 @@ internal class TextPromptDialogFragment : AbstractPromptTextDialogFragment(), Te with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putString(KEY_TITLE, title) putString(KEY_LABEL_INPUT, inputLabel) putString(KEY_DEFAULT_INPUT_VALUE, defaultInputValue) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragment.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragment.kt index 11dfab3a43e..ac485e95764 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragment.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragment.kt @@ -190,9 +190,9 @@ internal class TimePickerDialogFragment : PromptDialogFragment(), DatePicker.OnD override fun onClick(dialog: DialogInterface?, which: Int) { when (which) { - BUTTON_POSITIVE -> feature?.onConfirm(sessionId, selectedDate) - BUTTON_NEGATIVE -> feature?.onCancel(sessionId) - BUTTON_NEUTRAL -> feature?.onClear(sessionId) + BUTTON_POSITIVE -> feature?.onConfirm(sessionId, promptRequestUID, selectedDate) + BUTTON_NEGATIVE -> feature?.onCancel(sessionId, promptRequestUID) + BUTTON_NEUTRAL -> feature?.onClear(sessionId, promptRequestUID) } } @@ -200,6 +200,9 @@ internal class TimePickerDialogFragment : PromptDialogFragment(), DatePicker.OnD /** * A builder method for creating a [TimePickerDialogFragment] * @param sessionId to create the dialog. + * @param promptRequestUID identifier of the [PromptRequest] for which this dialog is shown. + * @param shouldDismissOnLoad whether or not the dialog should automatically be dismissed + * when a new page is loaded. * @param title of the dialog. * @param initialDate date that will be selected by default. * @param minDate the minimumDate date that will be allowed to be selected. @@ -207,11 +210,14 @@ internal class TimePickerDialogFragment : PromptDialogFragment(), DatePicker.OnD * @param selectionType indicate which type of time should be selected, valid values are * ([TimePickerDialogFragment.SELECTION_TYPE_DATE], [TimePickerDialogFragment.SELECTION_TYPE_DATE_AND_TIME], * and [TimePickerDialogFragment.SELECTION_TYPE_TIME]) + * * @return a new instance of [TimePickerDialogFragment] */ @Suppress("LongParameterList") fun newInstance( sessionId: String, + promptRequestUID: String, + shouldDismissOnLoad: Boolean, initialDate: Date, minDate: Date?, maxDate: Date?, @@ -222,6 +228,8 @@ internal class TimePickerDialogFragment : PromptDialogFragment(), DatePicker.OnD fragment.arguments = arguments with(arguments) { putString(KEY_SESSION_ID, sessionId) + putString(KEY_PROMPT_UID, promptRequestUID) + putBoolean(KEY_SHOULD_DISMISS_ON_LOAD, shouldDismissOnLoad) putSerializable(KEY_INITIAL_DATE, initialDate) putSerializable(KEY_MIN_DATE, minDate) putSerializable(KEY_MAX_DATE, maxDate) diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/file/FilePicker.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/file/FilePicker.kt index cf16ab659e8..a8febaf94f0 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/file/FilePicker.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/file/FilePicker.kt @@ -15,7 +15,7 @@ import android.provider.MediaStore.EXTRA_OUTPUT import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting.PRIVATE import androidx.fragment.app.Fragment -import mozilla.components.browser.state.selector.findTabOrCustomTabOrSelectedTab +import mozilla.components.browser.state.selector.findCustomTabOrSelectedTab import mozilla.components.browser.state.store.BrowserStore import mozilla.components.concept.engine.prompt.PromptRequest import mozilla.components.concept.engine.prompt.PromptRequest.File @@ -117,7 +117,7 @@ internal class FilePicker( var resultHandled = false val request = getActivePromptRequest() ?: return false if (requestCode == FILE_PICKER_ACTIVITY_REQUEST_CODE && request is File) { - store.consumePromptFrom(sessionId) { + store.consumePromptFrom(sessionId, request.uid) { if (resultCode == RESULT_OK) { handleFilePickerIntentResult(intent, request) } else { @@ -134,7 +134,9 @@ internal class FilePicker( } private fun getActivePromptRequest(): PromptRequest? = - store.state.findTabOrCustomTabOrSelectedTab(sessionId)?.content?.promptRequest + store.state.findCustomTabOrSelectedTab(sessionId)?.content?.promptRequests?.lastOrNull { + prompt -> prompt is File + } /** * Notifies the feature that the permissions request was completed. It will then @@ -175,8 +177,8 @@ internal class FilePicker( @VisibleForTesting(otherwise = PRIVATE) internal fun onPermissionsDenied() { // Nothing left to do. Consume / cleanup the requests. - store.consumePromptFrom(sessionId) { request -> - if (request is File) request.onDismiss() + store.consumePromptFrom(sessionId) { request -> + request.onDismiss() } } diff --git a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/login/LoginPicker.kt b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/login/LoginPicker.kt index 04265c182ff..575210a925e 100644 --- a/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/login/LoginPicker.kt +++ b/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/login/LoginPicker.kt @@ -39,9 +39,10 @@ internal class LoginPicker( } override fun onOptionSelect(option: Login) { - store.consumePromptFrom(sessionId) { - if (it is PromptRequest.SelectLoginPrompt) it.onConfirm(option) + store.consumePromptFrom(sessionId) { + it.onConfirm(option) } + loginSelectBar.hidePrompt() } @@ -53,9 +54,11 @@ internal class LoginPicker( @Suppress("TooGenericExceptionCaught") fun dismissCurrentLoginSelect(promptRequest: PromptRequest.SelectLoginPrompt? = null) { try { - promptRequest?.let { it.onDismiss() } ?: store.consumePromptFrom(sessionId) { - if (it is PromptRequest.SelectLoginPrompt) it.onDismiss() - } + promptRequest + ?.let { it.onDismiss() } + ?: store.consumePromptFrom(sessionId) { + it.onDismiss() + } } catch (e: RuntimeException) { Logger.error("Can't dismiss this login select prompt", e) } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptFeatureTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptFeatureTest.kt index 5f5366eab76..8cc7c946536 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptFeatureTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptFeatureTest.kt @@ -44,6 +44,8 @@ import mozilla.components.concept.engine.prompt.PromptRequest.SingleChoice import mozilla.components.concept.engine.prompt.PromptRequest.TextPrompt import mozilla.components.concept.engine.prompt.ShareData import mozilla.components.concept.storage.Login +import mozilla.components.feature.prompts.concept.SelectablePromptView +import mozilla.components.feature.prompts.creditcard.CreditCardPicker import mozilla.components.feature.prompts.dialog.ChoiceDialogFragment import mozilla.components.feature.prompts.dialog.ConfirmDialogFragment import mozilla.components.feature.prompts.dialog.MultiButtonDialogFragment @@ -51,8 +53,6 @@ import mozilla.components.feature.prompts.dialog.PromptDialogFragment import mozilla.components.feature.prompts.dialog.SaveLoginDialogFragment import mozilla.components.feature.prompts.file.FilePicker.Companion.FILE_PICKER_ACTIVITY_REQUEST_CODE import mozilla.components.feature.prompts.login.LoginPicker -import mozilla.components.feature.prompts.concept.SelectablePromptView -import mozilla.components.feature.prompts.creditcard.CreditCardPicker import mozilla.components.feature.prompts.share.ShareDelegate import mozilla.components.support.test.any import mozilla.components.support.test.eq @@ -65,7 +65,6 @@ import org.junit.After 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 @@ -485,11 +484,12 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, singleChoiceRequest)) .joinBlocking() - assertEquals(singleChoiceRequest, tab()?.content?.promptRequest) - feature.onCancel(tabId) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(singleChoiceRequest, tab()!!.content.promptRequests[0]) + feature.onCancel(tabId, singleChoiceRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()?.content?.promptRequests?.isEmpty() ?: false) } @Test @@ -502,11 +502,12 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, singleChoiceRequest)) .joinBlocking() - assertEquals(singleChoiceRequest, tab()?.content?.promptRequest) - feature.onConfirm(tabId, mock()) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(singleChoiceRequest, tab()!!.content.promptRequests[0]) + feature.onConfirm(tabId, singleChoiceRequest.uid, mock()) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -519,11 +520,12 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, menuChoiceRequest)) .joinBlocking() - assertEquals(menuChoiceRequest, tab()?.content?.promptRequest) - feature.onConfirm(tabId, mock()) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(menuChoiceRequest, tab()!!.content.promptRequests[0]) + feature.onConfirm(tabId, menuChoiceRequest.uid, mock()) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -536,11 +538,12 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, multipleChoiceRequest)) .joinBlocking() - assertEquals(multipleChoiceRequest, tab()?.content?.promptRequest) - feature.onConfirm(tabId, arrayOf()) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(multipleChoiceRequest, tab()!!.content.promptRequests[0]) + feature.onConfirm(tabId, multipleChoiceRequest.uid, arrayOf()) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -558,13 +561,13 @@ class PromptFeatureTest { feature.start() store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, false) + feature.onConfirm(tabId, promptRequest.uid, false) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onShowNoMoreAlertsWasCalled) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() assertTrue(onDismissWasCalled) } @@ -579,10 +582,10 @@ class PromptFeatureTest { feature.start() store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() assertTrue(onDismissWasCalled) - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -604,15 +607,15 @@ class PromptFeatureTest { feature.start() store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, false to "") + feature.onConfirm(tabId, promptRequest.uid, false to "") store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onConfirmWasCalled) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onDismissWasCalled) } @@ -633,9 +636,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onDismissWasCalled) } @@ -670,15 +673,15 @@ class PromptFeatureTest { .joinBlocking() val now = Date() - feature.onConfirm(tabId, now) + feature.onConfirm(tabId, promptRequest.uid, now) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertEquals(now, selectedDate) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)) .joinBlocking() - feature.onClear(tabId) + feature.onClear(tabId, promptRequest.uid) assertTrue(onClearWasCalled) feature.stop() } @@ -719,7 +722,7 @@ class PromptFeatureTest { feature.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, intent, RESULT_OK) store.waitUntilIdle() assertTrue(onSingleFileSelectionWasCalled) - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -753,7 +756,7 @@ class PromptFeatureTest { feature.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, intent, RESULT_OK) store.waitUntilIdle() assertTrue(onMultipleFileSelectionWasCalled) - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -775,7 +778,7 @@ class PromptFeatureTest { feature.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, intent, RESULT_CANCELED) store.waitUntilIdle() assertTrue(onDismissWasCalled) - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -881,14 +884,14 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, "" to "") + feature.onConfirm(tabId, promptRequest.uid, "" to "") store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onConfirmWasCalled) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() assertTrue(onDismissWasCalled) } @@ -922,9 +925,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, "" to "") + feature.onConfirm(tabId, promptRequest.uid, "" to "") store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onLeaveWasCalled) } @@ -952,9 +955,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onDismissWasCalled) } @@ -978,16 +981,16 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, "#f6b73c") + feature.onConfirm(tabId, promptRequest.uid, "#f6b73c") store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onConfirmWasCalled) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onDismissWasCalled) } @@ -1012,9 +1015,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, true) + feature.onConfirm(tabId, promptRequest.uid, true) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onConfirmWasCalled) } @@ -1037,9 +1040,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId, true) + feature.onCancel(tabId, promptRequest.uid, true) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onCancelWasCalled) } @@ -1069,9 +1072,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onCancelWasCalled) } @@ -1110,23 +1113,23 @@ class PromptFeatureTest { feature.start() store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, true to MultiButtonDialogFragment.ButtonType.POSITIVE) + feature.onConfirm(tabId, promptRequest.uid, true to MultiButtonDialogFragment.ButtonType.POSITIVE) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onPositiveButtonWasCalled) feature.promptAbuserDetector.resetJSAlertAbuseState() store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, true to MultiButtonDialogFragment.ButtonType.NEGATIVE) + feature.onConfirm(tabId, promptRequest.uid, true to MultiButtonDialogFragment.ButtonType.NEGATIVE) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onNegativeButtonWasCalled) feature.promptAbuserDetector.resetJSAlertAbuseState() store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onConfirm(tabId, true to MultiButtonDialogFragment.ButtonType.NEUTRAL) + feature.onConfirm(tabId, promptRequest.uid, true to MultiButtonDialogFragment.ButtonType.NEUTRAL) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onNeutralButtonWasCalled) } @@ -1159,9 +1162,9 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, promptRequest)).joinBlocking() - feature.onCancel(tabId) + feature.onCancel(tabId, promptRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onCancelWasCalled) } @@ -1230,7 +1233,7 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, popupPrompt)).joinBlocking() verify(fragmentManager, times(1)).beginTransaction() - feature.onCancel(tabId, true) + feature.onCancel(tabId, popupPrompt.uid, true) assertFalse(feature.promptAbuserDetector.shouldShowMoreDialogs) assertTrue(onDenyCalled) @@ -1361,11 +1364,12 @@ class PromptFeatureTest { ) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, shareRequest)).joinBlocking() - assertEquals(shareRequest, tab()?.content?.promptRequest) - feature.onConfirm(tabId, null) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(shareRequest, tab()!!.content.promptRequests[0]) + feature.onConfirm(tabId, shareRequest.uid, null) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onSuccessCalled) } @@ -1390,11 +1394,12 @@ class PromptFeatureTest { ) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, shareRequest)).joinBlocking() - assertEquals(shareRequest, tab()?.content?.promptRequest) - feature.onCancel(tabId) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(shareRequest, tab()!!.content.promptRequests[0]) + feature.onCancel(tabId, shareRequest.uid) store.waitUntilIdle() - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) assertTrue(onDismissCalled) } @@ -1418,7 +1423,8 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, shareRequest)).joinBlocking() val fragment = mock() - whenever(fragment.shouldDismissOnLoad()).thenReturn(true) + whenever(fragment.shouldDismissOnLoad).thenReturn(true) + whenever(fragment.sessionId).thenReturn(tabId) feature.activePrompt = WeakReference(fragment) val secondTabId = "second-test-tab" @@ -1454,7 +1460,8 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, shareRequest)).joinBlocking() val fragment = mock() - whenever(fragment.shouldDismissOnLoad()).thenReturn(true) + whenever(fragment.shouldDismissOnLoad).thenReturn(true) + whenever(fragment.sessionId).thenReturn(tabId) feature.activePrompt = WeakReference(fragment) val newTabId = "test-tab-2" @@ -1484,7 +1491,7 @@ class PromptFeatureTest { store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, shareRequest)).joinBlocking() val fragment = mock() - whenever(fragment.shouldDismissOnLoad()).thenReturn(true) + whenever(fragment.shouldDismissOnLoad).thenReturn(true) feature.activePrompt = WeakReference(fragment) store.dispatch(ContentAction.UpdateUrlAction(tabId, "mozilla.org")).joinBlocking() @@ -1550,6 +1557,8 @@ class PromptFeatureTest { val fragment = spy( SaveLoginDialogFragment.newInstance( tabId, + shareRequest.uid, + false, 0, Login( origin = "https://www.mozilla.org", @@ -1594,7 +1603,7 @@ class PromptFeatureTest { val prompt = feature.activePrompt?.get() assertNotNull(prompt) - assertFalse(prompt!!.shouldDismissOnLoad()) + assertFalse(prompt!!.shouldDismissOnLoad) } @Test @@ -1612,7 +1621,7 @@ class PromptFeatureTest { .joinBlocking() try { - feature.onConfirm(tabId, "wrong") + feature.onConfirm(tabId, singleChoiceRequest.uid, "wrong") } catch (e: IllegalArgumentException) { illegalArgumentExceptionThrown = true assertEquals( @@ -1636,8 +1645,10 @@ class PromptFeatureTest { isSaveLoginEnabled = { true }, loginValidationDelegate = mock() ) { } + val repostPromptRequest: PromptRequest.Repost = mock() + doReturn("uid").`when`(repostPromptRequest).uid - feature.handleDialogsRequest(mock(), mock()) + feature.handleDialogsRequest(repostPromptRequest, mock()) val dialog: ConfirmDialogFragment = feature.activePrompt!!.get() as ConfirmDialogFragment assertEquals(testContext.getString(R.string.mozac_feature_prompt_repost_title), dialog.title) @@ -1665,12 +1676,13 @@ class PromptFeatureTest { .dispatch(ContentAction.UpdatePromptRequestAction(tabId, repostRequest)) .joinBlocking() - assertEquals(repostRequest, tab()?.content?.promptRequest) - feature.onConfirm(tabId, null) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(repostRequest, tab()!!.content.promptRequests[0]) + feature.onConfirm(tabId, repostRequest.uid, null) store.waitUntilIdle() assertTrue(acceptCalled) - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } @Test @@ -1690,12 +1702,13 @@ class PromptFeatureTest { .dispatch(ContentAction.UpdatePromptRequestAction(tabId, repostRequest)) .joinBlocking() - assertEquals(repostRequest, tab()?.content?.promptRequest) - feature.onCancel(tabId) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(repostRequest, tab()!!.content.promptRequests[0]) + feature.onCancel(tabId, repostRequest.uid) store.waitUntilIdle() assertTrue(dismissCalled) - assertNull(tab()?.content?.promptRequest) + assertTrue(tab()!!.content.promptRequests.isEmpty()) } private fun mockFragmentManager(): FragmentManager { diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptMiddlewareTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptMiddlewareTest.kt index 4d0a750553d..b89143927b4 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptMiddlewareTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/PromptMiddlewareTest.kt @@ -59,13 +59,15 @@ class PromptMiddlewareTest { val popupPrompt1 = PromptRequest.Popup("https://firefox.com", onAllow = { }, onDeny = onDeny) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, popupPrompt1)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(popupPrompt1, tab()?.content?.promptRequest) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(popupPrompt1, tab()!!.content.promptRequests[0]) verify(onDeny, never()).invoke() val popupPrompt2 = PromptRequest.Popup("https://firefox.com", onAllow = { }, onDeny = onDeny) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, popupPrompt2)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(popupPrompt1, tab()?.content?.promptRequest) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(popupPrompt1, tab()!!.content.promptRequests[0]) verify(onDeny).invoke() } @@ -75,13 +77,16 @@ class PromptMiddlewareTest { val popupPrompt = PromptRequest.Popup("https://firefox.com", onAllow = { }, onDeny = onDeny) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, popupPrompt)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(popupPrompt, tab()?.content?.promptRequest) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(popupPrompt, tab()!!.content.promptRequests[0]) verify(onDeny, never()).invoke() val alert = PromptRequest.Alert("title", "message", false, { }, { }) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, alert)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(alert, tab()?.content?.promptRequest) + assertEquals(2, tab()!!.content.promptRequests.size) + assertEquals(popupPrompt, tab()!!.content.promptRequests[0]) + assertEquals(alert, tab()!!.content.promptRequests[1]) } @Test @@ -89,13 +94,16 @@ class PromptMiddlewareTest { val alert = PromptRequest.Alert("title", "message", false, { }, { }) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, alert)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(alert, tab()?.content?.promptRequest) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(alert, tab()!!.content.promptRequests[0]) val onDeny = spy { } val popupPrompt = PromptRequest.Popup("https://firefox.com", onAllow = { }, onDeny = onDeny) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, popupPrompt)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(popupPrompt, tab()?.content?.promptRequest) + assertEquals(2, tab()!!.content.promptRequests.size) + assertEquals(alert, tab()!!.content.promptRequests[0]) + assertEquals(popupPrompt, tab()!!.content.promptRequests[1]) verify(onDeny, never()).invoke() } @@ -104,11 +112,14 @@ class PromptMiddlewareTest { val alert = PromptRequest.Alert("title", "message", false, { }, { }) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, alert)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(alert, tab()?.content?.promptRequest) + assertEquals(1, tab()!!.content.promptRequests.size) + assertEquals(alert, tab()!!.content.promptRequests[0]) val beforeUnloadPrompt = PromptRequest.BeforeUnload("title", onLeave = { }, onStay = { }) store.dispatch(ContentAction.UpdatePromptRequestAction(tabId, beforeUnloadPrompt)).joinBlocking() testDispatcher.advanceUntilIdle() - assertEquals(beforeUnloadPrompt, tab()?.content?.promptRequest) + assertEquals(2, tab()!!.content.promptRequests.size) + assertEquals(alert, tab()!!.content.promptRequests[0]) + assertEquals(beforeUnloadPrompt, tab()!!.content.promptRequests[1]) } } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/creditcard/CreditCardPickerTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/creditcard/CreditCardPickerTest.kt index 3a06bf0df2d..4ff9d232bb2 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/creditcard/CreditCardPickerTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/creditcard/CreditCardPickerTest.kt @@ -97,7 +97,7 @@ class CreditCardPickerTest { val customTabContent: ContentState = mock() val customTab = CustomTabSessionState("custom-tab", customTabContent, mock(), mock()) - whenever(customTabContent.promptRequest).thenReturn(promptRequest) + whenever(customTabContent.promptRequests).thenReturn(listOf(promptRequest)) whenever(state.customTabs).thenReturn(listOf(customTab)) creditCardPicker.handleSelectCreditCardRequest(promptRequest) @@ -109,7 +109,7 @@ class CreditCardPickerTest { val promptRequest: PromptRequest = request ?: mock() val content: ContentState = mock() - whenever(content.promptRequest).thenReturn(promptRequest) + whenever(content.promptRequests).thenReturn(listOf(promptRequest)) val selected = TabSessionState("browser-tab", content, mock(), mock()) diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AlertDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AlertDialogFragmentTest.kt index 95b3fedbd13..29042428764 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AlertDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AlertDialogFragmentTest.kt @@ -13,8 +13,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.feature.prompts.PromptFeature import mozilla.components.feature.prompts.R import mozilla.components.feature.prompts.R.id -import mozilla.components.support.test.mock import mozilla.components.support.test.ext.appCompatContext +import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -40,7 +40,7 @@ class AlertDialogFragmentTest { @Test fun `build dialog`() { val fragment = spy( - AlertDialogFragment.newInstance("sessionId", "title", "message", true) + AlertDialogFragment.newInstance("sessionId", "uid", true, "title", "message", true) ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -54,6 +54,7 @@ class AlertDialogFragmentTest { val checkBox = dialog.findViewById(id.mozac_feature_prompts_no_more_dialogs_check_box) assertEquals(fragment.sessionId, "sessionId") + assertEquals(fragment.promptRequestUID, "uid") assertEquals(fragment.message, "message") assertEquals(fragment.hasShownManyDialogs, true) @@ -66,7 +67,7 @@ class AlertDialogFragmentTest { @Test fun `Alert with hasShownManyDialogs equals false should not have a checkbox`() { val fragment = spy( - AlertDialogFragment.newInstance("sessionId", "title", "message", false) + AlertDialogFragment.newInstance("sessionId", "uid", false, "title", "message", false) ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -86,7 +87,7 @@ class AlertDialogFragmentTest { val mockFeature: PromptFeature = mock() val fragment = spy( - AlertDialogFragment.newInstance("sessionId", "title", "message", false) + AlertDialogFragment.newInstance("sessionId", "uid", true, "title", "message", false) ) fragment.feature = mockFeature @@ -99,13 +100,13 @@ class AlertDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test fun `After checking no more dialogs checkbox feature onNoMoreDialogsChecked must be called`() { val fragment = spy( - AlertDialogFragment.newInstance("sessionId", "title", "message", true) + AlertDialogFragment.newInstance("sessionId", "uid", false, "title", "message", true) ) fragment.feature = mockFeature @@ -122,13 +123,13 @@ class AlertDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", true) + verify(mockFeature).onConfirm("sessionId", "uid", true) } @Test fun `touching outside of the dialog must notify the feature onCancel`() { val fragment = spy( - AlertDialogFragment.newInstance("sessionId", "title", "message", true) + AlertDialogFragment.newInstance("sessionId", "uid", true, "title", "message", true) ) fragment.feature = mockFeature @@ -137,6 +138,6 @@ class AlertDialogFragmentTest { fragment.onCancel(mock()) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragmentTest.kt index 6de941a1bbf..5a32fbf6eab 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/AuthenticationDialogFragmentTest.kt @@ -10,8 +10,8 @@ import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.feature.prompts.R.id -import mozilla.components.support.test.mock import mozilla.components.support.test.ext.appCompatContext +import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -38,6 +38,8 @@ class AuthenticationDialogFragmentTest { val fragment = spy( AuthenticationDialogFragment.newInstance( "sessionId", + "uid", + true, "title", "message", "username", @@ -59,6 +61,7 @@ class AuthenticationDialogFragmentTest { val passwordEditText = dialog.findViewById(id.password) assertEquals(fragment.sessionId, "sessionId") + assertEquals(fragment.promptRequestUID, "uid") assertEquals(fragment.title, "title") assertEquals(fragment.message, "message") assertEquals(fragment.username, "username") @@ -85,6 +88,8 @@ class AuthenticationDialogFragmentTest { val fragment = spy( AuthenticationDialogFragment.newInstance( "sessionId", + "uid", + false, "title", "message", "username", @@ -111,6 +116,8 @@ class AuthenticationDialogFragmentTest { val fragment = spy( AuthenticationDialogFragment.newInstance( "sessionId", + "uid", + true, "", "message", "username", @@ -137,6 +144,8 @@ class AuthenticationDialogFragmentTest { val fragment = spy( AuthenticationDialogFragment.newInstance( "sessionId", + "uid", + false, "title", "message", "username", @@ -156,7 +165,7 @@ class AuthenticationDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", "username" to "password") + verify(mockFeature).onConfirm("sessionId", "uid", "username" to "password") } @Test @@ -164,6 +173,8 @@ class AuthenticationDialogFragmentTest { val fragment = spy( AuthenticationDialogFragment.newInstance( "sessionId", + "uid", + true, "title", "message", "username", @@ -179,6 +190,6 @@ class AuthenticationDialogFragmentTest { fragment.onCancel(mock()) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragmentTest.kt index c0e1dbbfdaa..a46fb493ab8 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ChoiceDialogFragmentTest.kt @@ -62,7 +62,7 @@ class ChoiceDialogFragmentTest { @Test fun `Build single choice dialog`() { - val fragment = spy(newInstance(arrayOf(), "sessionId", SINGLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(arrayOf(), "sessionId", "uid", true, SINGLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -73,7 +73,7 @@ class ChoiceDialogFragmentTest { @Test fun `cancelling the dialog cancels the feature`() { - val fragment = spy(newInstance(arrayOf(), "sessionId", SINGLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(arrayOf(), "sessionId", "uid", false, SINGLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -91,13 +91,13 @@ class ChoiceDialogFragmentTest { fragment.onCancel(dialog) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test fun `Build menu choice dialog`() { - val fragment = spy(newInstance(arrayOf(), "sessionId", MENU_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(arrayOf(), "sessionId", "uid", true, MENU_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -109,7 +109,7 @@ class ChoiceDialogFragmentTest { @Test fun `Build multiple choice dialog`() { - val fragment = spy(newInstance(arrayOf(), "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(arrayOf(), "sessionId", "uid", false, MULTIPLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -121,7 +121,7 @@ class ChoiceDialogFragmentTest { @Test(expected = Exception::class) fun `Building a unknown dialog type will throw an exception`() { - val fragment = spy(newInstance(arrayOf(), "sessionId", -1)) + val fragment = spy(newInstance(arrayOf(), "sessionId", "uid", true, -1)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -133,7 +133,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(item) - val fragment = spy(newInstance(choices, "sessionId", SINGLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", false, SINGLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -151,7 +151,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(item) - val fragment = spy(newInstance(choices, "sessionId", MENU_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", true, MENU_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -169,7 +169,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(separator) - val fragment = spy(newInstance(choices, "sessionId", MENU_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", false, MENU_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -186,7 +186,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(separator) - val fragment = spy(newInstance(choices, "sessionId", MENU_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", true, MENU_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -205,7 +205,7 @@ class ChoiceDialogFragmentTest { Choice(id = "", label = "multiple choice") ) - var fragment = spy(newInstance(choices, "sessionId", SINGLE_CHOICE_DIALOG_TYPE)) + var fragment = spy(newInstance(choices, "sessionId", "uid", false, SINGLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -214,7 +214,7 @@ class ChoiceDialogFragmentTest { assertEquals(type, TYPE_SINGLE) - fragment = spy(newInstance(choices, "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + fragment = spy(newInstance(choices, "sessionId", "uid", true, MULTIPLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() adapter = getAdapterFrom(fragment) @@ -222,7 +222,7 @@ class ChoiceDialogFragmentTest { type = adapter.getItemViewType(1) assertEquals(type, TYPE_GROUP) - fragment = spy(newInstance(choices, "sessionId", MENU_CHOICE_DIALOG_TYPE)) + fragment = spy(newInstance(choices, "sessionId", "uid", false, MENU_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() adapter = getAdapterFrom(fragment) @@ -233,7 +233,7 @@ class ChoiceDialogFragmentTest { type = adapter.getItemViewType(3) assertEquals(type, TYPE_MENU_SEPARATOR) - fragment = spy(newInstance(choices, "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + fragment = spy(newInstance(choices, "sessionId", "uid", true, MULTIPLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() adapter = getAdapterFrom(fragment) @@ -248,7 +248,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(Choice(id = "", label = "item1", children = arrayOf(Choice(id = "", label = "sub-item1")))) - val fragment = spy(newInstance(choices, "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", false, MULTIPLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -276,7 +276,7 @@ class ChoiceDialogFragmentTest { ) ) - val fragment = spy(newInstance(choices, "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", true, MULTIPLE_CHOICE_DIALOG_TYPE)) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -301,7 +301,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(item) - val fragment = spy(newInstance(choices, "sessionId", SINGLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", false, SINGLE_CHOICE_DIALOG_TYPE)) fragment.feature = mockFeature @@ -319,9 +319,9 @@ class ChoiceDialogFragmentTest { holder.itemView.performClick() - verify(mockFeature).onConfirm("sessionId", choices.first()) + verify(mockFeature).onConfirm("sessionId", "uid", choices.first()) dialog.dismiss() - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test @@ -329,7 +329,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(item) - val fragment = spy(newInstance(choices, "sessionId", MENU_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", true, MENU_CHOICE_DIALOG_TYPE)) fragment.feature = mockFeature @@ -347,9 +347,9 @@ class ChoiceDialogFragmentTest { holder.itemView.performClick() - verify(mockFeature).onConfirm("sessionId", choices.first()) + verify(mockFeature).onConfirm("sessionId", "uid", choices.first()) dialog.dismiss() - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test @@ -357,7 +357,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(Choice(id = "", label = "item1", children = arrayOf(subItem))) - val fragment = spy(newInstance(choices, "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", false, MULTIPLE_CHOICE_DIALOG_TYPE)) fragment.feature = mockFeature doReturn(appCompatContext).`when`(fragment).requireContext() @@ -379,12 +379,12 @@ class ChoiceDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", fragment.mapSelectChoice.keys.toTypedArray()) + verify(mockFeature).onConfirm("sessionId", "uid", fragment.mapSelectChoice.keys.toTypedArray()) val negativeButton = dialog.getButton(BUTTON_NEGATIVE) negativeButton.performClick() - verify(mockFeature, times(2)).onCancel("sessionId") + verify(mockFeature, times(2)).onCancel("sessionId", "uid") } @Test @@ -392,7 +392,7 @@ class ChoiceDialogFragmentTest { val choices = arrayOf(item.copy(selected = true)) - val fragment = spy(newInstance(choices, "sessionId", MULTIPLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", true, MULTIPLE_CHOICE_DIALOG_TYPE)) fragment.feature = mockFeature doReturn(appCompatContext).`when`(fragment).requireContext() @@ -417,7 +417,7 @@ class ChoiceDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", fragment.mapSelectChoice.keys.toTypedArray()) + verify(mockFeature).onConfirm("sessionId", "uid", fragment.mapSelectChoice.keys.toTypedArray()) } @Test @@ -436,7 +436,7 @@ class ChoiceDialogFragmentTest { ) ) - val fragment = spy(newInstance(choices, "sessionId", SINGLE_CHOICE_DIALOG_TYPE)) + val fragment = spy(newInstance(choices, "sessionId", "uid", false, SINGLE_CHOICE_DIALOG_TYPE)) fragment.feature = mockFeature doReturn(appCompatContext).`when`(fragment).requireContext() doNothing().`when`(fragment).dismiss() @@ -466,7 +466,7 @@ class ChoiceDialogFragmentTest { assertEquals(labelView.text, choiceGroup1.label) itemView.performClick() - verify(mockFeature).onConfirm("sessionId", choiceGroup1) + verify(mockFeature).onConfirm("sessionId", "uid", choiceGroup1) } } @@ -480,7 +480,7 @@ class ChoiceDialogFragmentTest { item.copy(label = "item$index") } } - val fragment = newInstance(choices, "sessionId", SINGLE_CHOICE_DIALOG_TYPE) + val fragment = newInstance(choices, "sessionId", "uid", true, SINGLE_CHOICE_DIALOG_TYPE) val inflater = LayoutInflater.from(testContext) val dialog = fragment.createDialogContentView(inflater) val recyclerView = dialog.findViewById(R.id.recyclerView) diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragmentTest.kt index b281dd4429a..5d806a12845 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ColorPickerDialogFragmentTest.kt @@ -11,8 +11,8 @@ import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.RecyclerView import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.feature.prompts.R -import mozilla.components.support.test.mock import mozilla.components.support.test.ext.appCompatContext +import mozilla.components.support.test.mock import mozilla.components.support.test.robolectric.testContext import org.junit.Assert.assertEquals import org.junit.Before @@ -38,7 +38,7 @@ class ColorPickerDialogFragmentTest { fun `build dialog`() { val fragment = spy( - ColorPickerDialogFragment.newInstance("sessionId", "#e66465") + ColorPickerDialogFragment.newInstance("sessionId", "uid", true, "#e66465") ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -48,6 +48,7 @@ class ColorPickerDialogFragmentTest { dialog.show() assertEquals(fragment.sessionId, "sessionId") + assertEquals(fragment.promptRequestUID, "uid") assertEquals(fragment.selectedColor.toHexColor(), "#e66465") } @@ -55,7 +56,7 @@ class ColorPickerDialogFragmentTest { fun `clicking on positive button notifies the feature`() { val fragment = spy( - ColorPickerDialogFragment.newInstance("sessionId", "#e66465") + ColorPickerDialogFragment.newInstance("sessionId", "uid", false, "#e66465") ) fragment.feature = mockFeature @@ -70,14 +71,14 @@ class ColorPickerDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", "#4f4663") + verify(mockFeature).onConfirm("sessionId", "uid", "#4f4663") } @Test fun `clicking on negative button notifies the feature`() { val fragment = spy( - ColorPickerDialogFragment.newInstance("sessionId", "#e66465") + ColorPickerDialogFragment.newInstance("sessionId", "uid", true, "#e66465") ) fragment.feature = mockFeature @@ -90,14 +91,14 @@ class ColorPickerDialogFragmentTest { val negativeButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_NEGATIVE) negativeButton.performClick() - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test fun `touching outside of the dialog must notify the feature onCancel`() { val fragment = spy( - ColorPickerDialogFragment.newInstance("sessionId", "#e66465") + ColorPickerDialogFragment.newInstance("sessionId", "uid", false, "#e66465") ) fragment.feature = mockFeature @@ -106,14 +107,14 @@ class ColorPickerDialogFragmentTest { fragment.onCancel(mock()) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test fun `will show a color item`() { val fragment = spy( - ColorPickerDialogFragment.newInstance("sessionId", "#e66465") + ColorPickerDialogFragment.newInstance("sessionId", "uid", true, "#e66465") ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -133,7 +134,7 @@ class ColorPickerDialogFragmentTest { fun `clicking on a item will update the selected color`() { val fragment = spy( - ColorPickerDialogFragment.newInstance("sessionId", "#e66465") + ColorPickerDialogFragment.newInstance("sessionId", "uid", false, "#e66465") ) doReturn(appCompatContext).`when`(fragment).requireContext() diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragmentTest.kt index 6036371ab39..1f885359763 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/ConfirmDialogFragmentTest.kt @@ -34,6 +34,8 @@ class ConfirmDialogFragmentTest { fragment = spy( ConfirmDialogFragment.newInstance( "sessionId", + "uid", + true, "title", "message", "positiveLabel", @@ -55,6 +57,7 @@ class ConfirmDialogFragmentTest { val messageTextView = dialog.findViewById(R.id.message) assertEquals(fragment.sessionId, "sessionId") + assertEquals(fragment.promptRequestUID, "uid") assertEquals(fragment.message, "message") val positiveButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) @@ -79,7 +82,7 @@ class ConfirmDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", false) + verify(mockFeature).onConfirm("sessionId", "uid", false) } @Test @@ -95,7 +98,7 @@ class ConfirmDialogFragmentTest { val negativeButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_NEGATIVE) negativeButton.performClick() - verify(mockFeature).onCancel("sessionId", false) + verify(mockFeature).onCancel("sessionId", "uid", false) } @Test @@ -116,6 +119,6 @@ class ConfirmDialogFragmentTest { fragment.onCancel(dialog) - verify(mockFeature).onCancel("sessionId", false) + verify(mockFeature).onCancel("sessionId", "uid", false) } } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragmentTest.kt index ed65eb747f2..285702b76c9 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/MultiButtonDialogFragmentTest.kt @@ -13,8 +13,8 @@ import androidx.core.view.isVisible import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.feature.prompts.R import mozilla.components.feature.prompts.R.id -import mozilla.components.support.test.mock import mozilla.components.support.test.ext.appCompatContext +import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -43,6 +43,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", true, @@ -67,6 +68,7 @@ class MultiButtonDialogFragmentTest { val neutralButton = (dialog).getButton(DialogInterface.BUTTON_NEUTRAL) assertEquals(fragment.sessionId, "sessionId") + assertEquals(fragment.promptRequestUID, "uid") assertEquals(fragment.message, "message") assertEquals(fragment.hasShownManyDialogs, true) @@ -86,6 +88,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", false, @@ -113,6 +116,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", false, @@ -131,7 +135,7 @@ class MultiButtonDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", false to MultiButtonDialogFragment.ButtonType.POSITIVE) + verify(mockFeature).onConfirm("sessionId", "uid", false to MultiButtonDialogFragment.ButtonType.POSITIVE) } @Test @@ -140,6 +144,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", false, @@ -158,7 +163,7 @@ class MultiButtonDialogFragmentTest { val negativeButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_NEGATIVE) negativeButton.performClick() - verify(mockFeature).onConfirm("sessionId", false to MultiButtonDialogFragment.ButtonType.NEGATIVE) + verify(mockFeature).onConfirm("sessionId", "uid", false to MultiButtonDialogFragment.ButtonType.NEGATIVE) } @Test @@ -167,6 +172,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", false, @@ -185,7 +191,7 @@ class MultiButtonDialogFragmentTest { val neutralButton = (dialog as AlertDialog).getButton(DialogInterface.BUTTON_NEUTRAL) neutralButton.performClick() - verify(mockFeature).onConfirm("sessionId", false to MultiButtonDialogFragment.ButtonType.NEUTRAL) + verify(mockFeature).onConfirm("sessionId", "uid", false to MultiButtonDialogFragment.ButtonType.NEUTRAL) } @Test @@ -194,6 +200,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", true, @@ -216,7 +223,7 @@ class MultiButtonDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", true to MultiButtonDialogFragment.ButtonType.POSITIVE) + verify(mockFeature).onConfirm("sessionId", "uid", true to MultiButtonDialogFragment.ButtonType.POSITIVE) } @Test @@ -224,6 +231,7 @@ class MultiButtonDialogFragmentTest { val fragment = spy( MultiButtonDialogFragment.newInstance( "sessionId", + "uid", "title", "message", true, @@ -238,6 +246,6 @@ class MultiButtonDialogFragmentTest { fragment.onCancel(mock()) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragmentTest.kt index d28c2f807b4..2d0797f6b57 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/SaveLoginDialogFragmentTest.kt @@ -31,6 +31,8 @@ class SaveLoginDialogFragmentTest : TestCase() { @Test fun `dialog should always set the website icon if it is available`() { val sessionId = "sessionId" + val requestUID = "uid" + val shouldDismissOnLoad = true val hint = 42 val loginUsername = "username" val loginPassword = "password" @@ -39,7 +41,7 @@ class SaveLoginDialogFragmentTest : TestCase() { `when`(login.password).thenReturn(loginPassword) val icon: Bitmap = mock() val fragment = spy(SaveLoginDialogFragment.newInstance( - sessionId, hint, login, icon + sessionId, requestUID, shouldDismissOnLoad, hint, login, icon )) doReturn(appCompatContext).`when`(fragment).requireContext() doAnswer { @@ -55,6 +57,7 @@ class SaveLoginDialogFragmentTest : TestCase() { verify(fragment).inflateRootView(any()) verify(fragment).setupRootView(any()) assertEquals(sessionId, fragment.sessionId) + assertEquals(requestUID, fragment.promptRequestUID) // Using assertTrue since assertEquals / assertSame would fail here assertTrue(loginUsername == fragmentView.findViewById(R.id.username_field).text.toString()) assertTrue(loginPassword == fragmentView.findViewById(R.id.password_field).text.toString()) @@ -67,6 +70,8 @@ class SaveLoginDialogFragmentTest : TestCase() { @Test fun `dialog should use a default tinted icon if favicon is not available`() { val sessionId = "sessionId" + val requestUID = "uid" + val shouldDismissOnLoad = false val hint = 42 val loginUsername = "username" val loginPassword = "password" @@ -75,7 +80,7 @@ class SaveLoginDialogFragmentTest : TestCase() { `when`(login.password).thenReturn(loginPassword) val icon: Bitmap? = null // null favicon val fragment = spy(SaveLoginDialogFragment.newInstance( - sessionId, hint, login, icon + sessionId, requestUID, shouldDismissOnLoad, hint, login, icon )) val defaultIconResource = R.drawable.mozac_ic_globe doReturn(appCompatContext).`when`(fragment).requireContext() diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragmentTest.kt index 10cdcefeab8..11712cb970e 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TextPromptDialogFragmentTest.kt @@ -11,8 +11,8 @@ import androidx.appcompat.app.AlertDialog import androidx.core.view.isVisible import androidx.test.ext.junit.runners.AndroidJUnit4 import mozilla.components.feature.prompts.R.id -import mozilla.components.support.test.mock import mozilla.components.support.test.ext.appCompatContext +import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -39,7 +39,7 @@ class TextPromptDialogFragmentTest { fun `build dialog`() { val fragment = spy( - TextPromptDialogFragment.newInstance("sessionId", "title", "label", "defaultValue", true) + TextPromptDialogFragment.newInstance("sessionId", "uid", true, "title", "label", "defaultValue", true) ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -54,6 +54,7 @@ class TextPromptDialogFragmentTest { val checkBox = dialog.findViewById(id.mozac_feature_prompts_no_more_dialogs_check_box) assertEquals(fragment.sessionId, "sessionId") + assertEquals(fragment.promptRequestUID, "uid") assertEquals(fragment.title, "title") assertEquals(fragment.labelInput, "label") assertEquals(fragment.defaultInputValue, "defaultValue") @@ -76,7 +77,7 @@ class TextPromptDialogFragmentTest { fun `TextPrompt with hasShownManyDialogs equals false should not have a checkbox`() { val fragment = spy( - TextPromptDialogFragment.newInstance("sessionId", "title", "label", "defaultValue", false) + TextPromptDialogFragment.newInstance("sessionId", "uid", false, "title", "label", "defaultValue", false) ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -94,7 +95,7 @@ class TextPromptDialogFragmentTest { fun `Clicking on positive button notifies the feature`() { val fragment = spy( - TextPromptDialogFragment.newInstance("sessionId", "title", "label", "defaultValue", false) + TextPromptDialogFragment.newInstance("sessionId", "uid", true, "title", "label", "defaultValue", false) ) fragment.feature = mockFeature @@ -107,14 +108,14 @@ class TextPromptDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", false to "defaultValue") + verify(mockFeature).onConfirm("sessionId", "uid", false to "defaultValue") } @Test fun `After checking no more dialogs checkbox feature onNoMoreDialogsChecked must be called`() { val fragment = spy( - TextPromptDialogFragment.newInstance("sessionId", "title", "label", "defaultValue", true) + TextPromptDialogFragment.newInstance("sessionId", "uid", false, "title", "label", "defaultValue", true) ) fragment.feature = mockFeature @@ -131,14 +132,14 @@ class TextPromptDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm("sessionId", true to "defaultValue") + verify(mockFeature).onConfirm("sessionId", "uid", true to "defaultValue") } @Test fun `touching outside of the dialog must notify the feature onCancel`() { val fragment = spy( - TextPromptDialogFragment.newInstance("sessionId", "title", "label", "defaultValue", true) + TextPromptDialogFragment.newInstance("sessionId", "uid", true, "title", "label", "defaultValue", true) ) fragment.feature = mockFeature @@ -147,6 +148,6 @@ class TextPromptDialogFragmentTest { fragment.onCancel(mock()) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } } diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragmentTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragmentTest.kt index dd73e63a041..cf055e62feb 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragmentTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragmentTest.kt @@ -57,7 +57,7 @@ class TimePickerDialogFragmentTest { val minDate = "2019-11-28".toDate("yyyy-MM-dd") val maxDate = "2019-11-30".toDate("yyyy-MM-dd") val fragment = spy( - TimePickerDialogFragment.newInstance("sessionId", initialDate, minDate, maxDate) + TimePickerDialogFragment.newInstance("sessionId", "uid", true, initialDate, minDate, maxDate) ) doReturn(appCompatContext).`when`(fragment).requireContext() @@ -66,7 +66,8 @@ class TimePickerDialogFragmentTest { dialog.show() val datePicker = (dialog as DatePickerDialog).datePicker - + assertEquals("sessionId", fragment.sessionId) + assertEquals("uid", fragment.promptRequestUID) assertEquals(2019, datePicker.year) assertEquals(11, datePicker.month + 1) assertEquals(29, datePicker.dayOfMonth) @@ -78,7 +79,7 @@ class TimePickerDialogFragmentTest { fun `Clicking on positive, neutral and negative button notifies the feature`() { val initialDate = "2019-11-29".toDate("yyyy-MM-dd") val fragment = spy( - TimePickerDialogFragment.newInstance("sessionId", initialDate, null, null) + TimePickerDialogFragment.newInstance("sessionId", "uid", false, initialDate, null, null) ) fragment.feature = mockFeature @@ -89,28 +90,28 @@ class TimePickerDialogFragmentTest { val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE) positiveButton.performClick() - verify(mockFeature).onConfirm(eq("sessionId"), any()) + verify(mockFeature).onConfirm(eq("sessionId"), eq("uid"), any()) val neutralButton = dialog.getButton(BUTTON_NEUTRAL) neutralButton.performClick() - verify(mockFeature).onClear("sessionId") + verify(mockFeature).onClear("sessionId", "uid") } @Test fun `touching outside of the dialog must notify the feature onCancel`() { val fragment = spy( - TimePickerDialogFragment.newInstance("sessionId", Date(), null, null) + TimePickerDialogFragment.newInstance("sessionId", "uid", true, Date(), null, null) ) fragment.feature = mockFeature doReturn(testContext).`when`(fragment).requireContext() fragment.onCancel(mock()) - verify(mockFeature).onCancel("sessionId") + verify(mockFeature).onCancel("sessionId", "uid") } @Test fun `onTimeChanged must update the selectedDate`() { - val dialogPicker = TimePickerDialogFragment.newInstance("sessionId", Date(), null, null) + val dialogPicker = TimePickerDialogFragment.newInstance("sessionId", "uid", false, Date(), null, null) dialogPicker.onTimeChanged(mock(), 1, 12) @@ -128,6 +129,8 @@ class TimePickerDialogFragmentTest { val fragment = spy( TimePickerDialogFragment.newInstance( "sessionId", + "uid", + true, initialDate, minDate, maxDate, @@ -168,6 +171,8 @@ class TimePickerDialogFragmentTest { val fragment = spy( TimePickerDialogFragment.newInstance( "sessionId", + "uid", + false, initialDate, minDate, maxDate, @@ -209,6 +214,8 @@ class TimePickerDialogFragmentTest { val fragment = spy( TimePickerDialogFragment.newInstance( "sessionId", + "uid", + true, initialDate, minDate, maxDate, @@ -231,6 +238,8 @@ class TimePickerDialogFragmentTest { val fragment = spy( TimePickerDialogFragment.newInstance( "sessionId", + "uid", + false, initialDate, minDate, maxDate, @@ -252,6 +261,8 @@ class TimePickerDialogFragmentTest { val fragment = spy( TimePickerDialogFragment.newInstance( "sessionId", + "uid", + true, initialDate, minDate, maxDate, diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/file/FilePickerTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/file/FilePickerTest.kt index 21ad91648fc..3b91dae496f 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/file/FilePickerTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/file/FilePickerTest.kt @@ -76,18 +76,18 @@ class FilePickerTest { @Test fun `FilePicker acts on a given (custom tab) session or the selected session`() { val customTabContent: ContentState = mock() - whenever(customTabContent.promptRequest).thenReturn(request) + whenever(customTabContent.promptRequests).thenReturn(listOf(request)) val customTab = CustomTabSessionState("custom-tab", customTabContent, mock(), mock()) whenever(state.customTabs).thenReturn(listOf(customTab)) filePicker = FilePicker(fragment, store, customTab.id) { } filePicker.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, 0, null) - verify(store).dispatch(ContentAction.ConsumePromptRequestAction(customTab.id)) + verify(store).dispatch(ContentAction.ConsumePromptRequestAction(customTab.id, request)) val selected = prepareSelectedSession(request) filePicker = FilePicker(fragment, store) { } filePicker.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, 0, null) - verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id)) + verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id, request)) } @Test @@ -157,7 +157,7 @@ class FilePickerTest { filePicker.onPermissionsDenied() assertTrue(onDismissWasCalled) - verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id)) + verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id, filePickerRequest)) } @Test @@ -180,7 +180,7 @@ class FilePickerTest { filePicker.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, RESULT_OK, intent) assertTrue(onSingleFileSelectionWasCalled) - verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id)) + verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id, filePickerRequest)) } @Test @@ -214,7 +214,7 @@ class FilePickerTest { filePicker.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, RESULT_OK, intent) assertTrue(onMultipleFileSelectionWasCalled) - verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id)) + verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id, filePickerRequest)) } @Test @@ -231,7 +231,7 @@ class FilePickerTest { filePicker.onActivityResult(FILE_PICKER_ACTIVITY_REQUEST_CODE, RESULT_CANCELED, intent) assertTrue(onDismissWasCalled) - verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id)) + verify(store).dispatch(ContentAction.ConsumePromptRequestAction(selected.id, filePickerRequest)) } @Test @@ -249,7 +249,7 @@ class FilePickerTest { assertFalse(wasConfirmed) assertFalse(wasDismissed) - verify(store, never()).dispatch(ContentAction.ConsumePromptRequestAction(selected.id)) + verify(store, never()).dispatch(ContentAction.ConsumePromptRequestAction(selected.id, request)) verify(spiedFilePicker, never()).handleFilePickerIntentResult(intent, request) } @@ -361,7 +361,7 @@ class FilePickerTest { private fun prepareSelectedSession(request: PromptRequest? = null): TabSessionState { val promptRequest: PromptRequest = request ?: mock() val content: ContentState = mock() - whenever(content.promptRequest).thenReturn(promptRequest) + whenever(content.promptRequests).thenReturn(listOf(promptRequest)) val selected = TabSessionState("browser-tab", content, mock(), mock()) whenever(state.selectedTabId).thenReturn(selected.id) diff --git a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/login/LoginPickerTest.kt b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/login/LoginPickerTest.kt index 612261331d2..6769444361b 100644 --- a/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/login/LoginPickerTest.kt +++ b/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/login/LoginPickerTest.kt @@ -55,7 +55,7 @@ class LoginPickerTest { @Test fun `LoginPicker shows the login select bar on a custom tab`() { val customTabContent: ContentState = mock() - whenever(customTabContent.promptRequest).thenReturn(request) + whenever(customTabContent.promptRequests).thenReturn(listOf(request)) val customTab = CustomTabSessionState("custom-tab", customTabContent, mock(), mock()) whenever(state.customTabs).thenReturn(listOf(customTab)) @@ -105,7 +105,7 @@ class LoginPickerTest { private fun prepareSelectedSession(request: PromptRequest? = null): TabSessionState { val promptRequest: PromptRequest = request ?: mock() val content: ContentState = mock() - whenever(content.promptRequest).thenReturn(promptRequest) + whenever(content.promptRequests).thenReturn(listOf(promptRequest)) val selected = TabSessionState("browser-tab", content, mock(), mock()) whenever(state.selectedTabId).thenReturn(selected.id) diff --git a/docs/changelog.md b/docs/changelog.md index 79ff61d1878..d21d349ae7e 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,6 +12,9 @@ permalink: /changelog/ * [Gecko](https://github.com/mozilla-mobile/android-components/blob/master/buildSrc/src/main/java/Gecko.kt) * [Configuration](https://github.com/mozilla-mobile/android-components/blob/master/.config.yml) +* **feature-prompts**: + * ⚠️ **This is a breaking change**: [#8989](https://github.com/mozilla-mobile/android-components/issues/8989) - Add support for multiple prompts in ContentState and help avoid some Exceptions. + * **service-pocket** * ⚠️ **This is a breaking change**: Rebuilt from the ground up to better support offering to clients Pocket recommended articles. * See component's [README](https://github.com/mozilla-mobile/android-components/blob/master/components/service/pocket/README.md) to get more info.