diff --git a/.gitignore b/.gitignore index c22074e917..f1a52102ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.idea/ # Ignore Gradle project-specific cache directory .gradle diff --git a/CHANGELOG.md b/CHANGELOG.md index ea450186a4..76814e5cd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,10 +24,10 @@ Fixes: - Fix: not working when iOS simulator is in landscape orientation ([caveats apply](https://github.com/mobile-dev-inc/maestro/pull/1974#issuecomment-2346074593)) ([#1974](https://github.com/mobile-dev-inc/maestro/pull/1974)) - Fix: confusing error message "BlockingCoroutine is cancelling" ([#2036](https://github.com/mobile-dev-inc/maestro/pull/2036)) - Fix: AI-powered commands crashing when Anthropic is used ([#2033](https://github.com/mobile-dev-inc/maestro/pull/2033)) -- Fix: display warnings generated by AI-powered commands in CLI output when `optional: true` ([#2026](https://github.com/mobile-dev-inc/maestro/pull/2026)) -- Fix: visual bug with emojis having slightly different length in `maestro test`'s interactive CLI output ([#2016](https://github.com/mobile-dev-inc/maestro/pull/2016)) +- Fix: display warnings generated by AI-powered commands in CLI output when `optional: true` ([#2026](https://github.com/mobile-dev-inc/maestro/pull/2026)) +- Fix: visual bug with emojis having slightly different length in `maestro test`'s interactive CLI output ([#2016](https://github.com/mobile-dev-inc/maestro/pull/2016)) - Fix: no tests being run when flowsOrder specified all tests in the workspace ([#2003](https://github.com/mobile-dev-inc/maestro/pull/2003)) -- Fix: using integers from JavaScript outputs causing a deserialization error ([#1788](https://github.com/mobile-dev-inc/maestro/pull/1788) by [Muhammed Furkan Boran](https://github.com/boranfrkn)) +- Fix: using integers from JavaScript outputs causing a deserialization error ([#1788](https://github.com/mobile-dev-inc/maestro/pull/1788) by [Muhammed Furkan Boran](https://github.com/boranfrkn)) - Fix: delete temporary APKs after using them ([#1947](https://github.com/mobile-dev-inc/maestro/pull/1947) by [Tarek Belkahia](https://github.com/tokou)) - Fix: allow env vars in `setLocation` and `travel` commands ([#1988](https://github.com/mobile-dev-inc/maestro/pull/1988) by [Prasanta Biswas](https://github.com/prasanta-biswas)) - Fix: error message when specifying `--format` together with `--continuous` #1948 ([#1948](https://github.com/mobile-dev-inc/maestro/pull/1948) by [Tarek Belkahia](https://github.com/tokou)) @@ -37,6 +37,22 @@ Chores: - Chore: make Maestro build & compile on Java 17 ([#2008](https://github.com/mobile-dev-inc/maestro/pull/2008)) - Chore: Migrate all Gradle buildscripts to Gradle Kotlin DSL ([#1994](https://github.com/mobile-dev-inc/maestro/pull/1994)) +=== My Changes === + +## 1.38.2 + +Released on 2024-09-26 + +- change install script to rasyid7 +- update README +- change version checker to own repo +- [add sleep feature on maestro](https://github.com/rasyid7/maestro/commit/2a8575583af3aaf71de23c7def902db30041fa06) +- [fix tags include from OR to AND](https://github.com/rasyid7/maestro/commit/9949f1b204e86dedb3f2912ffdfa65cca2e6121f) + +-- note: need to implement another [--parallel option](https://github.com/rasyid7/maestro/commit/3d291ee0145c9e925a3fa1702a26e49230b6ec03) + +======= + ## 1.38.1 Released on 2024-08-30 @@ -89,7 +105,7 @@ Released on 2024-07-29 ### Bug fixes -- Fix `FileNotFoundException: ~.maestro/sessions` ([#1843](https://github.com/mobile-dev-inc/maestro/pull/1843)) +- Fix `FileNotFoundException: ~.maestro/sessions` ([#1843](https://github.com/mobile-dev-inc/maestro/pull/1843)) ## 1.37.2 - 2024-07-29 diff --git a/README.md b/README.md index 1339c0fd08..0cc068fde1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,14 @@ Maestro is the easiest way to automate UI testing for your mobile app. +> [!NOTE] +> +> **Full documentation for Maestro can be found at [maestro.mobile.dev](https://maestro.mobile.dev)** +> +> Since this is forked REPO, to install this maestro, please use +> +> `curl -Ls "https://raw.githubusercontent.com/rasyid7/maestro/main/scripts/install.sh" | bash` + ## Why Maestro? diff --git a/gradle.properties b/gradle.properties index 35378f46aa..7c27f833b6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ android.useAndroidX=true android.enableJetifier=true kotlin.code.style=official GROUP=dev.mobile -VERSION_NAME=1.38.1 +VERSION_NAME=1.38.2 POM_DESCRIPTION=Maestro is a server-driven platform-agnostic library that allows to drive tests for both iOS and Android using the same implementation through an intuitive API. POM_URL=https://github.com/mobile-dev-inc/maestro POM_SCM_URL=https://github.com/mobile-dev-inc/maestro diff --git a/maestro-cli/gradle.properties b/maestro-cli/gradle.properties index 000e8f711c..0f85fb6c76 100644 --- a/maestro-cli/gradle.properties +++ b/maestro-cli/gradle.properties @@ -1 +1 @@ -CLI_VERSION=1.38.1 +CLI_VERSION=1.38.2 diff --git a/maestro-cli/src/main/java/maestro/cli/App.kt b/maestro-cli/src/main/java/maestro/cli/App.kt index c50a238f9a..7db68d9d57 100644 --- a/maestro-cli/src/main/java/maestro/cli/App.kt +++ b/maestro-cli/src/main/java/maestro/cli/App.kt @@ -148,16 +148,10 @@ fun main(args: Array) { if (newVersion != null) { Updates.fetchChangelogAsync() System.err.println() - val changelog = Updates.getChangelog() - val anchor = newVersion.toString().replace(".", "") - System.err.println(listOf( - "A new version of the Maestro CLI is available ($newVersion).\n", - "See what's new:", - "https://github.com/mobile-dev-inc/maestro/blob/main/CHANGELOG.md#$anchor", - ChangeLogUtils.print(changelog), - "Upgrade command:", - "curl -Ls \"https://get.maestro.mobile.dev\" | bash", - ).joinToString("\n").box()) + System.err.println( + ("A new version of the Maestro CLI is available ($newVersion). Upgrade command:\n" + + "curl -Ls \"https://raw.githubusercontent.com/rasyid7/maestro/main/scripts/install.sh\" | bash").box() + ) } if (commandLine.isVersionHelpRequested) { diff --git a/maestro-cli/src/main/java/maestro/cli/api/ApiClient.kt b/maestro-cli/src/main/java/maestro/cli/api/ApiClient.kt index 16ed950c30..3a85d71203 100644 --- a/maestro-cli/src/main/java/maestro/cli/api/ApiClient.kt +++ b/maestro-cli/src/main/java/maestro/cli/api/ApiClient.kt @@ -88,7 +88,7 @@ class ApiClient( fun getLatestCliVersion(): CliVersion { val request = Request.Builder() .header("X-FRESH-INSTALL", if (!Analytics.hasRunBefore) "true" else "false") - .url("$baseUrl/maestro/version") + .url("https://raw.githubusercontent.com/rasyid7/maestro/main/version") .get() .build() diff --git a/maestro-client/src/main/java/maestro/Maestro.kt b/maestro-client/src/main/java/maestro/Maestro.kt index 1876e8365c..16a8fec3b3 100644 --- a/maestro-client/src/main/java/maestro/Maestro.kt +++ b/maestro-client/src/main/java/maestro/Maestro.kt @@ -581,6 +581,12 @@ class Maestro( ScreenshotUtils.waitUntilScreenIsStatic(timeout, SCREENSHOT_DIFF_THRESHOLD, driver) } + fun sleep(time: Long?) { + val time = time ?: ANIMATION_TIMEOUT_MS + LOGGER.info("Sleep for $time ms") + Thread.sleep(time) + } + fun setProxy( host: String = SocketUtils.localIp(), port: Int diff --git a/maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt b/maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt index eed7c3d390..1c728895e3 100644 --- a/maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt +++ b/maestro-orchestra-models/src/main/java/maestro/orchestra/Commands.kt @@ -37,7 +37,7 @@ sealed interface Command { fun visible(): Boolean = true val label: String? - + val optional: Boolean } @@ -864,6 +864,21 @@ data class WaitForAnimationToEndCommand( } } +data class SleepCommand( + val time: Long?, + override val label: String? = null, + override val optional: Boolean = false, +) : Command { + + override fun description(): String { + return label ?: "Sleep for $time ms" + } + + override fun evaluateScripts(jsEngine: JsEngine): Command { + return this + } +} + data class EvalScriptCommand( val scriptString: String, override val label: String? = null, diff --git a/maestro-orchestra-models/src/main/java/maestro/orchestra/MaestroCommand.kt b/maestro-orchestra-models/src/main/java/maestro/orchestra/MaestroCommand.kt index bb7090f5b3..46943dc850 100644 --- a/maestro-orchestra-models/src/main/java/maestro/orchestra/MaestroCommand.kt +++ b/maestro-orchestra-models/src/main/java/maestro/orchestra/MaestroCommand.kt @@ -67,6 +67,7 @@ data class MaestroCommand( val addMediaCommand: AddMediaCommand? = null, val setAirplaneModeCommand: SetAirplaneModeCommand? = null, val toggleAirplaneModeCommand: ToggleAirplaneModeCommand? = null, + val sleepCommand: SleepCommand? = null, ) { constructor(command: Command) : this( @@ -109,6 +110,7 @@ data class MaestroCommand( addMediaCommand = command as? AddMediaCommand, setAirplaneModeCommand = command as? SetAirplaneModeCommand, toggleAirplaneModeCommand = command as? ToggleAirplaneModeCommand, + sleepCommand = command as? SleepCommand, ) fun asCommand(): Command? = when { @@ -151,6 +153,7 @@ data class MaestroCommand( addMediaCommand != null -> addMediaCommand setAirplaneModeCommand != null -> setAirplaneModeCommand toggleAirplaneModeCommand != null -> toggleAirplaneModeCommand + sleepCommand != null -> sleepCommand else -> null } diff --git a/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt b/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt index 33b68efaae..53c96afcba 100644 --- a/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt +++ b/maestro-orchestra/src/main/java/maestro/orchestra/Orchestra.kt @@ -295,6 +295,7 @@ class Orchestra( is AddMediaCommand -> addMediaCommand(command.mediaPaths) is SetAirplaneModeCommand -> setAirplaneMode(command) is ToggleAirplaneModeCommand -> toggleAirplaneMode() + is SleepCommand -> sleepCommand(command) else -> true }.also { mutating -> if (mutating) { @@ -435,6 +436,11 @@ class Orchestra( return true } + private fun sleepCommand(command: SleepCommand): Boolean { + maestro.sleep(command.time) + return true + } + private fun defineVariablesCommand(command: DefineVariablesCommand): Boolean { command.env.forEach { (name, value) -> jsEngine.putEnv(name, value) diff --git a/maestro-orchestra/src/main/java/maestro/orchestra/workspace/WorkspaceExecutionPlanner.kt b/maestro-orchestra/src/main/java/maestro/orchestra/workspace/WorkspaceExecutionPlanner.kt index 48b14574f0..cb1a5d5f85 100644 --- a/maestro-orchestra/src/main/java/maestro/orchestra/workspace/WorkspaceExecutionPlanner.kt +++ b/maestro-orchestra/src/main/java/maestro/orchestra/workspace/WorkspaceExecutionPlanner.kt @@ -101,10 +101,17 @@ object WorkspaceExecutionPlanner { val config = configPerFlowFile[it] val tags = config?.tags ?: emptyList() - (allIncludeTags.isEmpty() || tags.any(allIncludeTags::contains)) + (allIncludeTags.isEmpty() || allIncludeTags.all { includeTag -> tags.contains(includeTag) }) && (allExcludeTags.isEmpty() || !tags.any(allExcludeTags::contains)) } + println("") + println("Number flows to run: ${allFlows.size}") + println("Flows to run:") + for (flow in allFlows) { + println(" * ${flow.fileName.toString().substringBeforeLast(".")}") + } + if (allFlows.isEmpty()) { val message = """ |Include / Exclude tags did not match any Flows: diff --git a/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlFluentCommand.kt b/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlFluentCommand.kt index 447b22ee6c..ecda3b72f8 100644 --- a/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlFluentCommand.kt +++ b/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlFluentCommand.kt @@ -79,6 +79,7 @@ data class YamlFluentCommand( val addMedia: YamlAddMedia? = null, val setAirplaneMode: YamlSetAirplaneMode? = null, val toggleAirplaneMode: YamlToggleAirplaneMode? = null, + val sleep: YamlSleepCommand? = null, ) { @SuppressWarnings("ComplexMethod") @@ -251,8 +252,16 @@ data class YamlFluentCommand( val tapRepeat = TapRepeat(2, delay) listOf(tapCommand(doubleTapOn, tapRepeat = tapRepeat)) } - setAirplaneMode != null -> listOf(MaestroCommand(SetAirplaneModeCommand(setAirplaneMode.value, setAirplaneMode.label, setAirplaneMode.optional))) - toggleAirplaneMode != null -> listOf(MaestroCommand(ToggleAirplaneModeCommand(toggleAirplaneMode.label, toggleAirplaneMode.optional))) + setAirplaneMode != null -> listOf(MaestroCommand(SetAirplaneModeCommand(setAirplaneMode.value, setAirplaneMode.label))) + toggleAirplaneMode != null -> listOf(MaestroCommand(ToggleAirplaneModeCommand(toggleAirplaneMode.label))) + sleep != null -> listOf( + MaestroCommand( + SleepCommand( + time = sleep.time, + label = sleep.label + ) + ) + ) else -> throw SyntaxError("Invalid command: No mapping provided for $this") } } @@ -726,6 +735,10 @@ data class YamlFluentCommand( assertNoDefectsWithAI = YamlAssertNoDefectsWithAI() ) + "sleep" -> YamlFluentCommand( + sleep = YamlSleepCommand(time = null) + ) + else -> throw SyntaxError("Invalid command: \"$stringCommand\"") } } diff --git a/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlSleepCommand.kt b/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlSleepCommand.kt new file mode 100644 index 0000000000..1f938ae691 --- /dev/null +++ b/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlSleepCommand.kt @@ -0,0 +1,16 @@ +package maestro.orchestra.yaml + +import com.fasterxml.jackson.annotation.JsonCreator + +data class YamlSleepCommand( + val time: Long?, + val label: String? = null, +) { + companion object { + @JvmStatic + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + fun create(time: Long): YamlSleepCommand { + return YamlSleepCommand(time = time) + } + } +} diff --git a/maestro-orchestra/src/test/java/maestro/orchestra/MaestroCommandSerializationTest.kt b/maestro-orchestra/src/test/java/maestro/orchestra/MaestroCommandSerializationTest.kt index b962514b43..436b4f15e9 100644 --- a/maestro-orchestra/src/test/java/maestro/orchestra/MaestroCommandSerializationTest.kt +++ b/maestro-orchestra/src/test/java/maestro/orchestra/MaestroCommandSerializationTest.kt @@ -587,6 +587,32 @@ internal class MaestroCommandSerializationTest { .isEqualTo(command) } + @Test + fun `serialize SleepCommand`() { + // given + val command = MaestroCommand( + SleepCommand(time = 1000) + ) + + // when + val serializedCommandJson = command.toJson() + val deserializedCommand = objectMapper.readValue(serializedCommandJson, MaestroCommand::class.java) + + // then + @Language("json") + val expectedJson = """ + { + "sleepCommand" : { + "time" : 1000 + } + } + """.trimIndent() + assertThat(serializedCommandJson) + .isEqualTo(expectedJson) + assertThat(deserializedCommand) + .isEqualTo(command) + } + private fun MaestroCommand.toJson(): String = objectMapper .writerWithDefaultPrettyPrinter() diff --git a/maestro-orchestra/src/test/java/maestro/orchestra/workspace/WorkspaceExecutionPlannerTest.kt b/maestro-orchestra/src/test/java/maestro/orchestra/workspace/WorkspaceExecutionPlannerTest.kt index f66d1cf3e6..2589dab184 100644 --- a/maestro-orchestra/src/test/java/maestro/orchestra/workspace/WorkspaceExecutionPlannerTest.kt +++ b/maestro-orchestra/src/test/java/maestro/orchestra/workspace/WorkspaceExecutionPlannerTest.kt @@ -258,7 +258,6 @@ internal class WorkspaceExecutionPlannerTest { assertThat(plan.flowsToRun).containsExactly( path("/workspaces/010_global_include_tags/flowA.yaml"), path("/workspaces/010_global_include_tags/flowA_subflow.yaml"), - path("/workspaces/010_global_include_tags/flowB.yaml"), ) } diff --git a/scripts/install.sh b/scripts/install.sh index a7f21069df..cf6477a068 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -90,9 +90,9 @@ mkdir -p "$maestro_tmp_folder" if [ -z "$MAESTRO_VERSION" ]; then - download_url="https://github.com/mobile-dev-inc/maestro/releases/latest/download/maestro.zip" + download_url="https://github.com/rasyid7/maestro/releases/latest/download/maestro.zip" else - download_url="https://github.com/mobile-dev-inc/maestro/releases/download/cli-$MAESTRO_VERSION/maestro.zip" + download_url="https://github.com/rasyid7/maestro/releases/download/cli-$MAESTRO_VERSION/maestro.zip" fi maestro_zip_file="${maestro_tmp_folder}/maestro.zip" diff --git a/version b/version new file mode 100644 index 0000000000..2c959a01bb --- /dev/null +++ b/version @@ -0,0 +1 @@ +{"major":1,"minor":38,"patch":2} \ No newline at end of file