Revision control

Copy as Markdown

/* 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.focus.helpers
import android.app.PendingIntent
import android.app.UiAutomation
import android.content.ActivityNotFoundException
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import android.os.storage.StorageManager
import android.os.storage.StorageVolume
import android.util.Log
import android.view.KeyEvent
import android.view.inputmethod.InputMethodManager
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsIntent.SHARE_STATE_ON
import androidx.core.net.toUri
import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.toPackage
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.espresso.web.sugar.Web
import androidx.test.filters.SdkSuppress
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.UiSelector
import junit.framework.AssertionFailedError
import mozilla.components.support.utils.ext.getApplicationInfoCompat
import okio.Buffer
import org.hamcrest.Matchers
import org.hamcrest.Matchers.allOf
import org.junit.Assert
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.mozilla.focus.R
import org.mozilla.focus.activity.IntentReceiverActivity
import org.mozilla.focus.utils.IntentUtils
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.util.concurrent.TimeUnit
@Suppress("TooManyFunctions")
object TestHelper {
@JvmField
var mDevice = UiDevice.getInstance(getInstrumentation())
val waitingTime = TimeUnit.SECONDS.toMillis(15)
val pageLoadingTime = TimeUnit.SECONDS.toMillis(25)
val waitingTimeShort: Long = TimeUnit.SECONDS.toMillis(3)
private val charPool: List<Char> = ('a'..'z') + ('A'..'Z') + ('0'..'9')
fun randomString(stringLength: Int) =
(1..stringLength)
.map { kotlin.random.Random.nextInt(0, charPool.size) }
.map(charPool::get)
.joinToString("")
@JvmStatic
val getTargetContext: Context = getInstrumentation().targetContext
@JvmStatic
val packageName: String = getTargetContext.packageName
@JvmStatic
val appName: String = getTargetContext.getString(R.string.app_name)
fun getStringResource(id: Int) = getTargetContext.resources.getString(id, appName)
fun verifySnackBarText(text: String) {
val snackbarText = mDevice.findObject(UiSelector().textContains(text))
assertTrue(snackbarText.waitForExists(waitingTime))
}
fun clickSnackBarActionButton(action: String) {
val snackbarActionButton =
onView(
allOf(
withId(R.id.snackbar_action),
withText(action),
),
)
snackbarActionButton.perform(click())
}
fun waitUntilSnackBarGone() {
mDevice.findObject(UiSelector().resourceId("$appName:id/snackbar_layout"))
.waitUntilGone(waitingTime)
}
fun isPackageInstalled(packageName: String): Boolean {
return try {
val packageManager = getInstrumentation().context.packageManager
packageManager.getApplicationInfoCompat(packageName, 0).enabled
} catch (exception: PackageManager.NameNotFoundException) {
Log.d("TestLog", exception.message.toString())
false
}
}
fun restartApp(activity: MainActivityFirstrunTestRule) {
with(activity) {
finishActivity()
mDevice.waitForIdle()
launchActivity(null)
}
}
// exit to the main view
fun exitToTop() {
val homeScreen =
mDevice.findObject(UiSelector().resourceId("$packageName:id/landingLayout"))
var homeScreenVisible = false
while (!homeScreenVisible) {
mDevice.pressBack()
homeScreenVisible = homeScreen.waitForExists(2000)
}
}
// exit to the browser view
fun exitToBrowser() {
val browserScreen =
mDevice.findObject(UiSelector().resourceId("$packageName:id/main_content"))
var browserScreenVisible = false
while (!browserScreenVisible) {
mDevice.pressBack()
browserScreenVisible = browserScreen.waitForExists(2000)
}
}
fun setNetworkEnabled(enabled: Boolean) {
when (enabled) {
true -> {
mDevice.executeShellCommand("svc data enable")
mDevice.executeShellCommand("svc wifi enable")
}
false -> {
mDevice.executeShellCommand("svc data disable")
mDevice.executeShellCommand("svc wifi disable")
}
}
mDevice.waitForIdle(waitingTime)
}
// verifies localized strings in different UIs
fun verifyTranslatedTextExists(text: String) =
assertTrue(mDevice.findObject(UiSelector().text(text)).waitForExists(waitingTime))
fun openAppFromExternalLink(url: String) {
val intent = Intent().apply {
action = Intent.ACTION_VIEW
data = url.toUri()
`package` = packageName
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
try {
getTargetContext.startActivity(intent)
} catch (ex: ActivityNotFoundException) {
intent.setPackage(null)
getTargetContext.startActivity(intent)
}
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.R)
fun verifyDownloadedFileOnStorage(fileName: String) {
val storageManager =
getInstrumentation().targetContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager
val storageVolumes = storageManager.storageVolumes
val storageVolume: StorageVolume = storageVolumes[0]
val file = File("${storageVolume.directory!!.path}/Download/$fileName")
assertTrue(file.exists())
}
// Method for granting app permission to access location/camera/mic
fun grantAppPermission() {
if (SDK_INT >= 23) {
mDevice.findObject(
UiSelector().textContains(
when (SDK_INT) {
Build.VERSION_CODES.R ->
"While using the app"
else -> "Allow"
},
),
).click()
}
}
fun UiAutomation.executeShellCommandBlocking(command: String) {
val output = executeShellCommand(command)
FileInputStream(output.fileDescriptor).use { it.readBytes() }
}
@JvmStatic
fun pressEnterKey() {
mDevice.pressKeyCode(KeyEvent.KEYCODE_ENTER)
}
@JvmStatic
fun pressBackKey() {
mDevice.pressBack()
}
@JvmStatic
fun pressHomeKey() {
mDevice.pressHome()
}
fun createCustomTabIntent(
pageUrl: String,
customMenuItemLabel: String = "",
customActionButtonDescription: String = "",
): Intent {
val appContext = getInstrumentation()
.targetContext
.applicationContext
val pendingIntent = PendingIntent.getActivity(appContext, 0, Intent(), IntentUtils.defaultIntentPendingFlags())
val customTabColorSchemeBuilder = CustomTabColorSchemeParams.Builder()
customTabColorSchemeBuilder.setToolbarColor(Color.MAGENTA)
val customTabsIntent = CustomTabsIntent.Builder()
.addMenuItem(customMenuItemLabel, pendingIntent)
.setShareState(SHARE_STATE_ON)
.setActionButton(createTestBitmap(), customActionButtonDescription, pendingIntent, true)
.setDefaultColorSchemeParams(customTabColorSchemeBuilder.build())
.build()
customTabsIntent.intent.data = pageUrl.toUri()
customTabsIntent.intent.component = ComponentName(appContext, IntentReceiverActivity::class.java)
return customTabsIntent.intent
}
fun assertNativeAppOpens(appPackageName: String) {
try {
if (isPackageInstalled(packageName)) {
intended(toPackage(appPackageName))
}
} catch (e: AssertionFailedError) {
e.printStackTrace()
}
}
private fun createTestBitmap(): Bitmap {
val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawColor(Color.GREEN)
return bitmap
}
/**
* Wrapper for tests to run only when certain conditions are met.
* For example: this method will avoid accidentally running a test on GV versions where the feature is disabled.
*/
fun runWithCondition(condition: Boolean, testBlock: () -> Unit) {
if (condition) {
testBlock()
}
}
/********* Old code locators - used only in Screenshots tests */
// wait for web area to be visible
@JvmStatic
fun waitForWebContent() {
Assert.assertTrue(geckoView.waitForExists(waitingTime))
}
@JvmField
var menuButton = Espresso.onView(
Matchers.allOf(
ViewMatchers.withId(R.id.menuView),
ViewMatchers.isDisplayed(),
),
)
@JvmField
var permAllowBtn = mDevice.findObject(
UiSelector()
.textContains("Allow")
.clickable(true),
)
@JvmField
var webView = mDevice.findObject(
UiSelector()
.className("android.webkit.WebView")
.enabled(true),
)
var geckoView = mDevice.findObject(
UiSelector()
.resourceId(packageName + ":id/engineView")
.enabled(true),
)
@JvmField
var progressBar = mDevice.findObject(
UiSelector()
.resourceId(packageName + ":id/progress")
.enabled(true),
)
@JvmField
var AddtoHSmenuItem = mDevice.findObject(
UiSelector()
.resourceId(packageName + ":id/add_to_homescreen")
.enabled(true),
)
@JvmField
var AddtoHSCancelBtn = mDevice.findObject(
UiSelector()
.resourceId(packageName + ":id/addtohomescreen_dialog_cancel")
.enabled(true),
)
@JvmField
var securityInfoIcon = mDevice.findObject(
UiSelector()
.resourceId(packageName + ":id/security_info")
.enabled(true),
)
@JvmField
var identityState = mDevice.findObject(
UiSelector()
.resourceId(packageName + ":id/site_identity_state")
.enabled(true),
)
@JvmField
var shareAppList = mDevice.findObject(
UiSelector()
.resourceId("android:id/resolver_list")
.enabled(true),
)
@JvmStatic
@Throws(IOException::class)
fun readTestAsset(filename: String?): Buffer {
getInstrumentation().getContext().assets.open(filename!!)
.use { stream -> return readStreamFile(stream) }
}
@Throws(IOException::class)
fun readStreamFile(file: InputStream?): Buffer {
val buffer = Buffer()
buffer.write(file!!.readBytes())
return buffer
}
@JvmStatic
fun waitForWebSiteTitleLoad() {
Web.onWebView(ViewMatchers.withText("focus test page"))
}
@JvmStatic
fun verifyKeyboardVisibility(isExpectedToBeVisible: Boolean) {
val imm = getTargetContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
if (isExpectedToBeVisible) {
assertTrue(imm.isAcceptingText)
} else {
assertFalse(imm.isAcceptingText)
}
}
}