Open Tool Ultron Versions Save

Easiest framework to develop Android UI tests

2.3.5

1 month ago

What's Changed

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

2.3.3

2 months ago

Compose List & RecyclerView Item Child improvements in https://github.com/open-tool/ultron/pull/60

  • ComposeListItem child

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)) }
}
  • RecyclerViewItem child

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

2.3.2

2 months ago

What's Changed

Compose LazyList improvements in https://github.com/open-tool/ultron/pull/59

Add

  • new methods to interact with LazyList item by position:
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

  • new method for UltronComposeList
fun assertItemDoesNotExist(itemMatcher: SemanticsMatcher)

Removed

method UltronComposeListItem().assertDoesNotExist()

Full Changelog: https://github.com/open-tool/ultron/compare/2.3.1...2.3.2

2.3.1

5 months ago

What's Changed

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

2.3.0

8 months ago

What's Changed

Enhanced framework extension for Espresso and Compose.

Here is a new wiki page

New features

Add new Compose methods to getNode and getNodeConfigProperty:

val semanticsNode = hasTestTag("Button").getNode()
val testTag = hasText("Some text").getNodeConfigProperty(SemanticsProperties.TestTag)

Simplified Compose extension

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()

Simplified Espresso operations extenstion

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
}

Breaking changes:

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
}

Deprecated

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

2.2.6

9 months ago

What's Changed

Note: To add window hierarchy for Compose you have to call

UltronComposeConfig.addListener(WindowHierarchyAttachListener())

See updated Allure doc

New Contributors

Full Changelog: https://github.com/open-tool/ultron/compare/2.2.4...2.2.6

2.2.4

10 months ago

https://github.com/open-tool/ultron/pull/45 - Fix method name for parametrized tests

2.2.3

11 months ago

Fix:

  • getViewForcibly()
  • performOnViewForcibly()

2.2.2

11 months ago

Inhencments

  1. New extension methods for 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
  1. Add method to perform action on view on Ui Thread.
View.performOnView(action: View.() -> Unit)
  1. Improved ResourceDrawableMatcher, affected methods:
hasDrawable(@DrawableRes resourceId: Int)
hasAnyDrawable()

Thanks @itsergpot

2.2.1

1 year ago

Allure report artifacts generation is supported.

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())
}

Breaking changes

UltronComposeConfig import changed from import com.atiurin.ultron.core.compose.UltronComposeConfig to import com.atiurin.ultron.core.compose.config.UltronComposeConfig