Repository: wordpress-mobile/WordPress-Android (language: kotlin)

Issue:
[Reader] Implement pull-to-refresh in "Tags" feed



Discussion hints:
Thanks for reporting! 👍 <!-- generated_by_dangermattic -->


Pull request: [Tags Feed] Implement pull-to-refresh
Fixes #20707 

Add pull-to-refresh to Tags Feed, which reloads the user's sites and tags then "restarts" the Tags Feed, fetching newer posts for each of the tags.

This also fixes the wrong post image being shown (it was showing the site avatar instead of the featured image).

-----

## To Test:

1. Open Jetpack
2. Turn on `reader_tags_feed` feature config
3. Go to Reader
4. Go to `Tags` feed
5. Scroll a bit, waiting for the posts to load
6. Wait a few minutes (optional: go to another device/web and follow a new tag)
7. Pull to refresh in the `Tags` feed
8. **Verify** the tags are refreshed
9. **Verify** the posts are loaded for all tags

Note: also **verify** that the post images are now correct.

-----

## Regression Notes

1. Potential unintended areas of impact

    - N/A

10. What I did to test those areas of impact (or what existing automated tests I relied on)

    - N/A

11. What automated tests I added (or what prevented me from doing so)

    - Updated existing tests
    - Added unit tests for the refresh behavior

-----

## PR Submission Checklist:

- [x] I have completed the Regression Notes.
- [x] I have considered adding accessibility improvements for my changes.
- [x] I have considered if this change warrants user-facing release notes and have added them to `RELEASE-NOTES.txt` if necessary.

-----

## Testing Checklist (strike-out the not-applying and unnecessary ones):

- [ ] WordPress.com sites and self-hosted Jetpack sites.
- [x] Portrait and landscape orientations.
- [ ] Light and dark modes.
- [ ] Fonts: Larger, smaller and bold text.
- [ ] High contrast.
- [ ] Talkback.
- [ ] Languages with large words or with letters/accents not frequently used in English.
- [ ] Right-to-left languages. (Even if translation isn’t complete, formatting should still respect the right-to-left layout)
- [ ] Large and small screen sizes. (Tablet and smaller phones)
- [ ] Multi-tasking: Split screen and Pop-up view. (Android 10 or higher)


Test command: ./gradlew :WordPress:testWordpressVanillaDebugUnitTest --tests "org.wordpress.android.ui.reader.viewmodels.tagsfeed.ReaderTagsFeedUiStateMapperTest" --tests "org.wordpress.android.ui.reader.viewmodels.ReaderTagsFeedViewModelTest"

Fix patch:
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagsFeedFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagsFeedFragment.kt
index 5dea6134f1a9..fd66bbe0b089 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagsFeedFragment.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/ReaderTagsFeedFragment.kt
@@ -109,6 +109,10 @@ class ReaderTagsFeedFragment : ViewPagerFragment(R.layout.reader_tag_feed_fragme
                 is ActionEvent.OpenTagPostsFeed -> {
                     subFilterViewModel.setSubfilterFromTag(it.readerTag)
                 }
+
+                ActionEvent.RefreshTagsFeed -> {
+                    subFilterViewModel.updateTagsAndSites()
+                }
             }
         }
     }
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapper.kt
index 396150e18891..8088fffd39cd 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapper.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapper.kt
@@ -35,7 +35,7 @@ class ReaderTagsFeedUiStateMapper @Inject constructor(
                     ),
                     postTitle = it.title,
                     postExcerpt = it.excerpt,
-                    postImageUrl = it.blogImageUrl,
+                    postImageUrl = it.featuredImage,
                     postNumberOfLikesText = if (it.numLikes > 0) readerUtilsWrapper.getShortLikeLabelText(
                         numLikes = it.numLikes
                     ) else "",
@@ -77,11 +77,13 @@ class ReaderTagsFeedUiStateMapper @Inject constructor(
 
     fun mapInitialPostsUiState(
         tags: List<ReaderTag>,
+        isRefreshing: Boolean,
         onTagClick: (ReaderTag) -> Unit,
         onItemEnteredView: (ReaderTagsFeedViewModel.TagFeedItem) -> Unit,
+        onRefresh: () -> Unit,
     ): ReaderTagsFeedViewModel.UiState.Loaded =
         ReaderTagsFeedViewModel.UiState.Loaded(
-            tags.map { tag ->
+            data = tags.map { tag ->
                 ReaderTagsFeedViewModel.TagFeedItem(
                     tagChip = ReaderTagsFeedViewModel.TagChip(
                         tag = tag,
@@ -90,7 +92,9 @@ class ReaderTagsFeedUiStateMapper @Inject constructor(
                     postList = ReaderTagsFeedViewModel.PostList.Initial,
                     onItemEnteredView = onItemEnteredView,
                 )
-            }
+            },
+            isRefreshing = isRefreshing,
+            onRefresh = onRefresh,
         )
 
     fun mapLoadingTagFeedItem(
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedViewModel.kt
index 139c0c1179cf..586d9fa5357d 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedViewModel.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedViewModel.kt
@@ -55,11 +55,11 @@ class ReaderTagsFeedViewModel @Inject constructor(
      * [UiState]s: [UiState.Initial], [UiState.Loaded], [UiState.Loading], [UiState.Empty].
      */
     fun start(tags: List<ReaderTag>) {
-        // don't start again if the tags match
-        if (_uiStateFlow.value is UiState.Loaded &&
-            tags == (_uiStateFlow.value as UiState.Loaded).data.map { it.tagChip.tag }
-        ) {
-            return
+        // don't start again if the tags match, unless the user requested a refresh
+        (_uiStateFlow.value as? UiState.Loaded)?.let { loadedState ->
+            if (!loadedState.isRefreshing && tags == loadedState.data.map { it.tagChip.tag }) {
+                return
+            }
         }
 
         if (tags.isEmpty()) {
@@ -74,7 +74,13 @@ class ReaderTagsFeedViewModel @Inject constructor(
 
         // Initially add all tags to the list with the posts loading UI
         _uiStateFlow.update {
-            readerTagsFeedUiStateMapper.mapInitialPostsUiState(tags, ::onTagClick, ::onItemEnteredView)
+            readerTagsFeedUiStateMapper.mapInitialPostsUiState(
+                tags,
+                false,
+                ::onTagClick,
+                ::onItemEnteredView,
+                ::onRefresh
+            )
         }
     }
 
@@ -162,10 +168,18 @@ class ReaderTagsFeedViewModel @Inject constructor(
                     updatedLoadedData[existingIndex] = updatedItem
                 }
 
-            UiState.Loaded(updatedLoadedData)
+            (uiState as? UiState.Loaded)?.copy(data = updatedLoadedData) ?: UiState.Loaded(updatedLoadedData)
         }
     }
 
+    @VisibleForTesting
+    fun onRefresh() {
+        _uiStateFlow.update {
+            (it as? UiState.Loaded)?.copy(isRefreshing = true) ?: it
+        }
+        _actionEvents.value = ActionEvent.RefreshTagsFeed
+    }
+
     @VisibleForTesting
     fun onItemEnteredView(item: TagFeedItem) {
         if (item.postList != PostList.Initial) {
@@ -286,15 +300,15 @@ class ReaderTagsFeedViewModel @Inject constructor(
 
     private fun findTagFeedItemToUpdate(uiState: UiState.Loaded, postItemToUpdate: TagsFeedPostItem) =
         uiState.data.firstOrNull { tagFeedItem ->
-        tagFeedItem.postList is PostList.Loaded && tagFeedItem.postList.items.firstOrNull {
-            it.postId == postItemToUpdate.postId && it.blogId == postItemToUpdate.blogId
-        } != null
-    }
+            tagFeedItem.postList is PostList.Loaded && tagFeedItem.postList.items.firstOrNull {
+                it.postId == postItemToUpdate.postId && it.blogId == postItemToUpdate.blogId
+            } != null
+        }
 
     private fun likePostRemote(postItem: TagsFeedPostItem, isPostLikedUpdated: Boolean) {
         launch {
-            findPost(postItem.postId, postItem.blogId)?.let {
-                postLikeUseCase.perform(it, !it.isLikedByCurrentUser, ReaderTracker.SOURCE_TAGS_FEED).collect {
+            findPost(postItem.postId, postItem.blogId)?.let { post ->
+                postLikeUseCase.perform(post, !post.isLikedByCurrentUser, ReaderTracker.SOURCE_TAGS_FEED).collect {
                     when (it) {
                         is PostLikeUseCase.PostLikeState.Success -> {
                             // Re-enable like button without changing the current post item UI.
@@ -348,11 +362,18 @@ class ReaderTagsFeedViewModel @Inject constructor(
 
     sealed class ActionEvent {
         data class OpenTagPostsFeed(val readerTag: ReaderTag) : ActionEvent()
+
+        data object RefreshTagsFeed : ActionEvent()
     }
 
     sealed class UiState {
         data object Initial : UiState()
-        data class Loaded(val data: List<TagFeedItem>) : UiState()
+
+        data class Loaded(
+            val data: List<TagFeedItem>,
+            val isRefreshing: Boolean = false,
+            val onRefresh: () -> Unit = {},
+        ) : UiState()
 
         data object Loading : UiState()
 
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/compose/tagsfeed/ReaderTagsFeed.kt b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/compose/tagsfeed/ReaderTagsFeed.kt
index 6a89eb1584ed..b917a579862c 100644
--- a/WordPress/src/main/java/org/wordpress/android/ui/reader/views/compose/tagsfeed/ReaderTagsFeed.kt
+++ b/WordPress/src/main/java/org/wordpress/android/ui/reader/views/compose/tagsfeed/ReaderTagsFeed.kt
@@ -23,7 +23,11 @@ import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Button
 import androidx.compose.material.ButtonDefaults
 import androidx.compose.material.ContentAlpha
+import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.MaterialTheme
+import androidx.compose.material.pullrefresh.PullRefreshIndicator
+import androidx.compose.material.pullrefresh.pullRefresh
+import androidx.compose.material.pullrefresh.rememberPullRefreshState
 import androidx.compose.material.ripple.rememberRipple
 import androidx.compose.material3.Icon
 import androidx.compose.material3.Text
@@ -58,6 +62,8 @@ import org.wordpress.android.ui.reader.views.compose.filter.ReaderFilterChip
 import org.wordpress.android.ui.utils.UiString
 import org.wordpress.android.util.AppLog
 
+private const val LOADING_POSTS_COUNT = 5
+
 @Composable
 fun ReaderTagsFeed(uiState: UiState) {
     Box(
@@ -77,46 +83,66 @@ fun ReaderTagsFeed(uiState: UiState) {
     }
 }
 
+@OptIn(ExperimentalMaterialApi::class)
 @Composable
 private fun Loaded(uiState: UiState.Loaded) {
-    LazyColumn(
+    val pullRefreshState = rememberPullRefreshState(
+        refreshing = uiState.isRefreshing,
+        onRefresh = {
+            uiState.onRefresh()
+        }
+    )
+
+    Box(
         modifier = Modifier
-            .fillMaxSize(),
+            .fillMaxSize()
+            .pullRefresh(state = pullRefreshState),
     ) {
-        items(
-            items = uiState.data,
-        ) { item ->
-            val tagChip = item.tagChip
-            val postList = item.postList
+        LazyColumn(
+            modifier = Modifier
+                .fillMaxSize(),
+        ) {
+            items(
+                items = uiState.data,
+            ) { item ->
+                val tagChip = item.tagChip
+                val postList = item.postList
 
-            LaunchedEffect(Unit) {
-                item.onEnteredView()
-            }
+                LaunchedEffect(item.postList) {
+                    item.onEnteredView()
+                }
 
-            val backgroundColor = if (isSystemInDarkTheme()) {
-                AppColor.White.copy(alpha = 0.12F)
-            } else {
-                AppColor.Black.copy(alpha = 0.08F)
-            }
-            Spacer(modifier = Modifier.height(Margin.Large.value))
-            // Tag chip UI
-            ReaderFilterChip(
-                modifier = Modifier.padding(
-                    start = Margin.Large.value,
-                ),
-                text = UiString.UiStringText(tagChip.tag.tagTitle),
-                onClick = { tagChip.onTagClick(tagChip.tag) },
-                height = 36.dp,
-            )
-            Spacer(modifier = Modifier.height(Margin.Large.value))
-            // Posts list UI
-            when (postList) {
-                is PostList.Initial, is PostList.Loading -> PostListLoading()
-                is PostList.Loaded -> PostListLoaded(postList, tagChip, backgroundColor)
-                is PostList.Error -> PostListError(backgroundColor, tagChip, postList)
+                val backgroundColor = if (isSystemInDarkTheme()) {
+                    AppColor.White.copy(alpha = 0.12F)
+                } else {
+                    AppColor.Black.copy(alpha = 0.08F)
+                }
+                Spacer(modifier = Modifier.height(Margin.Large.value))
+                // Tag chip UI
+                ReaderFilterChip(
+                    modifier = Modifier.padding(
+                        start = Margin.Large.value,
+                    ),
+                    text = UiString.UiStringText(tagChip.tag.tagTitle),
+                    onClick = { tagChip.onTagClick(tagChip.tag) },
+                    height = 36.dp,
+                )
+                Spacer(modifier = Modifier.height(Margin.Large.value))
+                // Posts list UI
+                when (postList) {
+                    is PostList.Initial, is PostList.Loading -> PostListLoading()
+                    is PostList.Loaded -> PostListLoaded(postList, tagChip, backgroundColor)
+                    is PostList.Error -> PostListError(backgroundColor, tagChip, postList)
+                }
+                Spacer(modifier = Modifier.height(Margin.ExtraExtraMediumLarge.value))
             }
-            Spacer(modifier = Modifier.height(Margin.ExtraExtraMediumLarge.value))
         }
+
+        PullRefreshIndicator(
+            refreshing = uiState.isRefreshing,
+            state = pullRefreshState,
+            modifier = Modifier.align(Alignment.TopCenter),
+        )
     }
 }
 
@@ -148,14 +174,12 @@ private fun Loading() {
                 LazyRow(
                     modifier = Modifier
                         .fillMaxWidth(),
-                    contentPadding = PaddingValues(horizontal = 12.dp),
                     userScrollEnabled = false,
+                    horizontalArrangement = Arrangement.spacedBy(Margin.Large.value),
+                    contentPadding = PaddingValues(horizontal = Margin.Large.value),
                 ) {
-                    item {
+                    items(LOADING_POSTS_COUNT) {
                         ReaderTagsFeedPostListItemLoading()
-                        Spacer(Modifier.width(12.dp))
-                        ReaderTagsFeedPostListItemLoading()
-                        Spacer(Modifier.width(12.dp))
                     }
                 }
             }
@@ -240,16 +264,14 @@ private fun PostListLoading() {
         modifier = Modifier
             .fillMaxWidth(),
         userScrollEnabled = false,
+        horizontalArrangement = Arrangement.spacedBy(Margin.ExtraMediumLarge.value),
         contentPadding = PaddingValues(
             start = Margin.Large.value,
             end = Margin.Large.value
         ),
     ) {
-        item {
-            ReaderTagsFeedPostListItemLoading()
-            Spacer(Modifier.width(Margin.ExtraMediumLarge.value))
+        items(LOADING_POSTS_COUNT) {
             ReaderTagsFeedPostListItemLoading()
-            Spacer(Modifier.width(Margin.ExtraMediumLarge.value))
         }
     }
 }
@@ -272,9 +294,9 @@ private fun PostListLoaded(
         items(
             items = postList.items,
         ) { postItem ->
-                ReaderTagsFeedPostListItem(
-                    item = postItem
-                )
+            ReaderTagsFeedPostListItem(
+                item = postItem
+            )
         }
         item {
             val baseColor = if (isSystemInDarkTheme()) AppColor.White else AppColor.Black


Test patch:
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderTagsFeedViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderTagsFeedViewModelTest.kt
index 0db161ad16f8..84e494c2eb1d 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderTagsFeedViewModelTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/ReaderTagsFeedViewModelTest.kt
@@ -20,6 +20,7 @@ import org.mockito.kotlin.verifyNoInteractions
 import org.mockito.kotlin.whenever
 import org.wordpress.android.BaseUnitTest
 import org.wordpress.android.datasets.wrappers.ReaderPostTableWrapper
+import org.wordpress.android.getOrAwaitValue
 import org.wordpress.android.models.ReaderPost
 import org.wordpress.android.models.ReaderPostList
 import org.wordpress.android.models.ReaderTag
@@ -288,11 +289,16 @@ class ReaderTagsFeedViewModelTest : BaseUnitTest() {
             posts2
         }
         mockMapInitialTagFeedItems()
+        mockMapLoadingTagFeedItems()
         mockMapLoadedTagFeedItems()
 
         // When
         viewModel.start(listOf(tag1, tag2))
         advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag1))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag2))
+        advanceUntilIdle()
         val firstCollectedStates = collectedUiStates.toList()
         Mockito.clearInvocations(readerPostRepository)
 
@@ -304,6 +310,54 @@ class ReaderTagsFeedViewModelTest : BaseUnitTest() {
         verifyNoInteractions(readerPostRepository)
     }
 
+    @Suppress("LongMethod")
+    @Test
+    fun `given tags fetched, when start again refreshing, then move back to initial state`() = testCollectingUiStates {
+        // Given
+        val tag1 = ReaderTestUtils.createTag("tag1")
+        val tag2 = ReaderTestUtils.createTag("tag2")
+        val posts1 = ReaderPostList().apply {
+            add(ReaderPost())
+        }
+        val posts2 = ReaderPostList().apply {
+            add(ReaderPost())
+        }
+        whenever(readerPostRepository.fetchNewerPostsForTag(tag1)).doSuspendableAnswer {
+            delay(100)
+            posts1
+        }
+        whenever(readerPostRepository.fetchNewerPostsForTag(tag2)).doSuspendableAnswer {
+            delay(200)
+            posts2
+        }
+        mockMapInitialTagFeedItems()
+        mockMapLoadingTagFeedItems()
+        mockMapLoadedTagFeedItems()
+
+        // When
+        viewModel.start(listOf(tag1, tag2))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag1))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag2))
+        advanceUntilIdle()
+
+        viewModel.onRefresh()
+
+        // Then
+        viewModel.start(listOf(tag1, tag2))
+        advanceUntilIdle()
+
+        val loadedState = collectedUiStates.last() as ReaderTagsFeedViewModel.UiState.Loaded
+        assertThat(loadedState.data).isEqualTo(
+            listOf(
+                    getInitialTagFeedItem(tag1),
+                    getInitialTagFeedItem(tag2)
+                )
+            )
+        assertThat(loadedState.isRefreshing).isFalse()
+    }
+
     @Suppress("LongMethod")
     @Test
     fun `given no tags requested, when start, then UI state should update properly`() = testCollectingUiStates {
@@ -318,6 +372,83 @@ class ReaderTagsFeedViewModelTest : BaseUnitTest() {
         assertThat(collectedUiStates).last().isInstanceOf(ReaderTagsFeedViewModel.UiState.Empty::class.java)
     }
 
+    @Suppress("LongMethod")
+    @Test
+    fun `given tags fetched, when refreshing, then update isRefreshing status`() = testCollectingUiStates {
+        // Given
+        val tag1 = ReaderTestUtils.createTag("tag1")
+        val tag2 = ReaderTestUtils.createTag("tag2")
+        val posts1 = ReaderPostList().apply {
+            add(ReaderPost())
+        }
+        val posts2 = ReaderPostList().apply {
+            add(ReaderPost())
+        }
+        whenever(readerPostRepository.fetchNewerPostsForTag(tag1)).doSuspendableAnswer {
+            delay(100)
+            posts1
+        }
+        whenever(readerPostRepository.fetchNewerPostsForTag(tag2)).doSuspendableAnswer {
+            delay(200)
+            posts2
+        }
+        mockMapInitialTagFeedItems()
+        mockMapLoadingTagFeedItems()
+        mockMapLoadedTagFeedItems()
+
+        // When
+        viewModel.start(listOf(tag1, tag2))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag1))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag2))
+        advanceUntilIdle()
+
+        // Then
+        viewModel.onRefresh()
+
+        val loadedState = collectedUiStates.last() as ReaderTagsFeedViewModel.UiState.Loaded
+        assertThat(loadedState.isRefreshing).isTrue()
+    }
+
+    @Suppress("LongMethod")
+    @Test
+    fun `given tags fetched, when refreshing, then RefreshTagsFeed action is posted`() = testCollectingUiStates {
+        // Given
+        val tag1 = ReaderTestUtils.createTag("tag1")
+        val tag2 = ReaderTestUtils.createTag("tag2")
+        val posts1 = ReaderPostList().apply {
+            add(ReaderPost())
+        }
+        val posts2 = ReaderPostList().apply {
+            add(ReaderPost())
+        }
+        whenever(readerPostRepository.fetchNewerPostsForTag(tag1)).doSuspendableAnswer {
+            delay(100)
+            posts1
+        }
+        whenever(readerPostRepository.fetchNewerPostsForTag(tag2)).doSuspendableAnswer {
+            delay(200)
+            posts2
+        }
+        mockMapInitialTagFeedItems()
+        mockMapLoadingTagFeedItems()
+        mockMapLoadedTagFeedItems()
+
+        // When
+        viewModel.start(listOf(tag1, tag2))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag1))
+        advanceUntilIdle()
+        viewModel.onItemEnteredView(getInitialTagFeedItem(tag2))
+        advanceUntilIdle()
+
+        // Then
+        viewModel.onRefresh()
+
+        val action = viewModel.actionEvents.getOrAwaitValue()
+        assertThat(action).isEqualTo(ActionEvent.RefreshTagsFeed)
+    }
     @Test
     fun `Should update UI immediately when like button is tapped`() = testCollectingUiStates {
         // Given
@@ -406,7 +537,7 @@ class ReaderTagsFeedViewModelTest : BaseUnitTest() {
     }
 
     private fun mockMapInitialTagFeedItems() {
-        whenever(readerTagsFeedUiStateMapper.mapInitialPostsUiState(any(), any(), any()))
+        whenever(readerTagsFeedUiStateMapper.mapInitialPostsUiState(any(), any(), any(), any(), any()))
             .thenAnswer {
                 val tags = it.getArgument<List<ReaderTag>>(0)
                 ReaderTagsFeedViewModel.UiState.Loaded(
diff --git a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapperTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapperTest.kt
index d5a4309d4086..49f11f698f2e 100644
--- a/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapperTest.kt
+++ b/WordPress/src/test/java/org/wordpress/android/ui/reader/viewmodels/tagsfeed/ReaderTagsFeedUiStateMapperTest.kt
@@ -35,7 +35,7 @@ class ReaderTagsFeedUiStateMapperTest : BaseUnitTest() {
             blogName = "Name"
             title = "Title"
             excerpt = "Excerpt"
-            blogImageUrl = "url"
+            featuredImage = "url"
             numLikes = 5
             numReplies = 10
             isLikedByCurrentUser = true
@@ -95,7 +95,7 @@ class ReaderTagsFeedUiStateMapperTest : BaseUnitTest() {
                         postDateLine = dateLine,
                         postTitle = readerPost.title,
                         postExcerpt = readerPost.excerpt,
-                        postImageUrl = readerPost.blogImageUrl,
+                        postImageUrl = readerPost.featuredImage,
                         postNumberOfLikesText = numberLikesText,
                         postNumberOfCommentsText = numberCommentsText,
                         isPostLiked = readerPost.isLikedByCurrentUser,
@@ -157,6 +157,7 @@ class ReaderTagsFeedUiStateMapperTest : BaseUnitTest() {
         // Given
         val onTagClick: (ReaderTag) -> Unit = {}
         val onItemEnteredView: (ReaderTagsFeedViewModel.TagFeedItem) -> Unit = {}
+        val onRefresh: () -> Unit = {}
         val tag1 = ReaderTag(
             "tag",
             "tag",
@@ -176,8 +177,10 @@ class ReaderTagsFeedUiStateMapperTest : BaseUnitTest() {
         // When
         val actual = classToTest.mapInitialPostsUiState(
             tags = tags,
+            isRefreshing = true,
             onTagClick = onTagClick,
             onItemEnteredView = onItemEnteredView,
+            onRefresh = onRefresh,
         )
 
         // Then
@@ -199,7 +202,9 @@ class ReaderTagsFeedUiStateMapperTest : BaseUnitTest() {
                     postList = ReaderTagsFeedViewModel.PostList.Initial,
                     onItemEnteredView = onItemEnteredView,
                 )
-            )
+            ),
+            isRefreshing = true,
+            onRefresh = onRefresh,
         )
         assertEquals(expected, actual)
     }