Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ android {
testImplementation(libs.robolectric)
testImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-test-manifest")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
androidTestImplementation(libs.androidx.runner)
androidTestImplementation(libs.androidx.rules)
androidTestImplementation(libs.androidx.core.ktx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package com.simplecityapps.shuttle.smoke
import android.Manifest
import android.content.SharedPreferences
import android.os.Build
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.recyclerview.widget.RecyclerView
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.launchActivity
Expand Down Expand Up @@ -34,10 +38,13 @@ import org.junit.Test
class NavigationSmokeTest {

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

@get:Rule(order = 1)
var permissionRule: GrantPermissionRule = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val composeRule = createEmptyComposeRule()

@get:Rule(order = 2)
val permissionRule: GrantPermissionRule = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
GrantPermissionRule.grant(Manifest.permission.READ_MEDIA_AUDIO)
} else {
GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE)
Expand Down Expand Up @@ -93,7 +100,9 @@ class NavigationSmokeTest {
onView(withId(R.id.libraryFragment)).perform(click())

onView(withText("Songs")).perform(click())
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(withText("Highway to Hell"))))
composeRule.waitUntil(5_000) {
composeRule.onAllNodesWithText("Highway to Hell").fetchSemanticsNodes().isNotEmpty()
}

onView(withText("Albums")).perform(click())
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(isDisplayed())))
Expand All @@ -102,8 +111,10 @@ class NavigationSmokeTest {
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(isDisplayed())))

onView(withText("Genres")).perform(click())
// Genres uses Compose — just verify the tab didn't crash
waitForView(allOf(withId(R.id.tabLayout), isDescendantOfA(withId(R.id.constraintLayout))))
// Genres uses Compose with a FastScroller delay that keeps the Compose
// idling resource busy. Advance the clock past it before Espresso tries
// to click the Playlists tab.
composeRule.mainClock.advanceTimeBy(2000)

onView(withText("Playlists")).perform(click())
waitForView(allOf(withId(R.id.recyclerView), isDisplayed()))
Expand Down Expand Up @@ -141,12 +152,13 @@ class NavigationSmokeTest {
fun playbackScreens() {
scenario = launchActivity()

// Play a song
// Play a song (Songs tab is Compose)
onView(withId(R.id.libraryFragment)).perform(click())
onView(withText("Songs")).perform(click())
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(isDisplayed())))
onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
composeRule.waitUntil(5_000) {
composeRule.onAllNodesWithText("Highway to Hell").fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithText("Highway to Hell").performClick()

// Mini player
waitForView(allOf(withId(R.id.titleTextView), isDescendantOfA(withId(R.id.sheet1PeekView))))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package com.simplecityapps.shuttle.smoke
import android.Manifest
import android.content.SharedPreferences
import android.os.Build
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.recyclerview.widget.RecyclerView
import androidx.test.core.app.ActivityScenario
import androidx.test.core.app.launchActivity
Expand Down Expand Up @@ -37,10 +41,13 @@ import org.junit.Test
class SmokeTestSuite {

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

@get:Rule(order = 1)
var permissionRule: GrantPermissionRule = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val composeRule = createEmptyComposeRule()

@get:Rule(order = 2)
val permissionRule: GrantPermissionRule = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
GrantPermissionRule.grant(Manifest.permission.READ_MEDIA_AUDIO)
} else {
GrantPermissionRule.grant(Manifest.permission.READ_EXTERNAL_STORAGE)
Expand Down Expand Up @@ -97,8 +104,10 @@ class SmokeTestSuite {
onView(withText("Songs"))
.perform(click())

// Wait for Flow to emit data, then verify a song from our test data is visible
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(withText("Highway to Hell"))))
// Songs tab is Compose — use compose rule to find text
composeRule.waitUntil(5_000) {
composeRule.onAllNodesWithText("Highway to Hell").fetchSemanticsNodes().isNotEmpty()
}
}

@Test
Expand All @@ -111,10 +120,11 @@ class SmokeTestSuite {
onView(withText("Songs"))
.perform(click())

// Wait for Flow to emit data, then tap the first song in the list
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(isDisplayed())))
onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
// Songs tab is Compose — wait then tap
composeRule.waitUntil(5_000) {
composeRule.onAllNodesWithText("Highway to Hell").fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithText("Highway to Hell").performClick()

// Mini player should show a song title
waitForView(allOf(withId(R.id.titleTextView), isDescendantOfA(withId(R.id.sheet1PeekView))))
Expand Down Expand Up @@ -162,10 +172,11 @@ class SmokeTestSuite {
onView(withText("Songs"))
.perform(click())

// Wait for Flow to emit data, then tap the first song
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(isDisplayed())))
onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
// Songs tab is Compose — wait then tap
composeRule.waitUntil(5_000) {
composeRule.onAllNodesWithText("Highway to Hell").fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithText("Highway to Hell").performClick()

// Wait for mini player, then expand to full playback
waitForView(allOf(withId(R.id.sheet1PeekView), isDisplayed()))
Expand All @@ -190,10 +201,11 @@ class SmokeTestSuite {
onView(withText("Songs"))
.perform(click())

// Wait for Flow to emit data, then tap the first song
waitForView(allOf(withId(R.id.recyclerView), hasDescendant(isDisplayed())))
onView(allOf(withId(R.id.recyclerView), isDisplayed()))
.perform(RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(0, click()))
// Songs tab is Compose — wait then tap
composeRule.waitUntil(5_000) {
composeRule.onAllNodesWithText("Highway to Hell").fetchSemanticsNodes().isNotEmpty()
}
composeRule.onNodeWithText("Highway to Hell").performClick()

// Wait for mini player to appear before expanding to queue
waitForView(allOf(withId(R.id.titleTextView), isDescendantOfA(withId(R.id.sheet1PeekView))))
Expand Down
Loading