Skip to content

Commit 07327cd

Browse files
authored
2.5.0-alpha13 (#83)
* Fix Compose recommended config * add runDesktopUltronUiTest * upd docs
1 parent 4db0a4e commit 07327cd

File tree

47 files changed

+783
-365
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+783
-365
lines changed

README.md

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,35 @@ You don't need to learn any new classes or special syntax. All magic actions and
1414
<img src="https://user-images.githubusercontent.com/12834123/252498170-61e5a440-c2b5-42ea-8bfb-91ee12248422.png#gh-dark-mode-only" width=600>
1515
</p>
1616

17-
## [Documentation](https://open-tool.github.io/ultron/) | [Releases](https://github.com/open-tool/ultron/releases)
17+
## [Documentation](https://open-tool.github.io/ultron/) | [Releases](https://github.com/open-tool/ultron/releases) | [Telegram](https://t.me/ultron_framework)
1818

1919
## What are the benefits of using the framework?
2020

21-
- Exceptional support for [**Compose**](https://github.com/open-tool/ultron/wiki/Compose)
22-
- Out-of-the-box generation of [**Allure report**](https://github.com/open-tool/ultron/wiki/Allure)
21+
- Page/Screen Object pattern support
22+
- Exceptional simplification for [**Compose UI tests**](https://open-tool.github.io/ultron/docs/compose/index)
23+
- Out-of-the-box generation of [**Allure report**](https://open-tool.github.io/ultron/docs/common/allure) (Now, for Android UI tests only)
2324
- A straightforward and expressive syntax
2425
- Ensured **Stability** for all actions and assertions
2526
- Complete control over every action and assertion
26-
- Incredible interaction with [**RecyclerView**](https://github.com/open-tool/ultron/wiki/RecyclerView) and [**Compose lists**](https://github.com/open-tool/ultron/wiki/Compose#ultron-compose-lazycolumnlazyrow).
27-
- An **Architectural** approach to developing UI tests
27+
- Incredible interaction with lists: [**RecyclerView**](./android/recyclerview.md) and [**Compose LazyList**](https://open-tool.github.io/ultron/docs/compose/lazylist).
28+
- An **Architectural** approach to developing UI tests (search "Best practice")
2829
- An incredible mechanism for setups and teardowns (You can even set up preconditions for a single test within a test class, without affecting the others)
29-
- [The ability to effortlessly extend the framework with your own operations](https://github.com/open-tool/ultron/wiki/Ultron-Extension)
30+
- [The ability to effortlessly extend the framework with your own operations](https://open-tool.github.io/ultron/docs/common/extension)
3031
- Accelerated UI Automator operations
31-
- Ability to monitor each stage of operation execution with [Listeners](https://github.com/open-tool/ultron/wiki/Listeners)
32-
- [Custom operation assertions](https://github.com/open-tool/ultron/wiki/Custom-operation-assertions)
32+
- Ability to monitor each stage of operation execution with [Listeners](https://open-tool.github.io/ultron/docs/common/listeners)
33+
- [Custom operation assertions](https://open-tool.github.io/ultron/docs/common/customassertion)
3334

3435
***
35-
### Wiki
36-
The framework offers an excellent [wiki](https://github.com/open-tool/ultron/wiki) that addresses the majority of significant usage scenarios.
36+
### Documentation
37+
The framework offers an excellent [documentation](https://open-tool.github.io/ultron/docs/) that addresses the majority of significant usage scenarios.
3738

3839
### A few words about syntax
3940

40-
The standard syntax provided by Google is intricate and not intuitive. This is especially evident when dealing with RecyclerView interactions.
41+
The standard syntax provided by Google is intricate and not intuitive. This is especially evident when dealing with **LazyList** and **RecyclerView** interactions.
4142

4243
Let's explore some examples:
4344

44-
#### 1. Simple compose operation (refer to the wiki [here](https://github.com/open-tool/ultron/wiki/Compose#ultron-compose))
45+
#### 1. Simple compose operation (refer to the doc [here](https://open-tool.github.io/ultron/docs/compose/index))
4546

4647
_Compose framework_
4748

@@ -56,7 +57,7 @@ hasTestTag("Continue").click()
5657
hasText("Welcome").assertIsDisplayed()
5758
```
5859

59-
#### 2. Compose list operation (refer to the wiki [here](https://github.com/open-tool/ultron/wiki/Compose#ultron-compose-lazycolumnlazyrow))
60+
#### 2. Compose list operation (refer to the [doc](https://open-tool.github.io/ultron/docs/compose/lazylist))
6061

6162
_Compose framework_
6263

@@ -89,9 +90,9 @@ _Ultron_
8990
```kotlin
9091
withId(R.id.send_button).isDisplayed().click()
9192
```
92-
This presents a cleaner approach. Ultron's operation names mirror Espresso's, while also providing additional operations.
93+
This presents a cleaner approach. Ultron's operation names mirror Espresso's, while also providing additional operations.
9394

94-
Refer to the [wiki](https://github.com/open-tool/ultron/wiki/Espresso-operations) for further details.
95+
Refer to the [doc](https://open-tool.github.io/ultron/docs/android/espress) for further details.
9596

9697
#### 4. Action on RecyclerView list item
9798

@@ -115,9 +116,9 @@ withRecyclerView(R.id.recycler_friends)
115116
.click()
116117
```
117118

118-
Explore the [wiki](https://github.com/open-tool/ultron/wiki/RecyclerView) to unveil Ultron's magic with RecyclerView interactions.
119+
Explore the [doc](https://open-tool.github.io/ultron/docs/android/espress) to unveil Ultron's magic with RecyclerView interactions.
119120

120-
#### 5. Espresso WebView operations
121+
#### 5. Espresso WebView operations
121122

122123
_Espresso_
123124

@@ -139,7 +140,7 @@ id("button1").webClick()
139140
id("title").hasText(newTitle)
140141
```
141142

142-
Refer to the [wiki](https://github.com/open-tool/ultron/wiki/WebView) for more details.
143+
Refer to the [doc](https://open-tool.github.io/ultron/docs/android/webview) for more details.
143144

144145
#### 6. UI Automator operations
145146

@@ -157,7 +158,7 @@ _Ultron_
157158
```kotlin
158159
byResId(R.id.button1).click()
159160
```
160-
Refer to the [wiki](https://github.com/open-tool/ultron/wiki/UI-Automator-operation)
161+
Refer to the [doc](https://open-tool.github.io/ultron/docs/android/uiautomator)
161162
***
162163
### Acquiring the result of any operation as Boolean value
163164

@@ -237,27 +238,27 @@ Refer to the full code sample [DemoEspressoTest.class](https://github.com/open-t
237238

238239
In essence, your project's architecture will look like this:
239240

240-
<img src="https://github.com/open-tool/ultron/assets/12834123/b0882d34-a18d-4f1f-959b-f75796d11036" width=700>
241+
[acrchitecture](https://github.com/open-tool/ultron/assets/12834123/b0882d34-a18d-4f1f-959b-f75796d11036)
241242

243+
***
242244
## Allure report
243245

244-
Ultron has built in support to generate artifacts for Allure reports. Just apply the recommended configuration and set testIntrumentationRunner.
246+
Ultron has built in support to generate artifacts for Allure reports. Just apply the recommended configuration and set testIntrumentationRunner.
245247

246-
For the complete guide, refer to the [wiki](https://github.com/open-tool/ultron/wiki/Allure)
248+
For the complete guide, refer to the [Allure description](https://open-tool.github.io/ultron/docs/common/allure)
247249

248250
```kotlin
249251
@BeforeClass @JvmStatic
250252
fun setConfig() {
251253
UltronConfig.applyRecommended()
252254
UltronAllureConfig.applyRecommended()
253-
UltronComposeConfig.applyRecommended()
255+
UltronComposeConfig.applyRecommended()
254256
}
255257
```
256258
![allure](https://github.com/open-tool/ultron/assets/12834123/c05c813a-ece6-45e6-a04f-e1c92b82ffb1)
257259

258260
![allure compose](https://github.com/open-tool/ultron/assets/12834123/1f751f3d-fc58-4874-a850-acd9181bfb70)
259261

260-
261262
## Add Ultron to your project
262263

263264
Gradle
@@ -272,8 +273,4 @@ dependencies {
272273
androidTestImplementation 'com.atiurin:ultron-compose:<latest_version>'
273274
}
274275
```
275-
Please, read [gradle dependencies management](https://github.com/open-tool/ultron/wiki/Gradle-Dependencies-Management) wiki page.
276-
277-
## AndroidX
278-
279-
It is required to use AndroidX libraries. You can get some problems with Android Support ones.
276+
Please, read [gradle dependencies management](https://open-tool.github.io/ultron/docs/intro/dependencies) doc.

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ plugins {
2929
alias(libs.plugins.kotlinJvm) apply false
3030
alias(libs.plugins.compose.compiler) apply false
3131
id("io.github.gradle-nexus.publish-plugin").version("2.0.0-rc-1")
32+
alias(libs.plugins.kotlinAndroid) apply false
3233
}
3334

3435
nexusPublishing {

composeApp/build.gradle.kts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,20 @@ kotlin {
4141
isStatic = true
4242
}
4343
}
44-
js(IR)
44+
js(IR){
45+
browser()
46+
}
4547
@OptIn(ExperimentalWasmDsl::class)
46-
wasmJs()
48+
wasmJs(){
49+
browser {
50+
testTask(Action {
51+
useKarma {
52+
useChromeHeadless()
53+
useConfigDirectory(project.projectDir.resolve("karma.config.d").resolve("wasm"))
54+
}
55+
})
56+
}
57+
}
4758

4859
sourceSets {
4960
val desktopMain by getting
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// see https://kotlinlang.org/docs/js-project-setup.html#webpack-configuration-file
2+
// This file provides karma.config.d configuration to run tests with k/wasm
3+
4+
const path = require("path");
5+
6+
config.browserConsoleLogOptions.level = "debug";
7+
8+
const basePath = config.basePath;
9+
const projectPath = path.resolve(basePath, "..", "..", "..", "..");
10+
const generatedAssetsPath = path.resolve(projectPath, "build", "karma-webpack-out")
11+
12+
const debug = message => console.log(`[karma-config] ${message}`);
13+
14+
debug(`karma basePath: ${basePath}`);
15+
debug(`karma generatedAssetsPath: ${generatedAssetsPath}`);
16+
17+
config.proxies["/"] = path.resolve(basePath, "kotlin");
18+
19+
config.files = [
20+
{pattern: path.resolve(generatedAssetsPath, "**/*"), included: false, served: true, watched: false},
21+
{pattern: path.resolve(basePath, "kotlin", "**/*.png"), included: false, served: true, watched: false},
22+
{pattern: path.resolve(basePath, "kotlin", "**/*.gif"), included: false, served: true, watched: false},
23+
{pattern: path.resolve(basePath, "kotlin", "**/*.ttf"), included: false, served: true, watched: false},
24+
{pattern: path.resolve(basePath, "kotlin", "**/*.txt"), included: false, served: true, watched: false},
25+
{pattern: path.resolve(basePath, "kotlin", "**/*.json"), included: false, served: true, watched: false},
26+
{pattern: path.resolve(basePath, "kotlin", "**/*.xml"), included: false, served: true, watched: false},
27+
].concat(config.files);
28+
29+
function KarmaWebpackOutputFramework(config) {
30+
// This controller is instantiated and set during the preprocessor phase.
31+
const controller = config.__karmaWebpackController;
32+
33+
// only if webpack has instantiated its controller
34+
if (!controller) {
35+
console.warn(
36+
"Webpack has not instantiated controller yet.\n" +
37+
"Check if you have enabled webpack preprocessor and framework before this framework"
38+
)
39+
return
40+
}
41+
42+
config.files.push({
43+
pattern: `${controller.outputPath}/**/*`,
44+
included: false,
45+
served: true,
46+
watched: false
47+
})
48+
}
49+
50+
const KarmaWebpackOutputPlugin = {
51+
'framework:webpack-output': ['factory', KarmaWebpackOutputFramework],
52+
};
53+
54+
config.plugins.push(KarmaWebpackOutputPlugin);
55+
config.frameworks.push("webpack-output");

composeApp/src/commonTest/kotlin/BaseInteractionTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
import androidx.compose.foundation.layout.Column
23
import androidx.compose.material.Button
34
import androidx.compose.material.Text
@@ -11,9 +12,9 @@ import com.atiurin.ultron.core.compose.config.UltronComposeConfig
1112
import com.atiurin.ultron.core.compose.nodeinteraction.click
1213
import com.atiurin.ultron.core.compose.runUltronUiTest
1314
import com.atiurin.ultron.extensions.assertIsDisplayed
14-
import com.atiurin.ultron.extensions.assertTextContains
1515
import com.atiurin.ultron.extensions.isSuccess
1616
import com.atiurin.ultron.extensions.withAssertion
17+
import com.atiurin.ultron.extensions.withTimeout
1718
import com.atiurin.ultron.extensions.withUseUnmergedTree
1819
import kotlin.test.AfterTest
1920
import kotlin.test.Test
@@ -47,7 +48,7 @@ class BaseInteractionTest {
4748
}
4849
UltronComposeConfig.params.useUnmergedTree = true
4950
assertFalse("Ultron operation success should be false") {
50-
hasTestTag(testTag).isSuccess { assertTextContains("Text1") }
51+
hasTestTag(testTag).isSuccess { withTimeout(1000).assertTextContains("Text1") }
5152
}
5253
assertTrue ("Ultron operation success should be true") {
5354
hasTestTag(testTag).withUseUnmergedTree(false).isSuccess { assertTextContains("Text1") }

composeApp/src/commonTest/kotlin/ExampleTest.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
import androidx.compose.material.*
2-
import androidx.compose.runtime.*
1+
2+
import androidx.compose.material.Button
3+
import androidx.compose.material.Text
4+
import androidx.compose.runtime.getValue
5+
import androidx.compose.runtime.mutableStateOf
6+
import androidx.compose.runtime.remember
7+
import androidx.compose.runtime.setValue
38
import androidx.compose.ui.Modifier
49
import androidx.compose.ui.platform.testTag
5-
import androidx.compose.ui.test.*
10+
import androidx.compose.ui.test.ExperimentalTestApi
11+
import androidx.compose.ui.test.assertTextEquals
12+
import androidx.compose.ui.test.onNodeWithTag
13+
import androidx.compose.ui.test.performClick
14+
import androidx.compose.ui.test.runComposeUiTest
615
import kotlin.test.Test
716

8-
class DesktopExampleTest {
9-
17+
class ExampleTest {
1018
@OptIn(ExperimentalTestApi::class)
1119
@Test
1220
fun myTest() = runComposeUiTest {

composeApp/src/desktopTest/kotlin/DesktopSampleTest.kt

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,17 @@ import androidx.compose.runtime.*
33
import androidx.compose.ui.Modifier
44
import androidx.compose.ui.test.*
55
import androidx.compose.ui.platform.testTag
6-
import androidx.compose.ui.test.junit4.createComposeRule
7-
import org.junit.Rule
6+
import com.atiurin.ultron.core.compose.runDesktopUltronUiTest
7+
import com.atiurin.ultron.extensions.assertTextEquals
8+
import com.atiurin.ultron.extensions.click
9+
import com.atiurin.ultron.page.Page
810
import org.junit.Test
911

1012
class DesktopSampleTest {
11-
@get:Rule
12-
val rule = createComposeRule()
13-
13+
@OptIn(ExperimentalTestApi::class)
1414
@Test
15-
fun myTest(){
16-
// Declares a mock UI to demonstrate API calls
17-
//
18-
// Replace with your own declarations to test the code in your project
19-
rule.setContent {
15+
fun myTest() = runDesktopUltronUiTest {
16+
setContent {
2017
var text by remember { mutableStateOf("Hello") }
2118

2219
Text(
@@ -31,9 +28,19 @@ class DesktopSampleTest {
3128
}
3229
}
3330

34-
// Tests the declared UI with assertions and actions of the JUnit-based testing API
35-
rule.onNodeWithTag("text").assertTextEquals("Hello")
36-
rule.onNodeWithTag("button").performClick()
37-
rule.onNodeWithTag("text").assertTextEquals("Compose")
31+
SamplePage {
32+
someStep()
33+
}
34+
}
35+
}
36+
37+
object SamplePage : Page<SamplePage>() {
38+
private val text = hasTestTag("text")
39+
private val button = hasTestTag("button")
40+
41+
fun someStep(){
42+
text.assertTextEquals("Hello")
43+
button.click()
44+
text.assertTextEquals("Compose")
3845
}
3946
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import androidx.compose.material.Button
2+
import androidx.compose.material.Text
3+
import androidx.compose.runtime.getValue
4+
import androidx.compose.runtime.mutableStateOf
5+
import androidx.compose.runtime.remember
6+
import androidx.compose.runtime.setValue
7+
import androidx.compose.ui.Modifier
8+
import androidx.compose.ui.platform.testTag
9+
import androidx.compose.ui.test.ExperimentalTestApi
10+
import androidx.compose.ui.test.hasTestTag
11+
import com.atiurin.ultron.core.compose.runUltronUiTest
12+
import com.atiurin.ultron.extensions.assertTextEquals
13+
import com.atiurin.ultron.extensions.click
14+
import com.atiurin.ultron.page.Page
15+
import kotlin.test.Test
16+
17+
class IOSSampleTest {
18+
@OptIn(ExperimentalTestApi::class)
19+
@Test
20+
fun sampleTest() = runUltronUiTest {
21+
setContent {
22+
var text by remember { mutableStateOf("Hello") }
23+
24+
Text(
25+
text = text,
26+
modifier = Modifier.testTag("text")
27+
)
28+
Button(
29+
onClick = { text = "Compose" },
30+
modifier = Modifier.testTag("button")
31+
) {
32+
Text("Click me")
33+
}
34+
}
35+
36+
SamplePage {
37+
someStep()
38+
}
39+
}
40+
}
41+
42+
object SamplePage : Page<SamplePage>() {
43+
private val text = hasTestTag("text")
44+
private val button = hasTestTag("button")
45+
46+
fun someStep(){
47+
text.assertTextEquals("Hello")
48+
button.click()
49+
text.assertTextEquals("Compose")
50+
}
51+
}

0 commit comments

Comments
 (0)