From ebfc8bf715d38ec7b6ac0929b836c5b988ce8fa6 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Mon, 17 May 2021 16:34:28 +0300 Subject: [PATCH] For #8989 - Support multiple prompts in the same Session Following crash reports it was seen that it is possible for multiple prompts to be shown at the same time with an edgecase being that one prompt request comes after the user interacted with a previous prompt but before the consume call completing in AC / GV time at which this code will try to use the new prompt, not the one the user interacted with. Having support for multiple prompt requests in ContentState and tightly coupling a PromptDialogFragment with it's PromptRequest ensures any action consuming a PromptDialogFragment will always consume the PromptRequest for which that dialog was shown irrespective of the number of prompts or which is currently shown on top. --- .../browser/state/action/BrowserAction.kt | 3 +- .../state/reducer/ContentStateReducer.kt | 4 +- .../browser/state/state/ContentState.kt | 4 +- .../browser/state/action/ContentActionTest.kt | 20 ++- .../concept/engine/prompt/PromptRequest.kt | 13 +- .../feature/prompts/PromptFeature.kt | 148 ++++++++++++---- .../feature/prompts/PromptMiddleware.kt | 2 +- .../prompts/creditcard/CreditCardPicker.kt | 8 +- .../prompts/dialog/AlertDialogFragment.kt | 14 +- .../dialog/AuthenticationDialogFragment.kt | 13 +- .../prompts/dialog/ChoiceDialogFragment.kt | 16 +- .../dialog/ColorPickerDialogFragment.kt | 22 ++- .../prompts/dialog/ConfirmDialogFragment.kt | 10 +- .../dialog/MultiButtonDialogFragment.kt | 13 +- .../prompts/dialog/PromptDialogFragment.kt | 33 ++-- .../prompts/dialog/SaveLoginDialogFragment.kt | 18 +- .../dialog/TextPromptDialogFragment.kt | 12 +- .../dialog/TimePickerDialogFragment.kt | 14 +- .../feature/prompts/file/FilePicker.kt | 12 +- .../feature/prompts/login/LoginPicker.kt | 13 +- .../feature/prompts/PromptFeatureTest.kt | 161 ++++++++++-------- .../feature/prompts/PromptMiddlewareTest.kt | 27 ++- .../creditcard/CreditCardPickerTest.kt | 4 +- .../prompts/dialog/AlertDialogFragmentTest.kt | 19 ++- .../AuthenticationDialogFragmentTest.kt | 17 +- .../dialog/ChoiceDialogFragmentTest.kt | 60 +++---- .../dialog/ColorPickerDialogFragmentTest.kt | 21 +-- .../dialog/ConfirmDialogFragmentTest.kt | 9 +- .../dialog/MultiButtonDialogFragmentTest.kt | 20 ++- .../dialog/SaveLoginDialogFragmentTest.kt | 9 +- .../dialog/TextPromptDialogFragmentTest.kt | 19 ++- .../dialog/TimePickerDialogFragmentTest.kt | 27 ++- .../feature/prompts/file/FilePickerTest.kt | 18 +- .../feature/prompts/login/LoginPickerTest.kt | 4 +- docs/changelog.md | 5 +- 35 files changed, 523 insertions(+), 289 deletions(-) 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 117ab5b1dc2..686bac2709c 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,24 @@ 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. + * Defaults to `true`. + * @param uid [PromptRequest] unique identifier. Defaults to a random UUID. + * (This two parameters, though present in all subclasses are not evaluated in subclasses equals() calls) */ -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 9db4b44665a..1792420745f 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 @@ -275,11 +274,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) { @@ -297,7 +296,7 @@ class PromptFeature private constructor( dismissSelectPrompts() } - activePromptRequest = it.promptRequest + activePromptRequest = it.promptRequests.lastOrNull() } } } @@ -313,10 +312,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() } + ) } } @@ -372,7 +373,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) @@ -396,10 +397,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 -> { @@ -417,11 +419,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) @@ -487,9 +490,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() } @@ -501,7 +505,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() @@ -516,8 +520,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) } ) } @@ -544,6 +548,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], @@ -553,21 +559,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() @@ -586,6 +597,8 @@ class PromptFeature private constructor( with(promptRequest) { TimePickerDialogFragment.newInstance( session.id, + promptRequest.uid, + true, initialDate, minimumDate, maximumDate, @@ -598,6 +611,8 @@ class PromptFeature private constructor( with(promptRequest) { TextPromptDialogFragment.newInstance( session.id, + promptRequest.uid, + true, title, inputLabel, inputValue, @@ -610,6 +625,8 @@ class PromptFeature private constructor( with(promptRequest) { AuthenticationDialogFragment.newInstance( session.id, + promptRequest.uid, + true, title, message, userName, @@ -622,6 +639,8 @@ class PromptFeature private constructor( is Color -> ColorPickerDialogFragment.newInstance( session.id, + promptRequest.uid, + true, promptRequest.defaultColor ) @@ -632,11 +651,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 -> { @@ -651,10 +672,12 @@ class PromptFeature private constructor( ConfirmDialogFragment.newInstance( sessionId = session.id, + promptRequest.uid, title = title, message = body, positiveButtonText = leaveLabel, - negativeButtonText = stayLabel + negativeButtonText = stayLabel, + shouldDismissOnLoad = true ) } @@ -673,6 +696,7 @@ class PromptFeature private constructor( MultiButtonDialogFragment.newInstance( session.id, + promptRequest.uid, title, message, promptAbuserDetector.areDialogsBeingAbused(), @@ -695,6 +719,8 @@ class PromptFeature private constructor( ConfirmDialogFragment.newInstance( sessionId = session.id, + promptRequestUID = promptRequest.uid, + shouldDismissOnLoad = true, title = title, message = message, positiveButtonText = positiveAction, @@ -712,7 +738,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() } @@ -766,20 +792,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 833298e0b41..39049357376 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 a4f4b874cb5..7ede51ef013 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, @@ -395,11 +393,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 @@ -410,6 +414,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 06edf28a922..f72c4cd37f0 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 @@ -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 @@ -882,14 +885,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) } @@ -923,9 +926,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) } @@ -954,9 +957,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) } @@ -980,16 +983,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) } @@ -1014,9 +1017,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) } @@ -1039,9 +1042,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) } @@ -1071,9 +1074,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) } @@ -1112,23 +1115,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) } @@ -1161,9 +1164,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) } @@ -1232,7 +1235,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) @@ -1433,11 +1436,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) } @@ -1462,11 +1466,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) } @@ -1490,7 +1495,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" @@ -1526,7 +1532,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" @@ -1556,7 +1563,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() @@ -1622,6 +1629,8 @@ class PromptFeatureTest { val fragment = spy( SaveLoginDialogFragment.newInstance( tabId, + shareRequest.uid, + false, 0, Login( origin = "https://www.mozilla.org", @@ -1666,7 +1675,7 @@ class PromptFeatureTest { val prompt = feature.activePrompt?.get() assertNotNull(prompt) - assertFalse(prompt!!.shouldDismissOnLoad()) + assertFalse(prompt!!.shouldDismissOnLoad) } @Test @@ -1684,7 +1693,7 @@ class PromptFeatureTest { .joinBlocking() try { - feature.onConfirm(tabId, "wrong") + feature.onConfirm(tabId, singleChoiceRequest.uid, "wrong") } catch (e: IllegalArgumentException) { illegalArgumentExceptionThrown = true assertEquals( @@ -1708,8 +1717,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) @@ -1737,12 +1748,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 @@ -1762,12 +1774,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 8906979b915..9c487dc17f5 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 fa762db50c2..cac1e305171 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -12,8 +12,11 @@ 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. + * **feature-prompts** **browser-storage-sync** - * ⚠️ A new `isCreditCardAutofillEnabled` callback is available in `PromptFeature` and `GeckoCreditCardsAddressesStorageDelegate` to allow clients controlling whether credit cards should be autofilled or not. Default is false* + * ⚠️ A new `isCreditCardAutofillEnabled` callback is available in `PromptFeature` and `GeckoCreditCardsAddressesStorageDelegate` to allow clients controlling whether credit cards should be autofilled or not. Default is false. * **service-pocket** * ⚠️ **This is a breaking change**: Rebuilt from the ground up to better support offering to clients Pocket recommended articles.