[fenix] Issue https://github.com/mozilla-mobile/fenix/issues/21099 - Update items under "Jump back in" section to the latest designs
parent
d4a454442b
commit
b057d6a76f
@ -1,132 +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.home.recenttabs.view
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.util.dpToPx
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
private const val TOP_MARGIN_DP = 1
|
||||
|
||||
/**
|
||||
* All possible positions of a recent tab in relation to others when shown in the "Jump back in" section.
|
||||
*/
|
||||
enum class RecentTabsItemPosition {
|
||||
/**
|
||||
* This is the only tab to be shown in this section.
|
||||
*/
|
||||
SINGLE,
|
||||
|
||||
/**
|
||||
* This item is to be shown at the top of the section with others below it.
|
||||
*/
|
||||
TOP,
|
||||
|
||||
/**
|
||||
* This item is to be shown between others in this section.
|
||||
*/
|
||||
MIDDLE,
|
||||
|
||||
/**
|
||||
* This item is to be shown at the bottom of the section with others above it.
|
||||
*/
|
||||
BOTTOM
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers for setting various layout properties for the view from a [RecentTabViewHolder].
|
||||
*
|
||||
* Depending on the provided [RecentTabsItemPosition]:
|
||||
* - sets a different background so that the entire section possibly containing
|
||||
* more such items would have rounded corners but sibling items not.
|
||||
* - sets small margins for the items so that there's a clear separation between siblings
|
||||
*/
|
||||
sealed class RecentTabViewDecorator {
|
||||
/**
|
||||
* Apply the decoration to [itemView].
|
||||
*/
|
||||
abstract operator fun invoke(itemView: View): View
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Get the appropriate decorator to set view background / margins depending on the position
|
||||
* of that view in the recent tabs section.
|
||||
*/
|
||||
fun forPosition(position: RecentTabsItemPosition) = when (position) {
|
||||
RecentTabsItemPosition.SINGLE -> SingleTabDecoration
|
||||
RecentTabsItemPosition.TOP -> TopTabDecoration
|
||||
RecentTabsItemPosition.MIDDLE -> MiddleTabDecoration
|
||||
RecentTabsItemPosition.BOTTOM -> BottomTabDecoration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator for a view shown in the recent tabs section that will update it to express
|
||||
* that that item is the single one shown in this section.
|
||||
*/
|
||||
object SingleTabDecoration : RecentTabViewDecorator() {
|
||||
override fun invoke(itemView: View): View {
|
||||
val context = itemView.context
|
||||
|
||||
itemView.background =
|
||||
AppCompatResources.getDrawable(context, R.drawable.card_list_row_background)
|
||||
|
||||
return itemView
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator for a view shown in the recent tabs section that will update it to express
|
||||
* that this is an item shown at the top of the section and there are others below it.
|
||||
*/
|
||||
object TopTabDecoration : RecentTabViewDecorator() {
|
||||
override fun invoke(itemView: View): View {
|
||||
val context = itemView.context
|
||||
|
||||
itemView.background =
|
||||
AppCompatResources.getDrawable(context, R.drawable.rounded_top_corners)
|
||||
|
||||
return itemView
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator for a view shown in the recent tabs section that will update it to express
|
||||
* that this is an item shown has other recents tabs to be shown on top or below it.
|
||||
*/
|
||||
object MiddleTabDecoration : RecentTabViewDecorator() {
|
||||
override fun invoke(itemView: View): View {
|
||||
val context = itemView.context
|
||||
|
||||
itemView.setBackgroundColor(context.getColorFromAttr(R.attr.above))
|
||||
|
||||
(itemView.layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin =
|
||||
TOP_MARGIN_DP.dpToPx(context.resources.displayMetrics)
|
||||
|
||||
return itemView
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorator for a view shown in the recent tabs section that will update it to express
|
||||
* that this is an item shown at the bottom of the section and there are others above it.
|
||||
*/
|
||||
object BottomTabDecoration : RecentTabViewDecorator() {
|
||||
override fun invoke(itemView: View): View {
|
||||
val context = itemView.context
|
||||
|
||||
itemView.background =
|
||||
AppCompatResources.getDrawable(context, R.drawable.rounded_bottom_corners)
|
||||
|
||||
(itemView.layoutParams as? ViewGroup.MarginLayoutParams)?.topMargin =
|
||||
TOP_MARGIN_DP.dpToPx(context.resources.displayMetrics)
|
||||
|
||||
return itemView
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,197 @@
|
||||
/* 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/. */
|
||||
|
||||
@file:Suppress("MagicNumber")
|
||||
|
||||
package org.mozilla.fenix.home.recenttabs.view
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import mozilla.components.browser.icons.compose.Loader
|
||||
import mozilla.components.browser.icons.compose.Placeholder
|
||||
import mozilla.components.browser.icons.compose.WithIcon
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.support.ktx.kotlin.getRepresentativeSnippet
|
||||
import mozilla.components.ui.colors.PhotonColors
|
||||
import org.mozilla.fenix.components.components
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
|
||||
/**
|
||||
* A list of recent tabs to jump back to.
|
||||
*
|
||||
* @param recentTabs List of [TabSessionState] to display.
|
||||
* @param onRecentTabClick Invoked when the user clicks on a recent tab.
|
||||
*/
|
||||
@Composable
|
||||
fun RecentTabs(
|
||||
recentTabs: List<TabSessionState>,
|
||||
onRecentTabClick: (String) -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
recentTabs.forEach { tab ->
|
||||
RecentTabItem(
|
||||
tabId = tab.id,
|
||||
url = tab.content.url,
|
||||
title = tab.content.title,
|
||||
icon = tab.content.icon,
|
||||
onRecentTabClick = onRecentTabClick
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent tab item.
|
||||
*
|
||||
* @param tabId Tbe id of the tab.
|
||||
* @param url The loaded URL of the tab.
|
||||
* @param title The title of the tab.
|
||||
* @param icon The icon of the tab.
|
||||
* @param onRecentTabClick Invoked when the user clicks on a recent tab.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabItem(
|
||||
tabId: String,
|
||||
url: String,
|
||||
title: String,
|
||||
icon: Bitmap? = null,
|
||||
onRecentTabClick: (String) -> Unit = {}
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(116.dp)
|
||||
.clickable { onRecentTabClick(tabId) },
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
backgroundColor = FirefoxTheme.colors.surface,
|
||||
elevation = 6.dp
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
RecentTabImage(
|
||||
url = url,
|
||||
modifier = Modifier.size(116.dp, 84.dp),
|
||||
icon = icon
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
RecentTabTitle(title = title)
|
||||
|
||||
RecentTabSubtitle(url = url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent tab image.
|
||||
*
|
||||
* @param url The loaded URL of the tab.
|
||||
* @param modifier modifier Modifier used to draw the image content.
|
||||
* @param icon The icon of the tab. Fallback to loading the icon from the [url] if the [icon]
|
||||
* is null.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabImage(
|
||||
url: String,
|
||||
modifier: Modifier = Modifier,
|
||||
icon: Bitmap? = null
|
||||
) {
|
||||
if (icon != null) {
|
||||
Image(
|
||||
painter = BitmapPainter(icon.asImageBitmap()),
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
)
|
||||
} else {
|
||||
components.core.icons.Loader(
|
||||
url = url
|
||||
) {
|
||||
Placeholder {
|
||||
Box(
|
||||
modifier = Modifier.background(
|
||||
color = when (isSystemInDarkTheme()) {
|
||||
true -> Color(0xFF42414D) // DarkGrey30
|
||||
false -> PhotonColors.LightGrey30
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
WithIcon { icon ->
|
||||
Image(
|
||||
painter = icon.painter,
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent tab title.
|
||||
*
|
||||
* @param title The title of the tab.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabTitle(title: String) {
|
||||
Text(
|
||||
text = title,
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
fontSize = 14.sp,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 2
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A recent tab subtitle.
|
||||
*
|
||||
* @param url The loaded URL of the tab.
|
||||
*/
|
||||
@Composable
|
||||
private fun RecentTabSubtitle(url: String) {
|
||||
Text(
|
||||
text = url.getRepresentativeSnippet(),
|
||||
color = FirefoxTheme.colors.textSecondary,
|
||||
fontSize = 12.sp,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1
|
||||
)
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:clipToPadding="false"
|
||||
android:elevation="@dimen/home_item_elevation"
|
||||
android:foreground="?android:attr/selectableItemBackground">
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/recent_tab_icon"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:adjustViewBounds="true"
|
||||
android:importantForAccessibility="no"
|
||||
style="@style/recentTabIcon"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:srcCompat="@tools:sample/avatars" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/recent_tab_title"
|
||||
style="@style/Body16TextStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="start"
|
||||
android:maxLines="1"
|
||||
android:minLines="1"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toBottomOf="@id/recent_tab_icon"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@+id/recent_tab_icon"
|
||||
app:layout_constraintTop_toTopOf="@id/recent_tab_icon"
|
||||
tools:text="@tools:sample/lorem/random" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,142 +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.home.recenttabs.view
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.slot
|
||||
import io.mockk.unmockkStatic
|
||||
import io.mockk.verify
|
||||
import mozilla.components.support.ktx.android.content.getColorFromAttr
|
||||
import mozilla.components.support.ktx.android.util.dpToPx
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class RecentTabViewDecoratorTest {
|
||||
@Test
|
||||
fun `WHEN forPosition is called with RecentTabsItemPosition#SINGLE THEN return SingleTabDecoration`() {
|
||||
val result = RecentTabViewDecorator.forPosition(RecentTabsItemPosition.SINGLE)
|
||||
|
||||
assertTrue(result is RecentTabViewDecorator.SingleTabDecoration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN forPosition is called with RecentTabsItemPosition#TOP THEN return TopTabDecoration`() {
|
||||
val result = RecentTabViewDecorator.forPosition(RecentTabsItemPosition.TOP)
|
||||
|
||||
assertTrue(result is RecentTabViewDecorator.TopTabDecoration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN forPosition is called with RecentTabsItemPosition#MIDDLE THEN return MiddleTabDecoration`() {
|
||||
val result = RecentTabViewDecorator.forPosition(RecentTabsItemPosition.MIDDLE)
|
||||
|
||||
assertTrue(result is RecentTabViewDecorator.MiddleTabDecoration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN forPosition is called with RecentTabsItemPosition#BOTTOM THEN return SingleTabDecoration`() {
|
||||
val result = RecentTabViewDecorator.forPosition(RecentTabsItemPosition.BOTTOM)
|
||||
|
||||
assertTrue(result is RecentTabViewDecorator.BottomTabDecoration)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN SingleTabDecoration is invoked for a View THEN set the appropriate background`() {
|
||||
val view: View = mockk(relaxed = true)
|
||||
val drawable: Drawable = mockk()
|
||||
val drawableResCaptor = slot<Int>()
|
||||
|
||||
try {
|
||||
mockkStatic(AppCompatResources::class)
|
||||
every { AppCompatResources.getDrawable(any(), capture(drawableResCaptor)) } returns drawable
|
||||
|
||||
RecentTabViewDecorator.SingleTabDecoration(view)
|
||||
|
||||
verify { view.background = drawable }
|
||||
assertEquals(R.drawable.card_list_row_background, drawableResCaptor.captured)
|
||||
} finally {
|
||||
unmockkStatic(AppCompatResources::class)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN TopTabDecoration is invoked for a View THEN set the appropriate background`() {
|
||||
val view: View = mockk(relaxed = true)
|
||||
val drawable: Drawable = mockk()
|
||||
val drawableResCaptor = slot<Int>()
|
||||
|
||||
try {
|
||||
mockkStatic(AppCompatResources::class)
|
||||
every { AppCompatResources.getDrawable(any(), capture(drawableResCaptor)) } returns drawable
|
||||
|
||||
RecentTabViewDecorator.TopTabDecoration(view)
|
||||
|
||||
verify { view.background = drawable }
|
||||
assertEquals(R.drawable.rounded_top_corners, drawableResCaptor.captured)
|
||||
} finally {
|
||||
unmockkStatic(AppCompatResources::class)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN MiddleTabDecoration is invoked for a View THEN set the appropriate background and layout params`() {
|
||||
val colorAttrCaptor = slot<Int>()
|
||||
val viewLayoutParams: ViewGroup.MarginLayoutParams = mockk(relaxed = true)
|
||||
|
||||
try {
|
||||
mockkStatic("mozilla.components.support.ktx.android.util.DisplayMetricsKt")
|
||||
mockkStatic("mozilla.components.support.ktx.android.content.ContextKt")
|
||||
val view: View = mockk(relaxed = true) {
|
||||
every { layoutParams } returns viewLayoutParams
|
||||
every { context.getColorFromAttr(capture(colorAttrCaptor)) } returns 42
|
||||
every { context.resources.displayMetrics } returns mockk(relaxed = true)
|
||||
}
|
||||
every { any<Int>().dpToPx(any()) } returns 43
|
||||
|
||||
RecentTabViewDecorator.MiddleTabDecoration(view)
|
||||
|
||||
verify { view.setBackgroundColor(42) }
|
||||
assertEquals(R.attr.above, colorAttrCaptor.captured)
|
||||
assertEquals(43, viewLayoutParams.topMargin)
|
||||
} finally {
|
||||
unmockkStatic("mozilla.components.support.ktx.android.content.ContextKt")
|
||||
unmockkStatic("mozilla.components.support.ktx.android.util.DisplayMetricsKt")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN BottomTabDecoration is invoked for a View THEN set the appropriate background and layout params`() {
|
||||
val viewLayoutParams: ViewGroup.MarginLayoutParams = mockk(relaxed = true)
|
||||
val drawable: Drawable = mockk()
|
||||
val drawableResCaptor = slot<Int>()
|
||||
|
||||
try {
|
||||
mockkStatic(AppCompatResources::class)
|
||||
every { AppCompatResources.getDrawable(any(), capture(drawableResCaptor)) } returns drawable
|
||||
mockkStatic("mozilla.components.support.ktx.android.util.DisplayMetricsKt")
|
||||
val view: View = mockk(relaxed = true) {
|
||||
every { layoutParams } returns viewLayoutParams
|
||||
every { context.resources.displayMetrics } returns mockk(relaxed = true)
|
||||
}
|
||||
every { any<Int>().dpToPx(any()) } returns 43
|
||||
|
||||
RecentTabViewDecorator.BottomTabDecoration(view)
|
||||
|
||||
verify { view.background = drawable }
|
||||
assertEquals(R.drawable.rounded_bottom_corners, drawableResCaptor.captured)
|
||||
assertEquals(43, viewLayoutParams.topMargin)
|
||||
} finally {
|
||||
unmockkStatic("mozilla.components.support.ktx.android.util.DisplayMetricsKt")
|
||||
}
|
||||
}
|
||||
}
|
@ -1,87 +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.home.recenttabs.view
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.icons.BrowserIcons
|
||||
import mozilla.components.browser.icons.IconRequest
|
||||
import mozilla.components.browser.state.state.createTab
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.databinding.RecentTabsListRowBinding
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class RecentTabViewHolderTest {
|
||||
|
||||
private lateinit var binding: RecentTabsListRowBinding
|
||||
private lateinit var interactor: SessionControlInteractor
|
||||
private lateinit var icons: BrowserIcons
|
||||
|
||||
private val tab = createTab(
|
||||
url = "https://mozilla.org",
|
||||
title = "Mozilla"
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
binding = RecentTabsListRowBinding.inflate(LayoutInflater.from(testContext))
|
||||
interactor = mockk(relaxed = true)
|
||||
icons = mockk(relaxed = true)
|
||||
|
||||
every { icons.loadIntoView(binding.recentTabIcon, any()) } returns mockk()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN a new recent tab on bind THEN set the title text and load the tab icon`() {
|
||||
RecentTabViewHolder(binding.root, interactor, icons).bindTab(tab)
|
||||
|
||||
assertEquals(tab.content.title, binding.recentTabTitle.text)
|
||||
|
||||
verify { icons.loadIntoView(binding.recentTabIcon, IconRequest(tab.content.url)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a recent tab item is clicked THEN interactor is called`() {
|
||||
RecentTabViewHolder(binding.root, interactor, icons).bindTab(tab)
|
||||
|
||||
binding.root.performClick()
|
||||
|
||||
verify { interactor.onRecentTabClicked(tab.id) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a recent tab icon exists THEN load it`() {
|
||||
val bitmap = testContext.getDrawable(R.drawable.ic_search)!!.toBitmap()
|
||||
val tabWithIcon = tab.copy(content = tab.content.copy(icon = bitmap))
|
||||
val viewHolder = RecentTabViewHolder(binding.root, interactor, icons)
|
||||
|
||||
assertNull(binding.recentTabIcon.drawable)
|
||||
|
||||
viewHolder.bindTab(tabWithIcon)
|
||||
|
||||
assertNotNull(binding.recentTabIcon.drawable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN a recent tab does not have a title THEN show the url`() {
|
||||
val tabWithoutTitle = createTab(url = "https://mozilla.org")
|
||||
|
||||
RecentTabViewHolder(binding.root, interactor, icons).bindTab(tabWithoutTitle)
|
||||
|
||||
assertEquals(tabWithoutTitle.content.url, binding.recentTabTitle.text)
|
||||
}
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.home.sessioncontrol
|
||||
|
||||
import io.mockk.mockk
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.home.recenttabs.view.RecentTabsItemPosition
|
||||
|
||||
class SessionControlViewTest {
|
||||
@Test
|
||||
fun `GIVEN two recent tabs WHEN showRecentTabs is called THEN add the header, and two recent items to be shown`() {
|
||||
val recentTab: TabSessionState = mockk()
|
||||
val mediaTab: TabSessionState = mockk()
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
|
||||
showRecentTabs(listOf(recentTab, mediaTab), items)
|
||||
|
||||
assertEquals(3, items.size)
|
||||
assertTrue(items[0] is AdapterItem.RecentTabsHeader)
|
||||
assertEquals(recentTab, (items[1] as AdapterItem.RecentTabItem).tab)
|
||||
assertEquals(mediaTab, (items[2] as AdapterItem.RecentTabItem).tab)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN one recent tab WHEN showRecentTabs is called THEN add the header and the recent tab to items shown`() {
|
||||
val recentTab: TabSessionState = mockk()
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
|
||||
showRecentTabs(listOf(recentTab), items)
|
||||
|
||||
assertEquals(2, items.size)
|
||||
assertTrue(items[0] is AdapterItem.RecentTabsHeader)
|
||||
assertEquals(recentTab, (items[1] as AdapterItem.RecentTabItem).tab)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN only one recent tab and no media tab WHEN showRecentTabs is called THEN add the recent item as a single one to be shown`() {
|
||||
val recentTab: TabSessionState = mockk()
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
|
||||
showRecentTabs(listOf(recentTab), items)
|
||||
|
||||
assertEquals(recentTab, (items[1] as AdapterItem.RecentTabItem).tab)
|
||||
assertSame(RecentTabsItemPosition.SINGLE, (items[1] as AdapterItem.RecentTabItem).position)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN two recent tabs WHEN showRecentTabs is called THEN add one item as top and one as bottom to be shown`() {
|
||||
val recentTab: TabSessionState = mockk()
|
||||
val mediaTab: TabSessionState = mockk()
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
|
||||
showRecentTabs(listOf(recentTab, mediaTab), items)
|
||||
|
||||
assertEquals(recentTab, (items[1] as AdapterItem.RecentTabItem).tab)
|
||||
assertSame(RecentTabsItemPosition.TOP, (items[1] as AdapterItem.RecentTabItem).position)
|
||||
assertEquals(mediaTab, (items[2] as AdapterItem.RecentTabItem).tab)
|
||||
assertSame(RecentTabsItemPosition.BOTTOM, (items[2] as AdapterItem.RecentTabItem).position)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `GIVEN three recent tabs WHEN showRecentTabs is called THEN add one recent item as top, one as middle and one as bottom to be shown`() {
|
||||
val recentTab1: TabSessionState = mockk()
|
||||
val recentTab2: TabSessionState = mockk()
|
||||
val mediaTab: TabSessionState = mockk()
|
||||
val items = mutableListOf<AdapterItem>()
|
||||
|
||||
showRecentTabs(listOf(recentTab1, recentTab2, mediaTab), items)
|
||||
|
||||
assertEquals(recentTab1, (items[1] as AdapterItem.RecentTabItem).tab)
|
||||
assertSame(RecentTabsItemPosition.TOP, (items[1] as AdapterItem.RecentTabItem).position)
|
||||
assertEquals(recentTab2, (items[2] as AdapterItem.RecentTabItem).tab)
|
||||
assertSame(RecentTabsItemPosition.MIDDLE, (items[2] as AdapterItem.RecentTabItem).position)
|
||||
assertEquals(mediaTab, (items[3] as AdapterItem.RecentTabItem).tab)
|
||||
assertSame(RecentTabsItemPosition.BOTTOM, (items[3] as AdapterItem.RecentTabItem).position)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue