Updated the method to verify system notifications and added new tests for media notifications (#9330)
parent
cee34893ed
commit
5f68d6cb29
|
@ -0,0 +1,26 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Audio_Test_Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="testContent">Page content: audio player</p>
|
||||||
|
<div class="audioPlayer">
|
||||||
|
<audio id="audioSample" controls loop>
|
||||||
|
<source src="../resources/audioSample.mp3">
|
||||||
|
</audio>
|
||||||
|
</div>
|
||||||
|
<div class="playbackState">
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const audio = document.querySelector('audio');
|
||||||
|
|
||||||
|
audio.addEventListener('playing', (event) => {
|
||||||
|
document.querySelector('.playbackState').innerText="Media file is playing"
|
||||||
|
});
|
||||||
|
|
||||||
|
audio.addEventListener('pause', (event) => {
|
||||||
|
document.querySelector('.playbackState').innerText="Media file is paused"
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,26 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Video_Test_Page</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p id="testContent">Page content: video player</p>
|
||||||
|
<div id="video-container">
|
||||||
|
<video id="videoSample" width="320" height="240" controls loop>
|
||||||
|
<source src="../resources/videoSample.webm">
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
<div class="playbackState">
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
const video = document.querySelector('video');
|
||||||
|
|
||||||
|
video.addEventListener('playing', (event) => {
|
||||||
|
document.querySelector('.playbackState').innerText="Media file is playing";
|
||||||
|
});
|
||||||
|
|
||||||
|
video.addEventListener('pause', (event) => {
|
||||||
|
document.querySelector('.playbackState').innerHTML="Media file is paused";
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
Binary file not shown.
|
@ -18,7 +18,7 @@ object TestAssetHelper {
|
||||||
val waitingTime: Long = TimeUnit.SECONDS.toMillis(15)
|
val waitingTime: Long = TimeUnit.SECONDS.toMillis(15)
|
||||||
val waitingTimeShort: Long = TimeUnit.SECONDS.toMillis(1)
|
val waitingTimeShort: Long = TimeUnit.SECONDS.toMillis(1)
|
||||||
|
|
||||||
data class TestAsset(val url: Uri, val content: String)
|
data class TestAsset(val url: Uri, val content: String, val title: String)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hosts 3 simple websites, found at androidTest/assets/pages/generic[1|2|3].html
|
* Hosts 3 simple websites, found at androidTest/assets/pages/generic[1|2|3].html
|
||||||
|
@ -33,7 +33,8 @@ object TestAssetHelper {
|
||||||
return (1..4).map {
|
return (1..4).map {
|
||||||
TestAsset(
|
TestAsset(
|
||||||
server.url("pages/generic$it.html").toString().toUri()!!,
|
server.url("pages/generic$it.html").toString().toUri()!!,
|
||||||
"Page content: $it"
|
"Page content: $it",
|
||||||
|
""
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,52 +42,70 @@ object TestAssetHelper {
|
||||||
fun getGenericAsset(server: MockWebServer, pageNum: Int): TestAsset {
|
fun getGenericAsset(server: MockWebServer, pageNum: Int): TestAsset {
|
||||||
val url = server.url("pages/generic$pageNum.html").toString().toUri()!!
|
val url = server.url("pages/generic$pageNum.html").toString().toUri()!!
|
||||||
val content = "Page content: $pageNum"
|
val content = "Page content: $pageNum"
|
||||||
|
val title = "Test_Page_$pageNum"
|
||||||
|
|
||||||
return TestAsset(url, content)
|
return TestAsset(url, content, title)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLoremIpsumAsset(server: MockWebServer): TestAsset {
|
fun getLoremIpsumAsset(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("pages/lorem-ipsum.html").toString().toUri()!!
|
val url = server.url("pages/lorem-ipsum.html").toString().toUri()!!
|
||||||
val content = "Page content: lorem ipsum"
|
val content = "Page content: lorem ipsum"
|
||||||
return TestAsset(url, content)
|
|
||||||
|
return TestAsset(url, content, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRefreshAsset(server: MockWebServer): TestAsset {
|
fun getRefreshAsset(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("pages/refresh.html").toString().toUri()!!
|
val url = server.url("pages/refresh.html").toString().toUri()!!
|
||||||
val content = "Page content: refresh"
|
val content = "Page content: refresh"
|
||||||
|
|
||||||
return TestAsset(url, content)
|
return TestAsset(url, content, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getUUIDPage(server: MockWebServer): TestAsset {
|
fun getUUIDPage(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("pages/basic_nav_uuid.html").toString().toUri()!!
|
val url = server.url("pages/basic_nav_uuid.html").toString().toUri()!!
|
||||||
val content = "Page content: basic_nav_uuid"
|
val content = "Page content: basic_nav_uuid"
|
||||||
|
|
||||||
return TestAsset(url, content)
|
return TestAsset(url, content, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getDownloadAsset(server: MockWebServer): TestAsset {
|
fun getDownloadAsset(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("pages/download.html").toString().toUri()!!
|
val url = server.url("pages/download.html").toString().toUri()!!
|
||||||
val content = "Page content: Globe.svg"
|
val content = "Page content: Globe.svg"
|
||||||
|
|
||||||
return TestAsset(url, content)
|
return TestAsset(url, content, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getEnhancedTrackingProtectionAsset(server: MockWebServer): TestAsset {
|
fun getEnhancedTrackingProtectionAsset(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("pages/etp.html").toString().toUri()!!
|
val url = server.url("pages/etp.html").toString().toUri()!!
|
||||||
|
|
||||||
return TestAsset(url, "")
|
return TestAsset(url, "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getImageAsset(server: MockWebServer): TestAsset {
|
fun getImageAsset(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("resources/rabbit.jpg").toString().toUri()!!
|
val url = server.url("resources/rabbit.jpg").toString().toUri()!!
|
||||||
|
|
||||||
return TestAsset(url, "")
|
return TestAsset(url, "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSaveLoginAsset(server: MockWebServer): TestAsset {
|
fun getSaveLoginAsset(server: MockWebServer): TestAsset {
|
||||||
val url = server.url("pages/password.html").toString().toUri()!!
|
val url = server.url("pages/password.html").toString().toUri()!!
|
||||||
|
|
||||||
return TestAsset(url, "")
|
return TestAsset(url, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAudioPageAsset(server: MockWebServer): TestAsset {
|
||||||
|
val url = server.url("pages/audioMediaPage.html").toString().toUri()!!
|
||||||
|
val title = "Audio_Test_Page"
|
||||||
|
val content = "Page content: audio player"
|
||||||
|
|
||||||
|
return TestAsset(url, content, title)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVideoPageAsset(server: MockWebServer): TestAsset {
|
||||||
|
val url = server.url("pages/videoMediaPage.html").toString().toUri()!!
|
||||||
|
val title = "Video_Test_Page"
|
||||||
|
val content = "Page content: video player"
|
||||||
|
|
||||||
|
return TestAsset(url, content, title)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
/* 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.ui
|
||||||
|
|
||||||
|
import okhttp3.mockwebserver.MockWebServer
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||||
|
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||||
|
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||||
|
import org.mozilla.fenix.ui.robots.browserScreen
|
||||||
|
import org.mozilla.fenix.ui.robots.homeScreen
|
||||||
|
import org.mozilla.fenix.ui.robots.mDevice
|
||||||
|
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for verifying basic functionality of media notifications:
|
||||||
|
* - video and audio playback system notifications appear and can pause/play the media content
|
||||||
|
* - a media notification icon is displayed on the homescreen for the tab playing media content
|
||||||
|
* Note: this test only verifies media notifications, not media itself
|
||||||
|
*/
|
||||||
|
class MediaNotificationTest {
|
||||||
|
/* ktlint-disable no-blank-line-before-rbrace */ // This imposes unreadable grouping.
|
||||||
|
|
||||||
|
private lateinit var mockWebServer: MockWebServer
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val activityTestRule = HomeActivityTestRule()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mockWebServer = MockWebServer().apply {
|
||||||
|
setDispatcher(AndroidAssetDispatcher())
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockWebServer.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun videoPlaybackSystemNotificationTest() {
|
||||||
|
val videoTestPage = TestAssetHelper.getVideoPageAsset(mockWebServer)
|
||||||
|
|
||||||
|
navigationToolbar {
|
||||||
|
}.enterURLAndEnterToBrowser(videoTestPage.url) {
|
||||||
|
clickMediaPlayerPlayButton()
|
||||||
|
waitForPlaybackToStart()
|
||||||
|
}.openNotificationShade {
|
||||||
|
verifySystemNotificationExists(videoTestPage.title)
|
||||||
|
clickMediaSystemNotificationControlButton("Pause")
|
||||||
|
verifyMediaSystemNotificationButtonState("Play")
|
||||||
|
}
|
||||||
|
|
||||||
|
mDevice.pressBack()
|
||||||
|
|
||||||
|
browserScreen {
|
||||||
|
verifyMediaIsPaused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun audioPlaybackSystemNotificationTest() {
|
||||||
|
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
|
||||||
|
|
||||||
|
navigationToolbar {
|
||||||
|
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||||
|
verifyPageContent(audioTestPage.content)
|
||||||
|
clickMediaPlayerPlayButton()
|
||||||
|
waitForPlaybackToStart()
|
||||||
|
}.openNotificationShade {
|
||||||
|
verifySystemNotificationExists(audioTestPage.title)
|
||||||
|
clickMediaSystemNotificationControlButton("Pause")
|
||||||
|
verifyMediaSystemNotificationButtonState("Play")
|
||||||
|
}
|
||||||
|
|
||||||
|
mDevice.pressBack()
|
||||||
|
|
||||||
|
browserScreen {
|
||||||
|
verifyMediaIsPaused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun tabMediaControlButtonTest() {
|
||||||
|
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
|
||||||
|
|
||||||
|
navigationToolbar {
|
||||||
|
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||||
|
verifyPageContent(audioTestPage.content)
|
||||||
|
clickMediaPlayerPlayButton()
|
||||||
|
waitForPlaybackToStart()
|
||||||
|
}.openHomeScreen {
|
||||||
|
verifyTabMediaControlButtonState("Pause")
|
||||||
|
clickTabMediaControlButton()
|
||||||
|
verifyTabMediaControlButtonState("Play")
|
||||||
|
}.openTab(audioTestPage.title) {
|
||||||
|
verifyMediaIsPaused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun mediaSystemNotificationInPrivateModeTest() {
|
||||||
|
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
|
||||||
|
|
||||||
|
homeScreen { }.togglePrivateBrowsingMode()
|
||||||
|
|
||||||
|
navigationToolbar {
|
||||||
|
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||||
|
verifyPageContent(audioTestPage.content)
|
||||||
|
clickMediaPlayerPlayButton()
|
||||||
|
waitForPlaybackToStart()
|
||||||
|
}.openNotificationShade {
|
||||||
|
verifySystemNotificationExists("A site is playing media")
|
||||||
|
clickMediaSystemNotificationControlButton("Pause")
|
||||||
|
verifyMediaSystemNotificationButtonState("Play")
|
||||||
|
}
|
||||||
|
|
||||||
|
mDevice.pressBack()
|
||||||
|
|
||||||
|
browserScreen {
|
||||||
|
verifyMediaIsPaused()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import org.mozilla.fenix.helpers.TestAssetHelper
|
||||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||||
import org.mozilla.fenix.ui.robots.homeScreen
|
import org.mozilla.fenix.ui.robots.homeScreen
|
||||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||||
|
import org.mozilla.fenix.ui.robots.notificationShade
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for verifying basic functionality of tabbed browsing
|
* Tests for verifying basic functionality of tabbed browsing
|
||||||
|
@ -259,6 +260,9 @@ class TabbedBrowsingTest {
|
||||||
navigationToolbar {
|
navigationToolbar {
|
||||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||||
mDevice.openNotification()
|
mDevice.openNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
notificationShade {
|
||||||
verifyPrivateTabsNotification()
|
verifyPrivateTabsNotification()
|
||||||
}.clickClosePrivateTabsNotification {
|
}.clickClosePrivateTabsNotification {
|
||||||
verifyPrivateSessionMessage()
|
verifyPrivateSessionMessage()
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.test.uiautomator.By.text
|
||||||
import androidx.test.uiautomator.UiDevice
|
import androidx.test.uiautomator.UiDevice
|
||||||
import androidx.test.uiautomator.UiSelector
|
import androidx.test.uiautomator.UiSelector
|
||||||
import androidx.test.uiautomator.Until
|
import androidx.test.uiautomator.Until
|
||||||
|
import androidx.test.uiautomator.Until.hasObject
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
import org.hamcrest.CoreMatchers.containsString
|
import org.hamcrest.CoreMatchers.containsString
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
|
@ -34,6 +35,7 @@ import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.helpers.Constants.LongClickDuration
|
import org.mozilla.fenix.helpers.Constants.LongClickDuration
|
||||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||||
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||||
|
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeShort
|
||||||
import org.mozilla.fenix.helpers.click
|
import org.mozilla.fenix.helpers.click
|
||||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||||
|
|
||||||
|
@ -145,11 +147,6 @@ class BrowserRobot {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun verifyPrivateTabsNotification() {
|
|
||||||
mDevice.wait(Until.hasObject(text("Close private tabs")), waitingTime)
|
|
||||||
assertPrivateTabsNotification()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clickContextOpenLinkInNewTab() {
|
fun clickContextOpenLinkInNewTab() {
|
||||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
mDevice.waitNotNull(
|
mDevice.waitNotNull(
|
||||||
|
@ -289,6 +286,36 @@ class BrowserRobot {
|
||||||
mDevice.findObject(text(optionToSaveLogin)).click()
|
mDevice.findObject(text(optionToSaveLogin)).click()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clickMediaPlayerPlayButton() {
|
||||||
|
mDevice.waitNotNull(
|
||||||
|
hasObject(
|
||||||
|
By
|
||||||
|
.clazz("android.widget.Button")
|
||||||
|
.textContains("Play")
|
||||||
|
),
|
||||||
|
waitingTime
|
||||||
|
)
|
||||||
|
mediaPlayerPlayButton().click()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun waitForPlaybackToStart() {
|
||||||
|
mDevice.waitNotNull(
|
||||||
|
hasObject(
|
||||||
|
text("Media file is playing")
|
||||||
|
), waitingTimeShort
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyMediaIsPaused() {
|
||||||
|
mDevice.waitNotNull(
|
||||||
|
hasObject(
|
||||||
|
text("Media file is paused")
|
||||||
|
), waitingTimeShort
|
||||||
|
)
|
||||||
|
|
||||||
|
mDevice.findObject(UiSelector().text("Media file is paused")).exists()
|
||||||
|
}
|
||||||
|
|
||||||
class Transition {
|
class Transition {
|
||||||
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
private val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
private fun threeDotButton() = onView(
|
private fun threeDotButton() = onView(
|
||||||
|
@ -329,12 +356,11 @@ class BrowserRobot {
|
||||||
return HomeScreenRobot.Transition()
|
return HomeScreenRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickClosePrivateTabsNotification(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
|
fun openNotificationShade(interact: NotificationRobot.() -> Unit): NotificationRobot.Transition {
|
||||||
mDevice.wait(Until.hasObject(text("Close private tabs")), waitingTime)
|
mDevice.openNotification()
|
||||||
closePrivateTabsNotification().clickAndWaitForNewWindow(waitingTime)
|
|
||||||
|
|
||||||
HomeScreenRobot().interact()
|
NotificationRobot().interact()
|
||||||
return HomeScreenRobot.Transition()
|
return NotificationRobot.Transition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -355,10 +381,9 @@ fun navURLBar() = onView(withId(R.id.mozac_browser_toolbar_url_view))
|
||||||
|
|
||||||
private fun tabsCounter() = onView(withId(R.id.mozac_browser_toolbar_browser_actions))
|
private fun tabsCounter() = onView(withId(R.id.mozac_browser_toolbar_browser_actions))
|
||||||
|
|
||||||
private fun closePrivateTabsNotification() =
|
private fun mediaPlayerPlayButton() =
|
||||||
mDevice.findObject(UiSelector().text("Close private tabs"))
|
mDevice.findObject(
|
||||||
|
By
|
||||||
private fun assertPrivateTabsNotification() {
|
.clazz("android.widget.Button")
|
||||||
mDevice.findObject(UiSelector().text("Firefox Preview (Private)")).exists()
|
.textContains("Play")
|
||||||
mDevice.findObject(UiSelector().text("Close private tabs")).exists()
|
)
|
||||||
}
|
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
package org.mozilla.fenix.ui.robots
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.action.ViewActions
|
import androidx.test.espresso.action.ViewActions
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
|
@ -16,30 +16,32 @@ import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions
|
import androidx.test.espresso.contrib.RecyclerViewActions
|
||||||
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem
|
import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
import androidx.test.espresso.matcher.ViewMatchers.Visibility
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
import androidx.test.espresso.matcher.ViewMatchers.hasDescendant
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
import androidx.test.uiautomator.By
|
import androidx.test.uiautomator.By
|
||||||
|
import androidx.test.uiautomator.By.text
|
||||||
import androidx.test.uiautomator.UiDevice
|
import androidx.test.uiautomator.UiDevice
|
||||||
import androidx.test.uiautomator.UiScrollable
|
import androidx.test.uiautomator.UiScrollable
|
||||||
import androidx.test.uiautomator.UiSelector
|
import androidx.test.uiautomator.UiSelector
|
||||||
import androidx.test.uiautomator.Until
|
import androidx.test.uiautomator.Until
|
||||||
import org.hamcrest.CoreMatchers.containsString
|
import androidx.test.uiautomator.Until.findObject
|
||||||
import org.hamcrest.CoreMatchers.allOf
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
|
import org.hamcrest.CoreMatchers.containsString
|
||||||
import org.hamcrest.CoreMatchers.not
|
import org.hamcrest.CoreMatchers.not
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.components.Search
|
||||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||||
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||||
import org.mozilla.fenix.helpers.click
|
import org.mozilla.fenix.helpers.click
|
||||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||||
import org.mozilla.fenix.components.Search
|
|
||||||
import org.mozilla.fenix.helpers.withBitmapDrawable
|
|
||||||
import org.mozilla.fenix.helpers.matchers.hasItem
|
import org.mozilla.fenix.helpers.matchers.hasItem
|
||||||
|
import org.mozilla.fenix.helpers.withBitmapDrawable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of Robot Pattern for the home screen menu.
|
* Implementation of Robot Pattern for the home screen menu.
|
||||||
|
@ -168,7 +170,8 @@ class HomeScreenRobot {
|
||||||
|
|
||||||
fun swipeToBottom() = onView(withId(R.id.homeLayout)).perform(ViewActions.swipeUp())
|
fun swipeToBottom() = onView(withId(R.id.homeLayout)).perform(ViewActions.swipeUp())
|
||||||
|
|
||||||
fun swipeToTop() = onView(withId(R.id.sessionControlRecyclerView)).perform(ViewActions.swipeDown())
|
fun swipeToTop() =
|
||||||
|
onView(withId(R.id.sessionControlRecyclerView)).perform(ViewActions.swipeDown())
|
||||||
|
|
||||||
fun swipeTabRight(title: String) =
|
fun swipeTabRight(title: String) =
|
||||||
onView(allOf(withId(R.id.tab_title), withText(title))).perform(ViewActions.swipeRight())
|
onView(allOf(withId(R.id.tab_title), withText(title))).perform(ViewActions.swipeRight())
|
||||||
|
@ -185,10 +188,21 @@ class HomeScreenRobot {
|
||||||
|
|
||||||
fun snackBarButtonClick(expectedText: String) {
|
fun snackBarButtonClick(expectedText: String) {
|
||||||
onView(allOf(withId(R.id.snackbar_btn), withText(expectedText))).check(
|
onView(allOf(withId(R.id.snackbar_btn), withText(expectedText))).check(
|
||||||
matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))
|
matches(withEffectiveVisibility(Visibility.VISIBLE))
|
||||||
).perform(ViewActions.click())
|
).perform(click())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun verifyTabMediaControlButtonState(action: String) {
|
||||||
|
mDevice.waitNotNull(
|
||||||
|
Until.findObject(By.res("org.mozilla.fenix.debug:id/play_pause_button")),
|
||||||
|
waitingTime
|
||||||
|
)
|
||||||
|
|
||||||
|
tabMediaControlButton().check(matches(withContentDescription(action)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clickTabMediaControlButton() = tabMediaControlButton().click()
|
||||||
|
|
||||||
class Transition {
|
class Transition {
|
||||||
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||||
|
|
||||||
|
@ -242,16 +256,25 @@ class HomeScreenRobot {
|
||||||
return NavigationToolbarRobot.Transition()
|
return NavigationToolbarRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openContextMenuOnTopSitesWithTitle(title: String, interact: HomeScreenRobot.() -> Unit): Transition {
|
fun openContextMenuOnTopSitesWithTitle(
|
||||||
|
title: String,
|
||||||
|
interact: HomeScreenRobot.() -> Unit
|
||||||
|
): Transition {
|
||||||
onView(withId(R.id.top_sites_list)).perform(
|
onView(withId(R.id.top_sites_list)).perform(
|
||||||
actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText(title)), ViewActions.longClick())
|
actionOnItem<RecyclerView.ViewHolder>(
|
||||||
|
hasDescendant(withText(title)),
|
||||||
|
ViewActions.longClick()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
HomeScreenRobot().interact()
|
HomeScreenRobot().interact()
|
||||||
return Transition()
|
return Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openTopSiteTabWithTitle(title: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
fun openTopSiteTabWithTitle(
|
||||||
|
title: String,
|
||||||
|
interact: BrowserRobot.() -> Unit
|
||||||
|
): BrowserRobot.Transition {
|
||||||
onView(withId(R.id.top_sites_list)).perform(
|
onView(withId(R.id.top_sites_list)).perform(
|
||||||
actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText(title)), click())
|
actionOnItem<RecyclerView.ViewHolder>(hasDescendant(withText(title)), click())
|
||||||
)
|
)
|
||||||
|
@ -285,6 +308,19 @@ class HomeScreenRobot {
|
||||||
BrowserRobot().interact()
|
BrowserRobot().interact()
|
||||||
return BrowserRobot.Transition()
|
return BrowserRobot.Transition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun openTab(title: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
|
||||||
|
mDevice.waitNotNull(findObject(text(title)))
|
||||||
|
onView(
|
||||||
|
allOf(
|
||||||
|
withId(R.id.tab_title),
|
||||||
|
withText(title)
|
||||||
|
)
|
||||||
|
).click()
|
||||||
|
|
||||||
|
BrowserRobot().interact()
|
||||||
|
return BrowserRobot.Transition()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,7 +406,8 @@ private fun assertNoCollectionsTextIsNotVisible() =
|
||||||
)
|
)
|
||||||
.check(doesNotExist())
|
.check(doesNotExist())
|
||||||
|
|
||||||
private fun assertHomeComponent() = onView(ViewMatchers.withResourceName("sessionControlRecyclerView"))
|
private fun assertHomeComponent() =
|
||||||
|
onView(ViewMatchers.withResourceName("sessionControlRecyclerView"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
private fun threeDotButton() = onView(allOf(withId(R.id.menuButton)))
|
private fun threeDotButton() = onView(allOf(withId(R.id.menuButton)))
|
||||||
|
@ -485,10 +522,10 @@ private fun assertPrivacyNoticeButton() =
|
||||||
|
|
||||||
// What's new elements
|
// What's new elements
|
||||||
private fun assertWhatsNewHeather() = onView(allOf(withText("See what’s new")))
|
private fun assertWhatsNewHeather() = onView(allOf(withText("See what’s new")))
|
||||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
private fun assertWhatsNewLink() = onView(allOf(withText("Get answers here")))
|
private fun assertWhatsNewLink() = onView(allOf(withText("Get answers here")))
|
||||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
private fun assertStartBrowsingButton() =
|
private fun assertStartBrowsingButton() =
|
||||||
onView(allOf(withText("Start browsing")))
|
onView(allOf(withText("Start browsing")))
|
||||||
|
@ -496,18 +533,20 @@ private fun assertStartBrowsingButton() =
|
||||||
|
|
||||||
// Take a position
|
// Take a position
|
||||||
private fun assertTakePositionheader() = onView(allOf(withText("Take a position")))
|
private fun assertTakePositionheader() = onView(allOf(withText("Take a position")))
|
||||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
|
||||||
|
|
||||||
private fun assertTakePositionTopRadioButton() = onView(ViewMatchers.withResourceName("toolbar_top_radio_button"))
|
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
private fun assertTakePositionBottomRadioButton() = onView(ViewMatchers.withResourceName("toolbar_bottom_radio_button"))
|
private fun assertTakePositionTopRadioButton() =
|
||||||
|
onView(ViewMatchers.withResourceName("toolbar_top_radio_button"))
|
||||||
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
|
private fun assertTakePositionBottomRadioButton() =
|
||||||
|
onView(ViewMatchers.withResourceName("toolbar_bottom_radio_button"))
|
||||||
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
// Private mode elements
|
// Private mode elements
|
||||||
private fun assertPrivateSessionHeader() =
|
private fun assertPrivateSessionHeader() =
|
||||||
onView(allOf(withText("Private tabs")))
|
onView(allOf(withText("Private tabs")))
|
||||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
|
||||||
|
|
||||||
const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and browsing history " +
|
const val PRIVATE_SESSION_MESSAGE = "Firefox Preview clears your search and browsing history " +
|
||||||
"when you quit the app or close all private tabs. While this doesn’t make you anonymous to websites or " +
|
"when you quit the app or close all private tabs. While this doesn’t make you anonymous to websites or " +
|
||||||
|
@ -522,7 +561,9 @@ private fun assertPrivateSessionMessage(visible: Boolean) =
|
||||||
|
|
||||||
private fun assertShareTabsButton(visible: Boolean) = onView(allOf(withId(R.id.share_tabs_button)))
|
private fun assertShareTabsButton(visible: Boolean) = onView(allOf(withId(R.id.share_tabs_button)))
|
||||||
.check(
|
.check(
|
||||||
if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) else matches(withEffectiveVisibility(Visibility.INVISIBLE))
|
if (visible) matches(withEffectiveVisibility(Visibility.VISIBLE)) else matches(
|
||||||
|
withEffectiveVisibility(Visibility.INVISIBLE)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun assertCloseTabsButton(title: String) =
|
private fun assertCloseTabsButton(title: String) =
|
||||||
|
@ -588,3 +629,5 @@ private fun assertTopSiteContextMenuItems() {
|
||||||
waitingTime
|
waitingTime
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun tabMediaControlButton() = onView(withId(R.id.play_pause_button))
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
package org.mozilla.fenix.ui.robots
|
||||||
|
|
||||||
|
import android.content.res.Resources
|
||||||
|
import androidx.test.uiautomator.By.text
|
||||||
|
import androidx.test.uiautomator.UiScrollable
|
||||||
|
import androidx.test.uiautomator.UiSelector
|
||||||
|
import androidx.test.uiautomator.Until
|
||||||
|
import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
|
||||||
|
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||||
|
|
||||||
|
class NotificationRobot {
|
||||||
|
|
||||||
|
fun verifySystemNotificationExists(notificationMessage: String) {
|
||||||
|
|
||||||
|
fun notificationTray() = UiScrollable(
|
||||||
|
UiSelector().resourceId("com.android.systemui:id/notification_stack_scroller")
|
||||||
|
)
|
||||||
|
|
||||||
|
mDevice.waitNotNull(
|
||||||
|
Until.hasObject(text(notificationMessage)),
|
||||||
|
waitingTime
|
||||||
|
)
|
||||||
|
|
||||||
|
var notificationFound = false
|
||||||
|
while (!notificationFound) {
|
||||||
|
try {
|
||||||
|
val notification = notificationTray().getChildByText(
|
||||||
|
UiSelector().text(notificationMessage), notificationMessage,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
notification.exists()
|
||||||
|
notificationFound = true
|
||||||
|
} catch (e: Resources.NotFoundException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyPrivateTabsNotification() {
|
||||||
|
mDevice.waitNotNull(Until.hasObject(text("Close private tabs")), waitingTime)
|
||||||
|
assertPrivateTabsNotification()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clickMediaSystemNotificationControlButton(action: String) {
|
||||||
|
mediaSystemNotificationButton(action).waitForExists(waitingTime)
|
||||||
|
mediaSystemNotificationButton(action).click()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyMediaSystemNotificationButtonState(action: String) {
|
||||||
|
mediaSystemNotificationButton(action).waitForExists(waitingTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Transition {
|
||||||
|
|
||||||
|
fun clickClosePrivateTabsNotification(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
|
||||||
|
NotificationRobot().verifySystemNotificationExists("Close private tabs")
|
||||||
|
closePrivateTabsNotification().clickAndWaitForNewWindow()
|
||||||
|
|
||||||
|
HomeScreenRobot().interact()
|
||||||
|
return HomeScreenRobot.Transition()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notificationShade(interact: NotificationRobot.() -> Unit): NotificationRobot.Transition {
|
||||||
|
NotificationRobot().interact()
|
||||||
|
return NotificationRobot.Transition()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertPrivateTabsNotification() {
|
||||||
|
mDevice.findObject(UiSelector().text("Firefox Preview (Private)")).exists()
|
||||||
|
mDevice.findObject(UiSelector().text("Close private tabs")).exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun closePrivateTabsNotification() =
|
||||||
|
mDevice.findObject(UiSelector().text("Close private tabs"))
|
||||||
|
|
||||||
|
private fun mediaSystemNotificationButton(action: String) =
|
||||||
|
mDevice.findObject(
|
||||||
|
UiSelector()
|
||||||
|
.resourceId("android:id/action0")
|
||||||
|
.descriptionContains(action)
|
||||||
|
)
|
Loading…
Reference in New Issue