Skip to content

Commit

Permalink
ISSUE-86: use native resources in kAutomator (KasperskyLab#516)
Browse files Browse the repository at this point in the history
ISSUE-86: use native resources in kAutomator
  • Loading branch information
Nikitae57 authored May 31, 2023
1 parent 56067aa commit c0d5e27
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.kaspersky.components.kautomator.common.resources

internal object KId : ResourceNameProvider() {
override val rClassName = "R\$id"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.kaspersky.components.kautomator.common.resources

import androidx.annotation.StringRes
import androidx.test.platform.app.InstrumentationRegistry

internal object KString : ResourceNameProvider() {
override val rClassName = "R\$string"

fun getString(@StringRes resId: Int): String = InstrumentationRegistry.getInstrumentation().targetContext.getString(resId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.kaspersky.components.kautomator.common.resources

internal object RClassProvider {
private val classNameToClass = mutableMapOf<String, Class<*>>()

@Synchronized
fun provideClass(className: String): Class<*> {
var rClass = classNameToClass[className]
if (rClass == null) {
rClass = Class.forName(className)
classNameToClass[className] = rClass
}

return rClass!!
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.kaspersky.components.kautomator.common.resources

internal abstract class ResourceNameProvider {
protected abstract val rClassName: String

fun resolveResName(packageName: String, resId: Int): String {
val rClass = RClassProvider.provideClass("$packageName.$rClassName")
return getResourceNameById(rClass, resId)
}

private fun getResourceNameById(rClass: Class<*>, id: Int): String {
return rClass.fields
.first { it.getInt(it) == id }
.name
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
@file:Suppress("unused")
package com.kaspersky.components.kautomator.component.common.builders

import androidx.annotation.IdRes
import androidx.annotation.StringRes
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.BySelectorHack
import com.google.common.truth.Truth.assertThat
import com.kaspersky.components.kautomator.common.resources.KId
import com.kaspersky.components.kautomator.common.resources.KString
import com.kaspersky.components.kautomator.component.common.KautomatorMarker
import java.util.regex.Pattern

Expand Down Expand Up @@ -37,6 +42,16 @@ class UiViewBuilder {
*/
fun withId(packageName: String, resourceId: String) = withResourceName(packageName, resourceId)

/**
* Matches the view with given resource id
* @param resourceId id to match
*/
fun withId(@IdRes resourceId: Int) {
val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName
val resName = KId.resolveResName(packageName, resourceId)
return withResourceName(packageName, resName)
}

/**
* Matches the view with given package name
* @param packageName package name to match
Expand Down Expand Up @@ -137,6 +152,13 @@ class UiViewBuilder {
fun withContentDescription(contentDescription: String) =
addSelector { desc(contentDescription) }

/**
* Matches the view with given content description
*
* @param contentDescriptionRes Content description to match
*/
fun withContentDescription(@StringRes contentDescriptionRes: Int) = addSelector { desc(KString.getString(contentDescriptionRes)) }

/**
* Matches the view with given content description
*
Expand All @@ -152,6 +174,13 @@ class UiViewBuilder {
*/
fun withText(text: String) = addSelector { text(text) }

/**
* Matches the view with given text
*
* @param textRes Text to match
*/
fun withText(@StringRes textRes: Int) = addSelector { text(KString.getString(textRes)) }

/**
* Matches the view with given text
*
Expand All @@ -167,6 +196,13 @@ class UiViewBuilder {
fun withoutText(text: String) =
addSelector { text(Regex("^((?!$text).)*\$").toPattern()) }

/**
* Matches if the view does not have a given text
*
* @param textRes Text to be matched
*/
fun withoutText(@StringRes textRes: Int) = addSelector { text(Regex("^((?!${KString.getString(textRes)}).)*\$").toPattern()) }

/**
* Matches the view which contains any text
*/
Expand All @@ -179,20 +215,41 @@ class UiViewBuilder {
*/
fun containsText(text: String) = addSelector { textContains(text) }

/**
* Matches the view which contain given text
*
* @param textRes Text to search
*/
fun containsText(@StringRes textRes: Int) = addSelector { textContains(KString.getString(textRes)) }

/**
* Matches if the view which text starts with given text
*
* @param text Text to be matched
*/
fun textStartsWith(text: String) = addSelector { textStartsWith(text) }

/**
* Matches if the view which text starts with given text
*
* @param textRes Text to be matched
*/
fun textStartsWith(@StringRes textRes: Int) = addSelector { textStartsWith(KString.getString(textRes)) }

/**
* Matches if the view which text ends with given text
*
* @param text Text to be matched
*/
fun textEndsWith(text: String) = addSelector { textEndsWith(text) }

/**
* Matches if the view which text ends with given text
*
* @param textRes Text to be matched
*/
fun textEndsWith(@StringRes textRes: Int) = addSelector { textEndsWith(KString.getString(textRes)) }

/**
* Matches the view with given resource name
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.kaspersky.kaspresso.kautomatorsample.screen

import com.kaspersky.components.kautomator.component.check.UiCheckBox
import com.kaspersky.components.kautomator.component.common.views.UiView
import com.kaspersky.components.kautomator.component.edit.UiEditText
import com.kaspersky.components.kautomator.component.text.UiButton
import com.kaspersky.components.kautomator.component.text.UiTextView
import com.kaspersky.components.kautomator.screen.UiScreen
import com.kaspersky.kaspresso.kautomatorsample.R

object MainNativeScreen : UiScreen<MainNativeScreen>() {

override val packageName: String = "com.kaspersky.kaspresso.kautomatorsample"

val header = UiTextView { withText(R.string.main_activity_text) }
val subHeader = UiTextView { textStartsWith(R.string.main_activity_text_start) }
val image = UiView { withContentDescription(R.string.main_activity_image_description) }
val simpleEditText = UiEditText { withId(R.id.editText) }
val simpleButton = UiButton { withId(R.id.button) }
val checkBox = UiCheckBox { withId(R.id.checkBox) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.kaspersky.kaspresso.kautomatorsample.test

import androidx.test.ext.junit.rules.activityScenarioRule
import com.kaspersky.kaspresso.kautomatorsample.MainActivity
import com.kaspersky.kaspresso.kautomatorsample.screen.MainNativeScreen
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import org.junit.Rule
import org.junit.Test

/**
* The sample of how to use native resources in combination with kAutomator
*/
class NativeSimpleTest : TestCase() {

@get:Rule
val activityRule = activityScenarioRule<MainActivity>()

@Test
fun nativeTest() = run {
step("Input text in EditText and check it") {
MainNativeScreen {
simpleEditText {
replaceText("Kaspresso")
hasText("Kaspresso")
}
}
}
step("Type more text and check it") {
MainNativeScreen {
simpleEditText {
typeText(" is super useful")
hasText("Kaspresso is super useful")
}
}
}
step("Click button") {
MainNativeScreen {
simpleButton {
click()
}
}
}
step("Click checkbox and check it") {
MainNativeScreen {
checkBox {
setChecked(true)
isChecked()
}
}
}
step("Check headers") {
MainNativeScreen {
header { isDisplayed() }
subHeader { isDisplayed() }
}
}
step("Check image") {
MainNativeScreen {
image { isDisplayed() }
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.kaspersky.kaspresso.kautomatorsample

import android.annotation.SuppressLint
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

findViewById<TextView>(R.id.subHeader).text = getString(R.string.main_activity_text_start) + getString(R.string.main_activity_text_end)
}
}
24 changes: 24 additions & 0 deletions samples/kautomator-sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,18 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.022" />

<TextView
android:id="@+id/subHeader"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginHorizontal="16dp"
android:textAlignment="center"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView"
tools:text="Kaspresso is super useful" />

<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
Expand Down Expand Up @@ -52,4 +64,16 @@
app:layout_constraintStart_toStartOf="@+id/button"
app:layout_constraintTop_toBottomOf="@+id/button" />

<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="300dp"
android:contentDescription="@string/main_activity_image_description"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/button"
app:layout_constraintBottom_toBottomOf="parent"
android:src="@drawable/ic_launcher_background" />


</androidx.constraintlayout.widget.ConstraintLayout>
3 changes: 3 additions & 0 deletions samples/kautomator-sample/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<resources>
<string name="app_name">sample_uiautomator_dsl</string>
<string name="main_activity_text">Sample of UiAutomator dsl</string>
<string name="main_activity_text_start">Kaspresso is</string>
<string name="main_activity_text_end"> super useful</string>
<string name="main_activity_image_description">What are you looking at?</string>
<string name="simple_button">Button</string>
<string name="simple_checkbox">CheckBox</string>

Expand Down

0 comments on commit c0d5e27

Please sign in to comment.