Easiest framework to develop Android UI tests
By default Allure results are saved in /data/data/<package_name>/files/allure-results
.
It's possible to set a custom directory. Make sure application has WRITE permission to the directory.
UltronAllureConfig.setAllureResultsDirectory(File("directory_path"))
UltronAllureConfig.setAllureResultsDirectory(Environment.DIRECTORY_DOCUMENTS)
UltronAllureConfig.setAllureResultsDirectory() // set Environment.DIRECTORY_DOWNLOADS as target directory
If custom directory is specified allure results artifacts are copied to the desired directory at the end of test run.
If no parameters are provided to UltronAllureConfig.setAllureResultsDirectory()
then you can pull artifacts from /sdcard/Download/allure-results
.
Full Changelog: https://github.com/open-tool/ultron/compare/2.3.3...2.3.5
old syntax:
class CustomListItem : UltronComposeListItem() {
val status by lazy { getChild(hasTestTag(contactStatusTestTag)) }
}
new:
class CustomListItem : UltronComposeListItem() {
val name by child { hasTestTag(contactNameTestTag) }
}
You can use both ways at the same time:
class CustomListItem : UltronComposeListItem() {
val name by child { hasTestTag(contactNameTestTag) }
val status by lazy { getChild(hasTestTag(contactStatusTestTag)) }
}
Old syntax:
class CustomRecyclerItem : UltronRecyclerViewItem() {
val name by lazy { getChild(withId(R.id.tv_name)) }
}
New:
class CustomRecyclerItem : UltronRecyclerViewItem() {
val name by child { withId(R.id.tv_name) }
}
You can use both ways at the same time:
class CustomRecyclerItem : UltronRecyclerViewItem() {
val name by child { withId(R.id.tv_name) }
val status by lazy { getChild(withId(R.id.tv_status)) }
}
Full Changelog: https://github.com/open-tool/ultron/compare/2.3.2...2.3.3
Compose LazyList improvements in https://github.com/open-tool/ultron/pull/59
firstItem()
item(position: Int)
getItem(position: Int): T
getFirstItem(): T
to use these methods you have to configure positionPropertyKey
in application code and specify it in composeList
declaration.
composeList(marcher =..., positionPropertyKey = ListItemPositionPropertyKey)
Read new wiki page for details - Efficient Strategies for Locating Items in Compose LazyList
UltronComposeList
fun assertItemDoesNotExist(itemMatcher: SemanticsMatcher)
method UltronComposeListItem().assertDoesNotExist()
Full Changelog: https://github.com/open-tool/ultron/compare/2.3.1...2.3.2
There are 2 new general methods:
withName(name: String)
withMetaInfo(meta: Any)
By default name
is used in exceptions messages.
These data also could be used in listeners and result analyzers:
class MetaListener: UltronLifecycleListener(){
override fun afterFailure(operationResult: OperationResult<Operation>) {
val name = operationResult.operation.elementInfo.name
val metaInfo = operationResult.operation.elementInfo.meta
}
}
Full Changelog: https://github.com/open-tool/ultron/compare/2.3.0...2.3.1
Enhanced framework extension for Espresso and Compose.
Here is a new wiki page
val semanticsNode = hasTestTag("Button").getNode()
val testTag = hasText("Some text").getNodeConfigProperty(SemanticsProperties.TestTag)
Introducing new perform
and execute
methods.
Use perform if you wish to evaluate an operation and return an updated UltronComposeSemanticsNodeInteraction
object.
fun UltronComposeSemanticsNodeInteraction.hasAnyChildren() = perform {
Assert.assertTrue(it.fetchSemanticsNode().children.isNotEmpty())
}
fun SemanticsMatcher.hasAnyChildren() = UltronComposeSemanticsNodeInteraction(this).hasAnyChildren()
Use execute
to evaluate operation and return the result.
fun UltronComposeSemanticsNodeInteraction.getWidth(): Int = execute {
it.fetchSemanticsNode().size.width
}
fun SemanticsMatcher.getWidth(): Int = UltronComposeSemanticsNodeInteraction(this).getWidth()
Use perform
if you intend to evaluate an operation and return an updated UltronEspressoInteraction
fun <T> UltronEspressoInteraction<T>.appendText(value: String) = perform { _, view ->
val textView = (view as TextView)
textView.text = "${textView.text}$value"
}
Use execute
to evaluate operation and return the result.
fun <T> UltronEspressoInteraction<T>.getText(): String = execute { _, view ->
(view as TextView).text.toString()
}
Customize your action using UltronEspressoActionParams
for both pefrorm
and execute
methods.
fun <T> UltronEspressoInteraction<T>.getDrawable(): Drawable? = execute(
UltronEspressoActionParams(
operationName = "GetDrawable from TextView with '${getInteractionMatcher()}'",
operationDescription = "description...",
operationType = CustomEspressoActionType.GET_DRAWABLE,
viewActionConstraints = isAssignableFrom(ImageView::class.java),
viewActionDescription = "getting Drawable from ImageView"
)
){ _, view ->
(view as ImageView).drawable
}
Use new assertMatches
method to extend framework with new assertion.
fun <T> UltronEspressoInteraction<T>.assertChecked(expectedState: Boolean) = assertMatches { view ->
(view as CheckBox).isChecked == expectedState
}
The signature of UltronComposeSemanticsNodeInteraction
old perform
method has been modified.
If you are using the old perform
method to obtain the result of an operation, you should replace it with execute
.
For instance, in the following cases, replace pefrorm
to execute
// in extension function
fun UltronComposeSemanticsNodeInteraction.getWidth(): Int = pefrorm {
it.fetchSemanticsNode().size.width
}
// direct usage
val width: Int = hasTestTag("button").pefrorm {
it.fetchSemanticsNode().size.width
}
Matcher<View>.performOnView()
- Use new Matcher<View>.perform { view -> }
instead.
Full Changelog: https://github.com/open-tool/ultron/compare/2.2.6...2.3.0
Note: To add window hierarchy for Compose you have to call
UltronComposeConfig.addListener(WindowHierarchyAttachListener())
See updated Allure doc
Full Changelog: https://github.com/open-tool/ultron/compare/2.2.4...2.2.6
https://github.com/open-tool/ultron/pull/45 - Fix method name for parametrized tests
Fix:
getViewForcibly()
performOnViewForcibly()
Matcher<View>, ViewInteraction, DataInteraction
:doesNotExistInAnyVisibleRoot()
getView(): View
getViewForcibly(): View //bypass Espresso idling state mechanism
performOnView(action: View.() -> Unit)
performOnViewForcibly(action: View.() -> Unit) //bypass Espresso idling state mechanism
View.performOnView(action: View.() -> Unit)
ResourceDrawableMatcher
, affected methods:hasDrawable(@DrawableRes resourceId: Int)
hasAnyDrawable()
Thanks @itsergpot
There is a new dependency com.atiurin:ultron-allure:<latest_version>
.
Ultron provides all required allure libs. You don't need to add them.
Please read Allure wiki carefully.
Ultron has recommendations about it's configuration. Basically you just need to add few lines of code to receive full power of framework:
@BeforeClass @JvmStatic
fun configuration() {
UltronConfig.applyRecommended()
UltronAllureConfig.applyRecommended()
UltronComposeConfig.applyRecommended()
UltronComposeConfig.addListener(ScreenshotAttachListener())
UltronComposeConfig.addListener(DetailedOperationAllureListener())
}
UltronComposeConfig
import changed from import com.atiurin.ultron.core.compose.UltronComposeConfig
to
import com.atiurin.ultron.core.compose.config.UltronComposeConfig