Skip to content

Commit

Permalink
Merge pull request #469 from pranavkonidena/MOBILE-203/UserPages-1.4
Browse files Browse the repository at this point in the history
Mobile 203/user pages 1.4(Unit Tests)
  • Loading branch information
akshaaatt authored Aug 18, 2024
2 parents d1f8753 + 97b98b9 commit 548d904
Show file tree
Hide file tree
Showing 41 changed files with 1,464 additions and 137 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ dependencies {
implementation(libs.google.exoplayer.mediasession)

// Spotify SDK
implementation(files("./lib/spotify-app-remote-release-0.7.2.aar"))
api(project(":spotify-app-remote"))

// Networking and parsing
implementation(libs.jsoup)
Expand Down
182 changes: 182 additions & 0 deletions app/src/androidTest/java/org/listenbrainz/android/UserPagesTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
package org.listenbrainz.android

import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToIndex
import androidx.test.ext.junit.runners.AndroidJUnit4
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestDispatcher
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.listenbrainz.android.ui.screens.profile.BaseProfileScreen
import org.listenbrainz.android.ui.theme.ListenBrainzTheme
import org.listenbrainz.android.viewmodel.FeedViewModel
import org.listenbrainz.android.viewmodel.ListensViewModel
import org.listenbrainz.android.viewmodel.SocialViewModel
import org.listenbrainz.android.viewmodel.UserViewModel
import org.listenbrainz.sharedtest.mocks.MockAppPreferences
import org.listenbrainz.sharedtest.mocks.MockFeedRepository
import org.listenbrainz.sharedtest.mocks.MockListensRepository
import org.listenbrainz.sharedtest.mocks.MockRemotePlaybackHandler
import org.listenbrainz.sharedtest.mocks.MockSocialRepository
import org.listenbrainz.sharedtest.mocks.MockSocketRepository
import org.listenbrainz.sharedtest.mocks.MockUserRepository
import org.listenbrainz.sharedtest.utils.EntityTestUtils.testUsername

@RunWith(AndroidJUnit4::class)
@HiltAndroidTest
class UserPagesTest {

@get:Rule(order = 0)
var hiltRule = HiltAndroidRule(this)

@get:Rule(order = 1)
val rule = createComposeRule()

private lateinit var viewModel: UserViewModel
private lateinit var feedViewModel: FeedViewModel
private lateinit var listensViewModel: ListensViewModel
private lateinit var socialViewModel: SocialViewModel

@Before
fun setup() {
val testDispatcher: TestDispatcher = StandardTestDispatcher()
viewModel = UserViewModel(
MockAppPreferences(),
MockUserRepository(),
MockListensRepository(),
MockSocialRepository(),
testDispatcher
)
feedViewModel = FeedViewModel(
feedRepository = MockFeedRepository(),
socialRepository = MockSocialRepository(),
listensRepository = MockListensRepository(),
appPreferences = MockAppPreferences(),
remotePlaybackHandler = MockRemotePlaybackHandler(),
testDispatcher,
testDispatcher
)
listensViewModel = ListensViewModel(
repository = MockListensRepository(),
appPreferences = MockAppPreferences(),
socketRepository = MockSocketRepository(),
remotePlaybackHandler = MockRemotePlaybackHandler(),
testDispatcher
)
socialViewModel = SocialViewModel(
repository = MockSocialRepository(),
appPreferences = MockAppPreferences(),
remotePlaybackHandler = MockRemotePlaybackHandler(),
testDispatcher
)

rule.setContent {
runBlocking {
viewModel.getUserDataFromRemote(testUsername)
testDispatcher.scheduler.advanceUntilIdle()
}
val uiState by viewModel.uiState.collectAsState()
ListenBrainzTheme {
Scaffold {
it ->
BaseProfileScreen(
username = testUsername,
snackbarState = remember {
SnackbarHostState()
},
uiState = uiState,
onFollowClick = {},
onUnfollowClick = {},
goToUserProfile = { /*TODO*/ },
viewModel = viewModel,
feedViewModel = feedViewModel,
socialViewModel = socialViewModel,
listensViewModel = listensViewModel,
)
}

}
}
}

@Test
fun allTabsExistenceTest() {
// Verify that all tabs ("Listens", "Stats", "Taste") exist on the main screen
rule.onNodeWithText("Listens").assertExists()
rule.onNodeWithText("Stats").assertExists()
rule.onNodeWithText("Taste").assertExists()
}

@Test
fun listensTabScreenFlowTest() {
// Navigate to the "Listens" tab
rule.onNodeWithText("Listens").performClick()

// Verify that key elements on the "Listens" screen are present
rule.onNodeWithText("You have listened to").assertExists()
rule.onNodeWithText("Recent Listens").assertExists()
rule.onNodeWithText("Followers").assertExists()

// Locate the scrollable container for the "Listens" screen
val scrollableContainer = rule.onNodeWithTag("listensScreenScrollableContainer")

// Scroll to a specific index to ensure additional content is loaded
scrollableContainer.performScrollToIndex(10)

// Verify that more elements are present after scrolling
rule.onNodeWithText("Similar Users").assertExists()
}

@Test
fun statsTabScreenFlowTest() {
// Navigate to the "Stats" tab
rule.onNodeWithText("Stats").performClick()

// Verify that key elements on the "Stats" screen are present
rule.onNodeWithText("Global").assertExists()
rule.onNodeWithText("This Week").assertExists()
rule.onNodeWithText("This Month").assertExists()
rule.onNodeWithText("This Year").assertExists()

// Locate the scrollable container for the "Stats" screen
val scrollableContainer = rule.onNodeWithTag("statsScreenScrollableContainer")

// Scroll to a specific index to ensure additional content is loaded
scrollableContainer.performScrollToIndex(2)

// Verify that more elements are present after scrolling
rule.onNodeWithText("Artists").assertExists()
rule.onNodeWithText("Albums").assertExists()
rule.onNodeWithText("Songs").assertExists()

// Scroll further to check the presence of additional content
scrollableContainer.performScrollToIndex(3)
rule.onNodeWithText("Load More").assertExists()
}

@Test
fun tasteTabScreenFlowTest() {
// Navigate to the "Taste" tab
rule.onNodeWithText("Taste").performClick()

// Verify that key elements on the "Taste" screen are present
rule.onNodeWithText("Loved").assertExists()
rule.onNodeWithText("Hated").assertExists()
rule.onNodeWithText("Pins").assertExists()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ package org.listenbrainz.android.model

import com.google.gson.annotations.SerializedName

data class CurrentPins(
@SerializedName("pinned_recording") val pinnedRecording: PinnedRecording? = null
)

data class PinnedRecording(
@SerializedName("created" ) val created: Float? = null,
@SerializedName("row_id" ) val rowId: Int? = null,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.listenbrainz.android.repository.user

import org.listenbrainz.android.model.CurrentPins
import org.listenbrainz.android.model.Listens
import org.listenbrainz.android.model.PinnedRecording
import org.listenbrainz.android.model.user.AllPinnedRecordings
Expand All @@ -14,7 +15,7 @@ import org.listenbrainz.android.util.Resource
interface UserRepository {
suspend fun fetchUserListenCount (username: String?) : Resource<Listens?>
suspend fun fetchUserSimilarity(username: String? , otherUserName: String?) : Resource<UserSimilarityPayload?>
suspend fun fetchUserCurrentPins(username: String?) : Resource<PinnedRecording?>
suspend fun fetchUserCurrentPins(username: String?) : Resource<CurrentPins?>
suspend fun fetchUserPins(username: String?) : Resource<AllPinnedRecordings?>
//TODO: Move to artists VM once implemented
suspend fun getTopArtists(username: String?, rangeString: String = "all_time", count: Int = 25): Resource<TopArtists>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.listenbrainz.android.repository.user

import org.listenbrainz.android.model.CurrentPins
import org.listenbrainz.android.model.Listens
import org.listenbrainz.android.model.PinnedRecording
import org.listenbrainz.android.model.ResponseError
Expand Down Expand Up @@ -29,7 +30,7 @@ class UserRepositoryImpl @Inject constructor(
service.getUserSimilarity(username,otherUserName)
}

override suspend fun fetchUserCurrentPins(username: String?): Resource<PinnedRecording?> = parseResponse {
override suspend fun fetchUserCurrentPins(username: String?): Resource<CurrentPins?> = parseResponse {
if(username.isNullOrEmpty()) return ResponseError.BAD_REQUEST.asResource()
service.getUserCurrentPins(username)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.listenbrainz.android.service

import org.listenbrainz.android.model.CurrentPins
import org.listenbrainz.android.model.Listens
import org.listenbrainz.android.model.PinnedRecording
import org.listenbrainz.android.model.user.AllPinnedRecordings
import org.listenbrainz.android.model.user.TopAlbums
import org.listenbrainz.android.model.user.TopArtists
Expand All @@ -22,7 +22,7 @@ interface UserService {
suspend fun getUserSimilarity(@Path("user_name") username: String? , @Path("other_user_name") otherUserName: String?) : Response<UserSimilarityPayload?>

@GET("{user_name}/pins/current")
suspend fun getUserCurrentPins(@Path("user_name") username: String?) : Response<PinnedRecording?>
suspend fun getUserCurrentPins(@Path("user_name") username: String?) : Response<CurrentPins?>

@GET("{user_name}/pins")
suspend fun getUserPins(@Path("user_name") username: String?) : Response<AllPinnedRecordings?>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.listenbrainz.android.ui.components

import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
Expand All @@ -13,6 +14,7 @@ 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.material3.Divider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
Expand All @@ -22,6 +24,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale
Expand Down Expand Up @@ -51,6 +54,7 @@ fun ListenCardSmall(
trackName: String,
artistName: String,
coverArtUrl: String?,
listenCount: Int? = null,
@DrawableRes errorAlbumArt: Int = R.drawable.ic_coverartarchive_logo_no_text,
enableDropdownIcon: Boolean = false,
onDropdownIconClick: () -> Unit = {},
Expand Down Expand Up @@ -100,16 +104,28 @@ fun ListenCardSmall(
Spacer(modifier = Modifier.width(ListenBrainzTheme.paddings.coverArtAndTextGap))

TitleAndSubtitle(modifier = Modifier.padding(end = 6.dp), title = trackName, subtitle = artistName, titleColor = titleColor, subtitleColor = subtitleColor)

}

Box(
modifier = modifier
.fillMaxWidth(1f - mainContentFraction)
.align(Alignment.CenterEnd),
contentAlignment = Alignment.Center
) {

if(listenCount != null){
Box(
modifier = Modifier
.clip(RoundedCornerShape(16.dp))
.background(ListenBrainzTheme.colorScheme.followerChipSelected)
.padding(6.dp),
contentAlignment = Alignment.Center
) {
Text(
text = listenCount.toString(),
color = Color.Black
)
}
}
// Trailing content
if (enableTrailingContent) {
trailingContent(
Expand Down
Loading

0 comments on commit 548d904

Please sign in to comment.