Commit cfcd5060 authored by Shivansh Saini's avatar Shivansh Saini

UI test: PlaylistActivity drag

Signed-off-by: Shivansh Saini's avatarShivansh Saini <shivanshs9@gmail.com>
parent 7e12d25b
......@@ -4,12 +4,14 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.rule.GrantPermissionRule
import androidx.test.runner.AndroidJUnit4
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
import org.videolan.medialibrary.MLServiceLocator
@RunWith(AndroidJUnit4::class)
open class BaseUITest {
abstract class BaseUITest {
@Rule
@JvmField
val storagePermissionGrant = GrantPermissionRule.grant(
......@@ -17,4 +19,12 @@ open class BaseUITest {
val context: Context = ApplicationProvider.getApplicationContext()
@Before
fun init() {
context.startMedialibrary()
beforeTest()
}
abstract fun beforeTest()
}
\ No newline at end of file
package org.videolan.vlc
import androidx.paging.PagedListAdapter
import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
......@@ -24,9 +25,21 @@ fun withMediaType(mediaType: Int): DiffAdapterMatcher<MediaLibraryItem> {
}
}
fun withMediaItem(mediaItem: MediaLibraryItem?): DiffAdapterMatcher<MediaLibraryItem> {
return object : DiffAdapterMatcher<MediaLibraryItem>() {
override fun describeTo(description: Description) {
description.appendText("with media item: ${mediaItem?.title}")
}
override fun matchesSafely(item: MediaLibraryItem?): Boolean {
return mediaItem?.equals(item) ?: true
}
}
}
@ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
fun <D, VH : RecyclerView.ViewHolder> findFirstPosition(adapter: DiffUtilAdapter<D, VH>?, vararg matchers: DiffAdapterMatcher<D>): Int = adapter?.let {
fun <D, VH : RecyclerView.ViewHolder> findFirstPosition(adapter: DiffUtilAdapter<D, VH>?, vararg matchers: DiffAdapterMatcher<D>): Int = adapter?.let { it ->
val iter = it.dataset.iterator().withIndex()
while (iter.hasNext()) {
val index = iter.next()
......@@ -35,3 +48,15 @@ fun <D, VH : RecyclerView.ViewHolder> findFirstPosition(adapter: DiffUtilAdapter
}
return -1
} ?: -1
@ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
fun <D, VH : RecyclerView.ViewHolder> findFirstPosition(adapter: PagedListAdapter<D, VH>?, vararg matchers: DiffAdapterMatcher<D>): Int = adapter?.let {
val iter = it.currentList!!.iterator().withIndex()
while (iter.hasNext()) {
val index = iter.next()
if (matchers.all { it.matches(index.value) })
return index.index
}
return -1
} ?: -1
package org.videolan.vlc
import android.content.Context
import android.content.res.Resources
import android.graphics.drawable.ColorDrawable
import android.view.View
import android.view.ViewGroup
......@@ -28,11 +29,6 @@ import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.appcompat.view.menu.ActionMenuItemView
class RecyclerViewMatcher(@IdRes private val recyclerViewId: Int) {
var recyclerView: RecyclerView? = null
......@@ -46,6 +42,14 @@ class RecyclerViewMatcher(@IdRes private val recyclerViewId: Int) {
override fun describeTo(description: Description) {
description.appendText("Recycler view doesn't have item at position $position")
if (targetViewId != -1) {
val idDescription = try {
childView?.resources?.getResourceName(targetViewId) ?: targetViewId.toString()
} catch (e: Resources.NotFoundException) {
targetViewId.toString()
}
description.appendText(" and with view $idDescription")
}
}
override fun matchesSafely(view: View): Boolean {
......
package org.videolan.vlc.gui
import android.content.Intent
import android.graphics.Point
import android.widget.EditText
import androidx.databinding.ViewDataBinding
import androidx.test.espresso.Espresso.*
import androidx.test.espresso.action.*
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.*
import androidx.test.espresso.contrib.DrawerActions.*
import androidx.test.espresso.matcher.RootMatchers.*
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.rule.ActivityTestRule
import com.google.android.material.internal.NavigationMenuItemView
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import org.hamcrest.Matchers.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
import org.videolan.vlc.BaseUITest
import org.videolan.vlc.R
import org.videolan.vlc.*
import org.videolan.vlc.databinding.AudioBrowserItemBinding
import org.videolan.vlc.gui.audio.AudioBrowserAdapter
import org.videolan.vlc.gui.audio.AudioBrowserFragment
import org.videolan.vlc.util.EXTRA_TARGET
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
class PlaylistActivityUITest: BaseUITest() {
@Rule
@JvmField
val activityTestRule = ActivityTestRule(PlaylistActivity::class.java, true, false)
lateinit var activity: PlaylistActivity
override fun beforeTest() {
// TODO: Hack because of IO Dispatcher used in MediaParsingService channel
Thread.sleep(3 * 1000)
val ml = AbstractMedialibrary.getInstance()
val pl = ml.createPlaylist("test")
pl.append(ml.getPagedVideos(AbstractMedialibrary.SORT_DEFAULT, false, 5, 0).map { it.id })
pl.append(ml.getPagedAudio(AbstractMedialibrary.SORT_DEFAULT, false, 5, 0).map { it.id })
val intent = Intent().apply {
putExtra(AudioBrowserFragment.TAG_ITEM, pl)
}
activityTestRule.launchActivity(intent)
activity = activityTestRule.activity
}
@Test
fun whenAtTestPlaylist_checkMediaList() {
onView(withId(R.id.songs))
.check(matches(sizeOfAtLeast(1)))
}
@Test
fun whenAtTestPlaylist_checkDragMediaWorks() {
val rvMatcher = withRecyclerView(R.id.songs)
onView(rvMatcher.atPosition(0))
.check(matches(isDisplayed()))
val recyclerView = rvMatcher.recyclerView!!
val adapter = recyclerView.adapter as AudioBrowserAdapter
val count = recyclerView.adapter!!.itemCount
val firstItem = (recyclerView.findViewHolderForAdapterPosition(0) as AudioBrowserAdapter.AbstractMediaItemViewHolder<AudioBrowserItemBinding>).binding.item.also { println(it!!.title) }
val finalCoord = CoordinatesProvider {
val coords = GeneralLocation.BOTTOM_CENTER.calculateCoordinates(it)
coords[1] = recyclerView.measuredHeight.toFloat() //point.y.toFloat()
coords
}
onView(rvMatcher.atPositionOnView(0, R.id.item_move))
.perform(GeneralSwipeAction(Swipe.SLOW, GeneralLocation.TOP_CENTER, finalCoord, Press.FINGER))
// To reflect the update in adapter's dataset
Thread.sleep(1000)
val newPos = findFirstPosition(adapter, withMediaItem(firstItem))
assertThat(newPos, equalTo(count - 1))
}
}
package org.videolan.vlc.gui
import android.content.Intent
import android.widget.EditText
import androidx.test.espresso.Espresso.*
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.*
import androidx.test.espresso.contrib.DrawerActions.*
import androidx.test.espresso.matcher.RootMatchers.*
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.rule.ActivityTestRule
import com.google.android.material.internal.NavigationMenuItemView
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.ObsoleteCoroutinesApi
import org.hamcrest.Matchers.*
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.videolan.medialibrary.interfaces.AbstractMedialibrary
import org.videolan.vlc.BaseUITest
import org.videolan.vlc.R
import org.videolan.vlc.*
import org.videolan.vlc.util.EXTRA_TARGET
@ObsoleteCoroutinesApi
@ExperimentalCoroutinesApi
class PlaylistFragmentUITest: BaseUITest() {
@Rule
@JvmField
val activityTestRule = ActivityTestRule(MainActivity::class.java, true, false)
lateinit var activity: MainActivity
override fun beforeTest() {
val intent = Intent().apply {
putExtra(EXTRA_TARGET, R.id.nav_playlists)
}
activityTestRule.launchActivity(intent)
activity = activityTestRule.activity
}
private fun createDummyPlaylist() {
val ml = AbstractMedialibrary.getInstance()
val pl = ml.createPlaylist("test")
pl.append(ml.getPagedVideos(AbstractMedialibrary.SORT_DEFAULT, false, 5, 0).map { it.id })
pl.append(ml.getPagedAudio(AbstractMedialibrary.SORT_DEFAULT, false, 5, 0).map { it.id })
}
@Test
fun whenAtRoot_checkAppbar() {
onView(withId(R.id.ml_menu_filter))
.check(matches(isDisplayed()))
onView(withId(R.id.ml_menu_sortby))
.check(matches(isDisplayed()))
openActionBarOverflowOrOptionsMenu(context)
onView(withText(R.string.refresh))
.inRoot(isPlatformPopup())
.check(matches(isDisplayed()))
}
@Test
fun whenNoMedia_checkListEmpty() {
onView(withId(R.id.audio_list))
.check(matches(withCount(equalTo(0))))
}
@Test
fun whenPlaylistAddedFromDirectoriesView_checkPlaylistUpdated() {
// Navigate to directories view
onView(withId(R.id.root_container))
.perform(open())
onView(allOf(instanceOf(NavigationMenuItemView::class.java), hasDescendant(withText(R.string.directories))))
.check(matches(isDisplayed()))
.perform(click())
// Add Internal Storage to playlist
onView(withRecyclerView(R.id.network_list).atPosition(3))
.perform(longClick())
openActionBarOverflowOrOptionsMenu(context)
onView(allOf(isDescendantOfA(withId(R.id.dialog_playlist_name)), instanceOf(EditText::class.java)))
.perform(click(), typeTextIntoFocusedView("storage"))
onView(withId(R.id.dialog_playlist_save))
.perform(click())
// Because playlist is saved with IO dispatcher.
// TODO: Once tests_vm is merged, I'll update the WorkersKt to use CoroutineContextProvider and remove this hack.
Thread.sleep(2000)
// Navigate back to playlists view
onView(withId(R.id.root_container))
.perform(open())
onView(allOf(instanceOf(NavigationMenuItemView::class.java), hasDescendant(withText(R.string.playlists))))
.check(matches(isDisplayed()))
.perform(click())
onView(withId(R.id.audio_list))
.check(matches(withCount(equalTo(1))))
onView(withRecyclerView(R.id.audio_list).atPosition(0))
.check(matches(hasDescendant(withText("storage"))))
}
@Test
fun whenOnePlaylist_checkCardDetails() {
createDummyPlaylist()
Thread.sleep(1500)
onView(withId(R.id.audio_list))
.check(matches(withCount(equalTo(1))))
val rvMatcher = withRecyclerView(R.id.audio_list)
onView(rvMatcher.atPositionOnView(0, R.id.title))
.check(matches(withText("test")))
}
}
\ No newline at end of file
......@@ -43,8 +43,7 @@ class FileBrowserFragmentUITest : BaseUITest() {
lateinit var activity: MainActivity
@Before
fun init() {
override fun beforeTest() {
val intent = Intent().apply {
putExtra(EXTRA_TARGET, R.id.nav_directories)
}
......
......@@ -24,8 +24,7 @@ class FilePickerFragmentUITest : BaseUITest() {
lateinit var activity: FilePickerActivity
@Before
fun init() {
override fun beforeTest() {
activity = activityTestRule.activity
}
......
......@@ -34,8 +34,7 @@ class StorageBrowserFragmentUITest : BaseUITest() {
lateinit var activity: SecondaryActivity
@Before
fun init() {
override fun beforeTest() {
val intent = Intent().apply {
putExtra(SecondaryActivity.KEY_FRAGMENT, SecondaryActivity.STORAGE_BROWSER)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment