Revert "For #24455 - Migrate NoCollectionsMessageViewHolder to Compose"

This reverts commit 7b895aba

(cherry picked from commit 6a33f29a91)
Arturo Mejia 2 years ago
parent ace6a83b27
commit e7bac4fcfe

@ -33,6 +33,7 @@ import androidx.test.uiautomator.Until
import androidx.test.uiautomator.Until.findObject
import mozilla.components.browser.state.state.searchEngines
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.instanceOf
import org.hamcrest.CoreMatchers.not
import org.hamcrest.Matchers
@ -140,11 +141,7 @@ class HomeScreenRobot {
fun verifyRecentBookmarksSectionIsDisplayed() = assertRecentBookmarksSectionIsDisplayed()
fun verifyRecentBookmarksSectionIsNotDisplayed() = assertRecentBookmarksSectionIsNotDisplayed()
fun verifyRecentlyVisitedSearchGroupDisplayed(
shouldBeDisplayed: Boolean,
searchTerm: String,
groupSize: Int
) {
fun verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed: Boolean, searchTerm: String, groupSize: Int) {
// checks if the search group exists in the Recently visited section
if (shouldBeDisplayed) {
recentlyVisitedList.waitForExists(waitingTime)
@ -165,11 +162,7 @@ class HomeScreenRobot {
}
}
fun verifyCurrentSearchGroupIsDisplayed(
shouldBeDisplayed: Boolean,
searchTerm: String,
groupSize: Int = 0
) {
fun verifyCurrentSearchGroupIsDisplayed(shouldBeDisplayed: Boolean, searchTerm: String, groupSize: Int = 0) {
// checks search group in the Jump back in section
if (shouldBeDisplayed) {
assertTrue(
@ -415,12 +408,8 @@ class HomeScreenRobot {
// return CollectionRobot.Transition()
// }
fun openRecentlyVisitedSearchGroupHistoryList(
title: String,
interact: HistoryRobot.() -> Unit
): HistoryRobot.Transition {
val searchGroup =
recentlyVisitedList.getChildByText(UiSelector().text(title), title, true)
fun openRecentlyVisitedSearchGroupHistoryList(title: String, interact: HistoryRobot.() -> Unit): HistoryRobot.Transition {
val searchGroup = recentlyVisitedList.getChildByText(UiSelector().text(title), title, true)
searchGroup.waitForExists(waitingTimeShort)
searchGroup.click()
@ -452,8 +441,7 @@ private fun assertKeyboardVisibility(isExpectedToBeVisible: Boolean) =
.contains("mInputShown=true")
)
private fun navigationToolbar() =
mDevice.findObject(UiSelector().resourceId("$packageName:id/toolbar"))
private fun navigationToolbar() = mDevice.findObject(UiSelector().resourceId("$packageName:id/toolbar"))
private fun assertNavigationToolbar() = assertTrue(navigationToolbar().waitForExists(waitingTime))
@ -462,8 +450,7 @@ private fun assertFocusedNavigationToolbar() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertHomeScreen() {
mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout"))
.waitForExists(waitingTime)
mDevice.findObject(UiSelector().resourceId("$packageName:id/homeLayout")).waitForExists(waitingTime)
onView(ViewMatchers.withResourceName("homeLayout"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
@ -487,23 +474,18 @@ private fun assertTabButton() =
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertCollectionsHeader() =
assertTrue(
mDevice.findObject(
UiSelector().textContains(
"Collections"
)
).waitForExists(waitingTime)
)
onView(allOf(withText("Collections")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertNoCollectionsText() =
assertTrue(
mDevice.findObject(
UiSelector().textContains(
onView(
withText(
containsString(
"Collect the things that matter to you.\n" +
"Group together similar searches, sites, and tabs for quick access later."
)
).waitForExists(waitingTime)
)
)
).check(matches(isDisplayed()))
private fun assertHomeComponent() =
onView(ViewMatchers.withResourceName("sessionControlRecyclerView"))
@ -542,7 +524,6 @@ private fun assertStartSyncHeader() {
onView(allOf(withText(R.string.onboarding_account_sign_in_header_1)))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertAccountsSignInButton() =
onView(ViewMatchers.withResourceName("fxa_sign_in_button"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
@ -552,7 +533,6 @@ private fun assertChooseThemeHeader() {
onView(withText("Choose your theme"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertChooseThemeText() {
scrollToElementByText("Choose your theme")
onView(allOf(withText("Save some battery and your eyesight with dark mode.")))
@ -582,7 +562,6 @@ private fun assertDarkThemeDescription() {
onView(allOf(withText("Dark theme")))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertAutomaticThemeToggle() {
scrollToElementByText("Choose your theme")
onView(withId(R.id.theme_automatic_radio_button))
@ -707,11 +686,9 @@ private fun assertTopSiteContextMenuItems() {
)
}
private fun assertJumpBackInSectionIsDisplayed() =
assertTrue(jumpBackInSection().waitForExists(waitingTime))
private fun assertJumpBackInSectionIsDisplayed() = assertTrue(jumpBackInSection().waitForExists(waitingTime))
private fun assertJumpBackInSectionIsNotDisplayed() =
assertFalse(jumpBackInSection().waitForExists(waitingTimeShort))
private fun assertJumpBackInSectionIsNotDisplayed() = assertFalse(jumpBackInSection().waitForExists(waitingTimeShort))
private fun assertRecentBookmarksSectionIsDisplayed() =
assertTrue(recentBookmarksSection().waitForExists(waitingTime))
@ -721,8 +698,7 @@ private fun assertRecentBookmarksSectionIsNotDisplayed() =
private fun privateBrowsingButton() = onView(withId(R.id.privateBrowsingButton))
private fun saveTabsToCollectionButton() =
mDevice.findObject(UiSelector().textContains(getStringResource(R.string.tabs_menu_save_to_collection1)))
private fun saveTabsToCollectionButton() = onView(withId(R.id.add_tabs_to_collections_button))
private fun tabsCounter() = onView(withId(R.id.tab_button))
@ -733,8 +709,7 @@ private fun recentBookmarksSection() =
mDevice.findObject(UiSelector().textContains(getStringResource(R.string.recent_bookmarks_title)))
private fun startBrowsingButton(): UiObject {
val startBrowsingButton =
mDevice.findObject(UiSelector().resourceId("$packageName:id/finish_button"))
val startBrowsingButton = mDevice.findObject(UiSelector().resourceId("$packageName:id/finish_button"))
homeScreenList()
.scrollIntoView(startBrowsingButton)
homeScreenList()

@ -1,117 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.compose
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.Spacer
import androidx.compose.material.Icon
import androidx.compose.material.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.mozilla.fenix.R
import org.mozilla.fenix.compose.button.PrimaryButton
import org.mozilla.fenix.compose.ext.dashedBorder
import org.mozilla.fenix.theme.FirefoxTheme
import org.mozilla.fenix.theme.Theme
/**
* [CollectionsPlaceholder] for displaying a message detailing the collections feature and
* allowing users to easily start creating their collection.
*
* @param showAddToCollectionButton Whether or not the "Add to Collection" button should be shown.
* @param onAddTabsToCollectionButtonClick Invoked when the user clicks on the "Add Tabs to Collection" button.
* @param onRemovePlaceholderClick Invoked when the user clicks on the close button to remove the Collections
* placeholder.
*/
@Composable
fun CollectionsPlaceholder(
showAddToCollectionButton: Boolean,
onAddTabsToCollectionButtonClick: () -> Unit,
onRemovePlaceholderClick: () -> Unit,
) {
Box(
modifier = Modifier
.semantics(mergeDescendants = true) {}
.dashedBorder(
color = FirefoxTheme.colors.borderPrimary,
cornerRadius = 8.dp,
dashHeight = 2.dp,
dashWidth = 4.dp
)
) {
Column(
Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Row(
modifier = Modifier.fillMaxWidth(),
) {
SectionHeader(
text = stringResource(R.string.collections_header),
modifier = Modifier.weight(1f)
)
IconButton(
onClick = onRemovePlaceholderClick,
modifier = Modifier.size(20.dp),
) {
Icon(
painter = painterResource(R.drawable.ic_close),
contentDescription = stringResource(
R.string.remove_home_collection_placeholder_content_description
),
tint = FirefoxTheme.colors.iconPrimary
)
}
}
Spacer(modifier = Modifier.height(4.dp))
SecondaryText(
text = stringResource(R.string.no_collections_description2),
modifier = Modifier.fillMaxWidth(),
fontSize = 14.sp
)
if (showAddToCollectionButton) {
Spacer(modifier = Modifier.height(12.dp))
PrimaryButton(
text = stringResource(R.string.tabs_menu_save_to_collection1),
icon = painterResource(R.drawable.ic_tab_collection),
onClick = onAddTabsToCollectionButtonClick
)
}
}
}
}
@Composable
@Preview
private fun CollectionsPlaceholderPreview() {
FirefoxTheme(theme = Theme.getTheme(isPrivate = false)) {
Box(Modifier.background(FirefoxTheme.colors.layer1)) {
CollectionsPlaceholder(
showAddToCollectionButton = true,
onAddTabsToCollectionButtonClick = {},
onRemovePlaceholderClick = {}
)
}
}
}

@ -27,6 +27,7 @@ import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
@ -35,6 +36,7 @@ import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.button.MaterialButton
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
@ -956,6 +958,9 @@ class HomeFragment : Fragment() {
}
binding.tabButton.setCountWithAnimation(tabCount)
// The add_tabs_to_collections_button is added at runtime. We need to search for it in the same way.
sessionControlView?.view?.findViewById<MaterialButton>(R.id.add_tabs_to_collections_button)
?.isVisible = tabCount > 0
}
private fun displayWallpaperIfEnabled() {

@ -176,8 +176,7 @@ sealed class AdapterItem(@LayoutRes val viewType: Int) {
object PocketStoriesItem : AdapterItem(PocketStoriesViewHolder.LAYOUT_ID)
object PocketCategoriesItem : AdapterItem(PocketCategoriesViewHolder.LAYOUT_ID)
object PocketRecommendationsFooterItem :
AdapterItem(PocketRecommendationsHeaderViewHolder.LAYOUT_ID)
object PocketRecommendationsFooterItem : AdapterItem(PocketRecommendationsHeaderViewHolder.LAYOUT_ID)
object BottomSpacer : AdapterItem(BottomSpacerViewHolder.LAYOUT_ID)
@ -278,11 +277,6 @@ class SessionControlAdapter(
composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner
)
NoCollectionsMessageViewHolder.LAYOUT_ID -> return NoCollectionsMessageViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner,
interactor = interactor
)
CollectionViewHolder.LAYOUT_ID -> return CollectionViewHolder(
composeView = ComposeView(parent.context),
viewLifecycleOwner = viewLifecycleOwner,
@ -303,6 +297,13 @@ class SessionControlAdapter(
viewLifecycleOwner = viewLifecycleOwner,
interactor = interactor
)
NoCollectionsMessageViewHolder.LAYOUT_ID ->
NoCollectionsMessageViewHolder(
view,
viewLifecycleOwner,
components.core.store,
interactor
)
OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view)
OnboardingSectionHeaderViewHolder.LAYOUT_ID -> OnboardingSectionHeaderViewHolder(view)
OnboardingManualSignInViewHolder.LAYOUT_ID -> OnboardingManualSignInViewHolder(view)
@ -328,7 +329,6 @@ class SessionControlAdapter(
when (holder) {
is CollectionHeaderViewHolder,
is CustomizeHomeButtonViewHolder,
is NoCollectionsMessageViewHolder,
is RecentlyVisitedViewHolder,
is RecentVisitsHeaderViewHolder,
is RecentBookmarksViewHolder,

@ -5,63 +5,59 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.View
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.unit.dp
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.lib.state.ext.observeAsComposableState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.lib.state.ext.flowScoped
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.R
import org.mozilla.fenix.components.components
import org.mozilla.fenix.compose.CollectionsPlaceholder
import org.mozilla.fenix.compose.ComposeViewHolder
import org.mozilla.fenix.databinding.NoCollectionsMessageBinding
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
import org.mozilla.fenix.utils.view.ViewHolder
/**
* [RecyclerView.ComposeViewHolder] for displaying a message detailing the collections feature and
* allowing users to easily start creating their first.
*
* @param composeView [ComposeView] which will be populated with Jetpack Compose UI content.
* @param viewLifecycleOwner [LifecycleOwner] to which this Composable will be tied to.
* @param interactor [CollectionInteractor] callback for user interaction.
*/
class NoCollectionsMessageViewHolder(
composeView: ComposeView,
@OptIn(ExperimentalCoroutinesApi::class)
open class NoCollectionsMessageViewHolder(
view: View,
viewLifecycleOwner: LifecycleOwner,
private val interactor: CollectionInteractor
) : ComposeViewHolder(composeView, viewLifecycleOwner) {
store: BrowserStore,
interactor: CollectionInteractor
) : ViewHolder(view) {
init {
val horizontalPadding =
composeView.resources.getDimensionPixelSize(R.dimen.home_item_horizontal_margin)
composeView.setPadding(horizontalPadding, 0, horizontalPadding, 0)
}
val binding = NoCollectionsMessageBinding.bind(view)
companion object {
val LAYOUT_ID = View.generateViewId()
}
binding.addTabsToCollectionsButton.apply {
@Composable
override fun Content() {
val normalTabsState = components.core.store.observeAsComposableState {
state ->
state.normalTabs
}.value ?: emptyList()
Column {
Spacer(modifier = Modifier.height(40.dp))
setOnClickListener {
interactor.onAddTabsToCollectionTapped()
}
isVisible = store.state.normalTabs.isNotEmpty()
}
CollectionsPlaceholder(
showAddToCollectionButton = normalTabsState.isNotEmpty(),
onAddTabsToCollectionButtonClick = interactor::onAddTabsToCollectionTapped,
onRemovePlaceholderClick = interactor::onRemoveCollectionsPlaceholder
binding.removeCollectionPlaceholder.apply {
increaseTapArea(
view.resources.getDimensionPixelSize(R.dimen.tap_increase_16)
)
setOnClickListener {
interactor.onRemoveCollectionsPlaceholder()
}
}
Spacer(modifier = Modifier.height(12.dp))
store.flowScoped(viewLifecycleOwner) { flow ->
flow.map { state -> state.normalTabs.size }
.ifChanged()
.collect { tabs ->
binding.addTabsToCollectionsButton.isVisible = tabs > 0
}
}
}
companion object {
const val LAYOUT_ID = R.layout.no_collections_message
}
}

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke android:width="2dp" android:dashWidth="4dp" android:color="?borderPrimary" android:dashGap="4dp" />
<corners android:radius="8dp" />
</shape>

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/no_collections_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/home_item_horizontal_margin"
android:layout_marginTop="40dp"
android:layout_marginBottom="12dp"
android:background="@drawable/empty_session_control_background"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/no_collections_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/collections_header"
android:textAppearance="@style/HeaderTextStyle"
android:textSize="16sp"
app:fontFamily="@font/metropolis_semibold"
app:layout_constraintEnd_toStartOf="@id/remove_collection_placeholder"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatImageButton
android:id="@+id/remove_collection_placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/remove_home_collection_placeholder_content_description"
app:layout_constraintBottom_toBottomOf="@id/no_collections_header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/no_collections_header"
app:srcCompat="@drawable/ic_close" />
<TextView
android:id="@+id/no_collections_description"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@string/no_collections_description2"
android:textAlignment="viewStart"
android:textColor="?attr/textSecondary"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/no_collections_header" />
<com.google.android.material.button.MaterialButton
android:id="@+id/add_tabs_to_collections_button"
style="@style/PositiveButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="2"
android:layout_marginTop="8dp"
android:text="@string/tabs_menu_save_to_collection1"
android:visibility="gone"
app:icon="@drawable/ic_tab_collection"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/no_collections_description" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -185,4 +185,5 @@
<!-- a11y -->
<dimen name="accessibility_min_height">48dp</dimen>
<dimen name="tap_increase_16">16dp</dimen>
</resources>

@ -0,0 +1,78 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.home.sessioncontrol.viewholders
import android.view.LayoutInflater
import androidx.core.view.isVisible
import androidx.lifecycle.LifecycleOwner
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.databinding.NoCollectionsMessageBinding
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
@RunWith(FenixRobolectricTestRunner::class)
class NoCollectionsMessageViewHolderTest {
private lateinit var binding: NoCollectionsMessageBinding
private val store: BrowserStore = BrowserStore(
initialState = BrowserState(
listOf(
createTab("https://www.mozilla.org", id = "reader-inactive-tab")
)
)
)
private lateinit var lifecycleOwner: LifecycleOwner
private lateinit var interactor: CollectionInteractor
@Before
fun setup() {
binding = NoCollectionsMessageBinding.inflate(LayoutInflater.from(testContext))
lifecycleOwner = mockk(relaxed = true)
interactor = mockk(relaxed = true)
}
@Test
fun `hide add to collection button when there are no tabs open`() {
val noTabsStore = BrowserStore()
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, noTabsStore, interactor)
assertFalse(binding.addTabsToCollectionsButton.isVisible)
}
@Test
fun `show add to collection button when there are tabs`() {
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
assertTrue(binding.addTabsToCollectionsButton.isVisible)
}
@Test
fun `call interactor on click`() {
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
binding.addTabsToCollectionsButton.performClick()
verify { interactor.onAddTabsToCollectionTapped() }
}
@Test
fun `hide view and change setting on remove placeholder click`() {
NoCollectionsMessageViewHolder(binding.root, lifecycleOwner, store, interactor)
binding.removeCollectionPlaceholder.performClick()
verify {
interactor.onRemoveCollectionsPlaceholder()
}
}
}
Loading…
Cancel
Save