├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.yaml
│ └── feature_request.yaml
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── close-inactive-issues.yaml
│ ├── lock-closed-issues.yaml
│ ├── publish-cli.yaml
│ ├── publish-release.yaml
│ ├── publish-snapshot.yaml
│ ├── test-e2e-prod.yaml
│ ├── test-e2e.yaml
│ ├── test.yaml
│ ├── update-samples.yaml
│ └── warn_build_xctestrunner.yaml
├── .gitignore
├── .idea
├── .gitignore
└── icon.svg
├── .run
├── cli-version.run.xml
└── cli.run.xml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── RELEASING.md
├── assets
├── add_contact_android.gif
├── edit_contacts_ios.gif
├── project-dependency-graph.svg
└── run-on-robin.png
├── build.gradle.kts
├── debug.keystore
├── detekt.yml
├── e2e
├── .gitignore
├── README.md
├── download_apps
├── install_apps
├── manifest.txt
├── run_tests
├── update_samples
└── workspaces
│ ├── demo_app
│ ├── ai_complex.yaml
│ ├── ai_simple.yaml
│ ├── commands
│ │ ├── assertNotVisible.yaml
│ │ ├── assertTrue.yaml
│ │ ├── assertVisible.yaml
│ │ ├── back.yaml
│ │ ├── copyTextFrom.yaml
│ │ ├── eraseText.yaml
│ │ ├── evalScript.yaml
│ │ ├── extendedWaitUntil.yaml
│ │ ├── hideKeyboard.yaml
│ │ ├── inputRandomEmail.yaml
│ │ ├── inputRandomNumber.yaml
│ │ ├── inputRandomPersonName.yaml
│ │ ├── inputRandomText.yaml
│ │ ├── inputText.yaml
│ │ ├── killApp.yaml
│ │ ├── launchApp.yaml
│ │ ├── pasteText.yaml
│ │ ├── pressKey.yaml
│ │ ├── repeat.yaml
│ │ ├── retry.yaml
│ │ ├── runFlow.yaml
│ │ ├── runScript.js
│ │ └── runScript.yaml
│ ├── commands_optional_tournee.yaml
│ ├── commands_tour.yaml
│ ├── fail_fast.yaml
│ ├── fail_launchApp.yaml
│ ├── fail_launchApp_nonDefault.yaml
│ ├── fail_not_found.yaml
│ ├── fail_visible.yaml
│ ├── fail_visible_extended.yaml
│ ├── fill_form.yaml
│ ├── long_input_text.yaml
│ ├── relatives.yaml
│ ├── scrollUntilVisible_timeout.yaml
│ └── swipe.yaml
│ ├── no-app
│ ├── README.md
│ └── environment-variables.yaml
│ ├── nowinandroid
│ ├── bookmarks.yaml
│ └── fail.yaml
│ └── wikipedia
│ ├── android-advanced-flow.yaml
│ ├── android-flow.yaml
│ ├── ios-advanced-flow.yaml
│ ├── ios-flow.yaml
│ ├── scripts
│ └── getSearchQuery.js
│ ├── subflows
│ ├── launch-clearstate-android.yaml
│ ├── launch-clearstate-ios.yaml
│ ├── onboarding-android.yaml
│ └── onboarding-ios.yaml
│ └── wikipedia-android-advanced
│ ├── auth
│ ├── login.yml
│ └── signup.yml
│ ├── dashboard
│ ├── copy-paste.yml
│ ├── feed.yml
│ ├── main.yml
│ ├── saved.yml
│ └── search.yml
│ ├── onboarding
│ ├── add-language.yml
│ ├── main.yml
│ └── remove-language.yml
│ ├── run-test.yml
│ └── scripts
│ ├── fetchTestUser.js
│ └── generateCredentials.js
├── example
├── build.gradle.kts
└── src
│ └── main
│ └── kotlin
│ └── Main.kt
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── installLocally.sh
├── logo.png
├── maestro
├── maestro-ai
├── README.md
├── build.gradle.kts
├── gradle.properties
└── src
│ └── main
│ ├── java
│ └── maestro
│ │ └── ai
│ │ ├── AI.kt
│ │ ├── CloudPredictionAIEngine.kt
│ │ ├── DemoApp.kt
│ │ ├── IAPredictionEngine.kt
│ │ ├── Prediction.kt
│ │ ├── anthropic
│ │ ├── Client.kt
│ │ ├── Common.kt
│ │ ├── Request.kt
│ │ └── Response.kt
│ │ ├── cloud
│ │ └── ApiClient.kt
│ │ ├── common
│ │ └── Image.kt
│ │ └── openai
│ │ ├── Client.kt
│ │ ├── Request.kt
│ │ └── Response.kt
│ └── resources
│ ├── askForDefects_schema.json
│ └── extractText_schema.json
├── maestro-android
├── build.gradle.kts
└── src
│ ├── androidTest
│ ├── AndroidManifest.xml
│ └── java
│ │ ├── androidx
│ │ └── test
│ │ │ └── uiautomator
│ │ │ └── UiDeviceExt.kt
│ │ └── dev
│ │ └── mobile
│ │ └── maestro
│ │ ├── AccessibilityNodeInfoExt.kt
│ │ ├── MaestroDriverService.kt
│ │ ├── Media.kt
│ │ ├── ToastAccessibilityListener.kt
│ │ ├── ViewHierarchy.kt
│ │ └── location
│ │ ├── FusedLocationProvider.kt
│ │ ├── LocationManagerProvider.kt
│ │ ├── MockLocationProvider.kt
│ │ └── PlayServices.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── dev
│ │ └── mobile
│ │ └── maestro
│ │ ├── handlers
│ │ ├── AbstractSettingHandler.kt
│ │ └── LocaleSettingHandler.kt
│ │ └── receivers
│ │ ├── HasAction.kt
│ │ └── LocaleSettingReceiver.kt
│ └── res
│ └── values
│ └── stub.xml
├── maestro-cli
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── jreleaser
│ └── distributions
│ │ └── maestro
│ │ └── brew
│ │ └── formula.rb.tpl
│ ├── main
│ ├── java
│ │ └── maestro
│ │ │ └── cli
│ │ │ ├── App.kt
│ │ │ ├── CliError.kt
│ │ │ ├── Dependencies.kt
│ │ │ ├── DisableAnsiMixin.kt
│ │ │ ├── ShowHelpMixin.kt
│ │ │ ├── analytics
│ │ │ └── Analytics.kt
│ │ │ ├── api
│ │ │ ├── ApiClient.kt
│ │ │ └── Chatbot.kt
│ │ │ ├── auth
│ │ │ └── Auth.kt
│ │ │ ├── cloud
│ │ │ └── CloudInteractor.kt
│ │ │ ├── command
│ │ │ ├── BugReportCommand.kt
│ │ │ ├── ChatCommand.kt
│ │ │ ├── CheckSyntaxCommand.kt
│ │ │ ├── CloudCommand.kt
│ │ │ ├── DownloadSamplesCommand.kt
│ │ │ ├── DriverCommand.kt
│ │ │ ├── LoginCommand.kt
│ │ │ ├── LogoutCommand.kt
│ │ │ ├── PrintHierarchyCommand.kt
│ │ │ ├── QueryCommand.kt
│ │ │ ├── RecordCommand.kt
│ │ │ ├── StartDeviceCommand.kt
│ │ │ ├── StudioCommand.kt
│ │ │ ├── TestCommand.kt
│ │ │ └── UploadCommand.kt
│ │ │ ├── db
│ │ │ └── KeyValueStore.kt
│ │ │ ├── device
│ │ │ ├── DeviceCreateUtil.kt
│ │ │ ├── PickDeviceInteractor.kt
│ │ │ └── PickDeviceView.kt
│ │ │ ├── driver
│ │ │ ├── DriverBuildConfig.kt
│ │ │ ├── DriverBuilder.kt
│ │ │ ├── RealIOSDeviceDriver.kt
│ │ │ ├── Spinner.kt
│ │ │ └── XcodeBuildProcessBuilderFactory.kt
│ │ │ ├── graphics
│ │ │ ├── AWTUtils.kt
│ │ │ ├── LocalVideoRenderer.kt
│ │ │ ├── RemoteVideoRenderer.kt
│ │ │ ├── SkiaFrameRenderer.kt
│ │ │ ├── SkiaTextClipper.kt
│ │ │ ├── SkiaUtils.kt
│ │ │ └── VideoRenderer.kt
│ │ │ ├── insights
│ │ │ └── TestAnalysisManager.kt
│ │ │ ├── model
│ │ │ ├── DeviceStartOptions.kt
│ │ │ ├── FlowStatus.kt
│ │ │ ├── RunningFlow.kt
│ │ │ └── TestExecutionSummary.kt
│ │ │ ├── report
│ │ │ ├── HtmlAITestSuiteReporter.kt
│ │ │ ├── HtmlInsightsAnalysisReporter.kt
│ │ │ ├── HtmlTestSuiteReporter.kt
│ │ │ ├── JUnitTestSuiteReporter.kt
│ │ │ ├── ReportFormat.kt
│ │ │ ├── ReporterFactory.kt
│ │ │ ├── TestDebugReporter.kt
│ │ │ └── TestSuiteReporter.kt
│ │ │ ├── runner
│ │ │ ├── CliWatcher.kt
│ │ │ ├── CommandState.kt
│ │ │ ├── CommandStatus.kt
│ │ │ ├── FileWatcher.kt
│ │ │ ├── MaestroCommandRunner.kt
│ │ │ ├── TestRunner.kt
│ │ │ ├── TestSuiteInteractor.kt
│ │ │ └── resultview
│ │ │ │ ├── AnsiResultView.kt
│ │ │ │ ├── PlainTextResultView.kt
│ │ │ │ ├── ResultView.kt
│ │ │ │ └── UiState.kt
│ │ │ ├── session
│ │ │ ├── MaestroSessionManager.kt
│ │ │ └── SessionStore.kt
│ │ │ ├── update
│ │ │ └── Updates.kt
│ │ │ ├── util
│ │ │ ├── ChangeLogUtils.kt
│ │ │ ├── CiUtils.kt
│ │ │ ├── DeviceConfig.kt
│ │ │ ├── EnvUtils.kt
│ │ │ ├── ErrorReporter.kt
│ │ │ ├── FileDownloader.kt
│ │ │ ├── FileUtils.kt
│ │ │ ├── IOSEnvUtils.kt
│ │ │ ├── PrintUtils.kt
│ │ │ ├── ResourceUtils.kt
│ │ │ ├── ScreenReporter.kt
│ │ │ ├── ScreenshotUtils.kt
│ │ │ ├── SocketUtils.kt
│ │ │ ├── TimeUtils.kt
│ │ │ ├── Unpacker.kt
│ │ │ └── WorkspaceUtils.kt
│ │ │ ├── view
│ │ │ ├── ErrorViewUtils.kt
│ │ │ ├── ProgressBar.kt
│ │ │ ├── TestSuiteStatusView.kt
│ │ │ └── ViewUtils.kt
│ │ │ └── web
│ │ │ └── WebInteractor.kt
│ └── resources
│ │ ├── ai_report.css
│ │ ├── deps
│ │ └── applesimutils
│ │ ├── logback-test.xml
│ │ ├── record-background.jpg
│ │ └── tailwind.config.js
│ └── test
│ ├── kotlin
│ └── maestro
│ │ └── cli
│ │ ├── android
│ │ ├── AndroidDeviceProvider.kt
│ │ └── AndroidIntegrationTest.kt
│ │ ├── driver
│ │ ├── DriverBuilderTest.kt
│ │ └── RealDeviceDriverTest.kt
│ │ ├── report
│ │ └── JUnitTestSuiteReporterTest.kt
│ │ └── util
│ │ └── ChangeLogUtilsTest.kt
│ └── resources
│ ├── location
│ └── assert_multiple_locations.yaml
│ └── travel
│ └── assert_travel_command.yaml
├── maestro-client
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── main
│ ├── java
│ │ └── maestro
│ │ │ ├── Bounds.kt
│ │ │ ├── Capability.kt
│ │ │ ├── DeviceInfo.kt
│ │ │ ├── Driver.kt
│ │ │ ├── Errors.kt
│ │ │ ├── Filters.kt
│ │ │ ├── FindElementResult.kt
│ │ │ ├── KeyCode.kt
│ │ │ ├── Maestro.kt
│ │ │ ├── Media.kt
│ │ │ ├── Platform.kt
│ │ │ ├── Point.kt
│ │ │ ├── ScreenRecording.kt
│ │ │ ├── ScrollDirection.kt
│ │ │ ├── SwipeDirection.kt
│ │ │ ├── TapRepeat.kt
│ │ │ ├── TreeNode.kt
│ │ │ ├── UiElement.kt
│ │ │ ├── ViewHierarchy.kt
│ │ │ ├── android
│ │ │ ├── AndroidAppFiles.kt
│ │ │ ├── AndroidBuildToolsDirectory.kt
│ │ │ ├── AndroidLaunchArguments.kt
│ │ │ └── chromedevtools
│ │ │ │ ├── AndroidWebViewHierarchyClient.kt
│ │ │ │ ├── DadbChromeDevToolsClient.kt
│ │ │ │ └── DadbSocket.kt
│ │ │ ├── debuglog
│ │ │ ├── DebugLogStore.kt
│ │ │ └── LogConfig.kt
│ │ │ ├── device
│ │ │ ├── Device.kt
│ │ │ ├── DeviceError.kt
│ │ │ ├── DeviceService.kt
│ │ │ ├── Platform.kt
│ │ │ └── util
│ │ │ │ ├── AndroidEnvUtils.kt
│ │ │ │ ├── AvdDevice.kt
│ │ │ │ ├── CommandLineUtils.kt
│ │ │ │ ├── EnvUtils.kt
│ │ │ │ ├── PrintUtils.kt
│ │ │ │ ├── SimctlList.kt
│ │ │ │ └── SystemInfo.kt
│ │ │ ├── drivers
│ │ │ ├── AndroidDriver.kt
│ │ │ ├── IOSDriver.kt
│ │ │ └── WebDriver.kt
│ │ │ ├── js
│ │ │ ├── GraalJsEngine.kt
│ │ │ ├── GraalJsHttp.kt
│ │ │ ├── Js.kt
│ │ │ ├── JsConsole.kt
│ │ │ ├── JsEngine.kt
│ │ │ ├── JsHttp.kt
│ │ │ ├── JsScope.kt
│ │ │ └── RhinoJsEngine.kt
│ │ │ ├── mockserver
│ │ │ └── MockInteractor.kt
│ │ │ └── utils
│ │ │ ├── BlockingStreamObserver.kt
│ │ │ ├── FileUtils.kt
│ │ │ ├── HttpUtils.kt
│ │ │ ├── LocaleUtils.kt
│ │ │ ├── ScreenshotUtils.kt
│ │ │ ├── StringUtils.kt
│ │ │ └── TemporaryDirectory.kt
│ └── resources
│ │ ├── maestro-app.apk
│ │ ├── maestro-server.apk
│ │ └── maestro-web.js
│ └── test
│ ├── java
│ └── maestro
│ │ ├── PointTest.kt
│ │ ├── UiElementTest.kt
│ │ ├── android
│ │ ├── AndroidAppFilesTest.kt
│ │ ├── AndroidLaunchArgumentsTest.kt
│ │ └── chromedevtools
│ │ │ └── AndroidWebViewHierarchyClientTest.kt
│ │ ├── ios
│ │ └── MockXCTestInstaller.kt
│ │ ├── utils
│ │ ├── HttpUtilsTest.kt
│ │ ├── LocaleUtilsTest.kt
│ │ └── StringUtilsTest.kt
│ │ └── xctestdriver
│ │ └── XCTestDriverClientTest.kt
│ └── resources
│ └── logback-test.xml
├── maestro-ios-driver
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── main
│ ├── kotlin
│ │ ├── device
│ │ │ ├── IOSDevice.kt
│ │ │ └── SimctlIOSDevice.kt
│ │ ├── hierarchy
│ │ │ └── AXElement.kt
│ │ ├── util
│ │ │ ├── CommandLineUtils.kt
│ │ │ ├── IOSDevice.kt
│ │ │ ├── IOSLaunchArguments.kt
│ │ │ ├── LocalIOSDevice.kt
│ │ │ ├── LocalIOSDeviceController.kt
│ │ │ ├── LocalSimulatorUtils.kt
│ │ │ ├── PrintUtils.kt
│ │ │ ├── SimctlList.kt
│ │ │ └── XCRunnerCLIUtils.kt
│ │ └── xcuitest
│ │ │ ├── XCTestClient.kt
│ │ │ ├── XCTestDriverClient.kt
│ │ │ ├── api
│ │ │ ├── DeviceInfo.kt
│ │ │ ├── EraseTextRequest.kt
│ │ │ ├── Error.kt
│ │ │ ├── GetRunningAppIdResponse.kt
│ │ │ ├── GetRunningAppRequest.kt
│ │ │ ├── InputTextRequest.kt
│ │ │ ├── IsScreenStaticResponse.kt
│ │ │ ├── KeyboardInfoRequest.kt
│ │ │ ├── KeyboardInfoResponse.kt
│ │ │ ├── LaunchAppRequest.kt
│ │ │ ├── NetworkExceptions.kt
│ │ │ ├── OkHttpClientInstance.kt
│ │ │ ├── PressButtonRequest.kt
│ │ │ ├── PressKeyRequest.kt
│ │ │ ├── SetPermissionsRequest.kt
│ │ │ ├── SwipeRequest.kt
│ │ │ ├── TerminateAppRequest.kt
│ │ │ ├── TouchRequest.kt
│ │ │ └── ViewHierarchyRequest.kt
│ │ │ └── installer
│ │ │ ├── IOSBuildProductsExtractor.kt
│ │ │ ├── LocalXCTestInstaller.kt
│ │ │ └── XCTestInstaller.kt
│ └── resources
│ │ ├── driver-iPhoneSimulator
│ │ ├── Debug-iphonesimulator
│ │ │ ├── maestro-driver-ios.zip
│ │ │ └── maestro-driver-iosUITests-Runner.zip
│ │ └── maestro-driver-ios-config.xctestrun
│ │ ├── driver-iphoneos
│ │ ├── Debug-iphoneos
│ │ │ ├── maestro-driver-ios.zip
│ │ │ └── maestro-driver-iosUITests-Runner.zip
│ │ └── maestro-driver-ios-config.xctestrun
│ │ └── screenrecord.sh
│ └── test
│ └── kotlin
│ ├── DeviceCtlResponseTest.kt
│ ├── IOSBuildProductsExtractorTest.kt
│ └── IOSLaunchArgumentsTest.kt
├── maestro-ios-xctest-runner
├── .gitignore
├── build-maestro-ios-runner-all.sh
├── build-maestro-ios-runner.sh
├── maestro-driver-ios.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── driver-verification-tests.xcscheme
│ │ └── maestro-driver-ios.xcscheme
├── maestro-driver-ios
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── SceneDelegate.swift
│ └── ViewController.swift
├── maestro-driver-iosUITests
│ ├── Categories
│ │ ├── XCAXClient_iOS+FBSnapshotReqParams.h
│ │ ├── XCAXClient_iOS+FBSnapshotReqParams.m
│ │ ├── XCUIApplication+FBQuiescence.h
│ │ ├── XCUIApplication+FBQuiescence.m
│ │ ├── XCUIApplication+Helper.h
│ │ ├── XCUIApplication+Helper.m
│ │ ├── XCUIApplicationProcess+FBQuiescence.h
│ │ ├── XCUIApplicationProcess+FBQuiescence.m
│ │ └── maestro-driver-iosUITests-Bridging-Header.h
│ ├── PrivateHeaders
│ │ └── XCTest
│ │ │ ├── CDStructures.h
│ │ │ ├── NSString-XCTAdditions.h
│ │ │ ├── NSValue-XCTestAdditions.h
│ │ │ ├── UIGestureRecognizer-RecordingAdditions.h
│ │ │ ├── UILongPressGestureRecognizer-RecordingAdditions.h
│ │ │ ├── UIPanGestureRecognizer-RecordingAdditions.h
│ │ │ ├── UIPinchGestureRecognizer-RecordingAdditions.h
│ │ │ ├── UISwipeGestureRecognizer-RecordingAdditions.h
│ │ │ ├── UITapGestureRecognizer-RecordingAdditions.h
│ │ │ ├── XCAXClient_iOS.h
│ │ │ ├── XCActivityRecord.h
│ │ │ ├── XCApplicationMonitor.h
│ │ │ ├── XCApplicationMonitor_iOS.h
│ │ │ ├── XCApplicationQuery.h
│ │ │ ├── XCDebugLogDelegate-Protocol.h
│ │ │ ├── XCEventGenerator.h
│ │ │ ├── XCKeyMappingPath.h
│ │ │ ├── XCKeyboardInputSolver.h
│ │ │ ├── XCKeyboardKeyMap.h
│ │ │ ├── XCKeyboardLayout.h
│ │ │ ├── XCPointerEvent.h
│ │ │ ├── XCPointerEventPath.h
│ │ │ ├── XCSourceCodeRecording.h
│ │ │ ├── XCSourceCodeTreeNode.h
│ │ │ ├── XCSourceCodeTreeNodeEnumerator.h
│ │ │ ├── XCSymbolicationRecord.h
│ │ │ ├── XCSymbolicatorHolder.h
│ │ │ ├── XCSynthesizedEventRecord.h
│ │ │ ├── XCTAXClient-Protocol.h
│ │ │ ├── XCTAsyncActivity-Protocol.h
│ │ │ ├── XCTAsyncActivity.h
│ │ │ ├── XCTAutomationTarget-Protocol.h
│ │ │ ├── XCTDarwinNotificationExpectation.h
│ │ │ ├── XCTElementSetTransformer-Protocol.h
│ │ │ ├── XCTKVOExpectation.h
│ │ │ ├── XCTMetric.h
│ │ │ ├── XCTNSNotificationExpectation.h
│ │ │ ├── XCTNSPredicateExpectation.h
│ │ │ ├── XCTNSPredicateExpectationObject-Protocol.h
│ │ │ ├── XCTRunnerAutomationSession.h
│ │ │ ├── XCTRunnerDaemonSession.h
│ │ │ ├── XCTRunnerIDESession.h
│ │ │ ├── XCTTestRunSession.h
│ │ │ ├── XCTTestRunSessionDelegate-Protocol.h
│ │ │ ├── XCTUIApplicationMonitor-Protocol.h
│ │ │ ├── XCTWaiter.h
│ │ │ ├── XCTWaiterDelegate-Protocol.h
│ │ │ ├── XCTWaiterDelegatePrivate-Protocol.h
│ │ │ ├── XCTWaiterManagement-Protocol.h
│ │ │ ├── XCTWaiterManager.h
│ │ │ ├── XCTest.h
│ │ │ ├── XCTestCase.h
│ │ │ ├── XCTestCaseRun.h
│ │ │ ├── XCTestCaseSuite.h
│ │ │ ├── XCTestConfiguration.h
│ │ │ ├── XCTestContext.h
│ │ │ ├── XCTestContextScope.h
│ │ │ ├── XCTestDriver.h
│ │ │ ├── XCTestDriverInterface-Protocol.h
│ │ │ ├── XCTestExpectation.h
│ │ │ ├── XCTestExpectationDelegate-Protocol.h
│ │ │ ├── XCTestExpectationWaiter.h
│ │ │ ├── XCTestLog.h
│ │ │ ├── XCTestManager_IDEInterface-Protocol.h
│ │ │ ├── XCTestManager_ManagerInterface-Protocol.h
│ │ │ ├── XCTestManager_TestsInterface-Protocol.h
│ │ │ ├── XCTestMisuseObserver.h
│ │ │ ├── XCTestObservation-Protocol.h
│ │ │ ├── XCTestObservationCenter.h
│ │ │ ├── XCTestObserver.h
│ │ │ ├── XCTestProbe.h
│ │ │ ├── XCTestRun.h
│ │ │ ├── XCTestSuite.h
│ │ │ ├── XCTestSuiteRun.h
│ │ │ ├── XCTestWaiter.h
│ │ │ ├── XCUIApplication.h
│ │ │ ├── XCUIApplicationImpl.h
│ │ │ ├── XCUIApplicationProcess.h
│ │ │ ├── XCUICoordinate.h
│ │ │ ├── XCUIDevice.h
│ │ │ ├── XCUIElement.h
│ │ │ ├── XCUIElementAsynchronousHandlerWrapper.h
│ │ │ ├── XCUIElementHitPointCoordinate.h
│ │ │ ├── XCUIElementQuery.h
│ │ │ ├── XCUIHitPointResult.h
│ │ │ ├── XCUIRecorderNodeFinder.h
│ │ │ ├── XCUIRecorderNodeFinderMatch.h
│ │ │ ├── XCUIRecorderTimingMessage.h
│ │ │ ├── XCUIRecorderUtilities.h
│ │ │ ├── XCUIScreen.h
│ │ │ ├── XCUIScreenDataSource-Protocol.h
│ │ │ ├── _XCInternalTestRun.h
│ │ │ ├── _XCKVOExpectationImplementation.h
│ │ │ ├── _XCTDarwinNotificationExpectationImplementation.h
│ │ │ ├── _XCTNSNotificationExpectationImplementation.h
│ │ │ ├── _XCTNSPredicateExpectationImplementation.h
│ │ │ ├── _XCTWaiterImpl.h
│ │ │ ├── _XCTestCaseImplementation.h
│ │ │ ├── _XCTestCaseInterruptionException.h
│ │ │ ├── _XCTestExpectationImplementation.h
│ │ │ ├── _XCTestImplementation.h
│ │ │ ├── _XCTestObservationCenterImplementation.h
│ │ │ └── _XCTestSuiteImplementation.h
│ ├── Routes
│ │ ├── Extensions
│ │ │ ├── Logger.swift
│ │ │ ├── StringExtensions.swift
│ │ │ └── XCUIElement+Extensions.swift
│ │ ├── Handlers
│ │ │ ├── DeviceInfoHandler.swift
│ │ │ ├── EraseTextHandler.swift
│ │ │ ├── InputTextRouteHandler.swift
│ │ │ ├── KeyboardRouteHandler.swift
│ │ │ ├── LaunchAppHandler.swift
│ │ │ ├── PressButtonHandler.swift
│ │ │ ├── PressKeyHandler.swift
│ │ │ ├── RunningAppRouteHandler.swift
│ │ │ ├── ScreenDiffHandler.swift
│ │ │ ├── ScreenshotHandler.swift
│ │ │ ├── SetPermissionsHandler.swift
│ │ │ ├── StatusHandler.swift
│ │ │ ├── SwipeRouteHandler.swift
│ │ │ ├── SwipeRouteHandlerV2.swift
│ │ │ ├── TerminateAppHandler.swift
│ │ │ ├── TouchRouteHandler.swift
│ │ │ └── ViewHierarchyHandler.swift
│ │ ├── Helpers
│ │ │ ├── AppError.swift
│ │ │ ├── ScreenSizeHelper.swift
│ │ │ ├── SystemPermissionHelper.swift
│ │ │ ├── TextInputHelper.swift
│ │ │ └── TimeoutHelper.swift
│ │ ├── Models
│ │ │ ├── AXElement.swift
│ │ │ ├── DeviceInfoResponse.swift
│ │ │ ├── EraseTextRequest.swift
│ │ │ ├── GetRunningAppRequest.swift
│ │ │ ├── InputTextRequest.swift
│ │ │ ├── KeyboardHandlerRequest.swift
│ │ │ ├── KeyboardHandlerResponse.swift
│ │ │ ├── LaunchAppRequest.swift
│ │ │ ├── PressButtonRequest.swift
│ │ │ ├── PressKeyRequest.swift
│ │ │ ├── SetPermissionsRequest.swift
│ │ │ ├── StatusResponse.swift
│ │ │ ├── SwipeRequest.swift
│ │ │ ├── TerminateAppRequest.swift
│ │ │ ├── TouchRequest.swift
│ │ │ └── ViewHierarchyRequest.swift
│ │ ├── RouteHandlerFactory.swift
│ │ ├── XCTest
│ │ │ ├── AXClientSwizzler.swift
│ │ │ ├── EventRecord.swift
│ │ │ ├── EventTarget.swift
│ │ │ ├── KeyModifierFlags.swift
│ │ │ ├── PointerEventPath.swift
│ │ │ ├── RunnerDaemonProxy.swift
│ │ │ └── RunningApp.swift
│ │ └── XCTestHTTPServer.swift
│ ├── Utilities
│ │ ├── AXClientProxy.h
│ │ ├── AXClientProxy.m
│ │ ├── FBConfiguration.h
│ │ ├── FBConfiguration.m
│ │ ├── FBLogger.h
│ │ ├── FBLogger.m
│ │ ├── XCAccessibilityElement.h
│ │ ├── XCTestDaemonsProxy.h
│ │ └── XCTestDaemonsProxy.m
│ ├── ViewHierarchyHandleTests.swift
│ ├── maestro_driver_iosUITests.swift
│ └── maestro_driver_iosUITestsLaunchTests.swift
├── run-maestro-ios-runner.sh
└── test-maestro-ios-runner.sh
├── maestro-ios
├── README.md
├── build.gradle.kts
├── gradle.properties
└── src
│ └── main
│ └── java
│ └── ios
│ ├── IOSDeviceErrors.kt
│ ├── LocalIOSDevice.kt
│ ├── devicectl
│ └── DeviceControlIOSDevice.kt
│ └── xctest
│ └── XCTestIOSDevice.kt
├── maestro-orchestra-models
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── main
│ └── java
│ │ └── maestro
│ │ └── orchestra
│ │ ├── Commands.kt
│ │ ├── Condition.kt
│ │ ├── ElementSelector.kt
│ │ ├── ElementTrait.kt
│ │ ├── MaestroCommand.kt
│ │ ├── MaestroConfig.kt
│ │ ├── WorkspaceConfig.kt
│ │ └── util
│ │ ├── Env.kt
│ │ └── InputRandomTextHelper.kt
│ └── test
│ └── kotlin
│ ├── LitmusTest.kt
│ └── maestro
│ └── orchestra
│ └── util
│ └── EnvTest.kt
├── maestro-orchestra
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── main
│ └── java
│ │ └── maestro
│ │ └── orchestra
│ │ ├── Orchestra.kt
│ │ ├── error
│ │ ├── InvalidFlowFile.kt
│ │ ├── MediaFileNotFound.kt
│ │ ├── NoInputException.kt
│ │ ├── SyntaxError.kt
│ │ ├── UnicodeNotSupportedError.kt
│ │ └── ValidationError.kt
│ │ ├── filter
│ │ ├── FilterWithDescription.kt
│ │ ├── LaunchArguments.kt
│ │ └── TraitFilters.kt
│ │ ├── geo
│ │ └── Traveller.kt
│ │ ├── workspace
│ │ ├── ExecutionOrderPlanner.kt
│ │ ├── Filters.kt
│ │ ├── WorkspaceExecutionPlanner.kt
│ │ └── YamlCommandsPathValidator.kt
│ │ └── yaml
│ │ ├── MaestroFlowParser.kt
│ │ ├── YamlAction.kt
│ │ ├── YamlAddMedia.kt
│ │ ├── YamlAssertNoDefectsWithAI.kt
│ │ ├── YamlAssertTrue.kt
│ │ ├── YamlAssertWithAI.kt
│ │ ├── YamlClearState.kt
│ │ ├── YamlCommandReader.kt
│ │ ├── YamlCondition.kt
│ │ ├── YamlConfig.kt
│ │ ├── YamlElementSelector.kt
│ │ ├── YamlElementSelectorUnion.kt
│ │ ├── YamlEraseTextUnion.kt
│ │ ├── YamlEvalScript.kt
│ │ ├── YamlExtendedWaitUntil.kt
│ │ ├── YamlExtractTextWithAI.kt
│ │ ├── YamlFluentCommand.kt
│ │ ├── YamlInputRandomText.kt
│ │ ├── YamlInputText.kt
│ │ ├── YamlKillApp.kt
│ │ ├── YamlLaunchApp.kt
│ │ ├── YamlOnFlowComplete.kt
│ │ ├── YamlOnFlowStart.kt
│ │ ├── YamlOpenLink.kt
│ │ ├── YamlPressKey.kt
│ │ ├── YamlRepeatCommand.kt
│ │ ├── YamlRetry.kt
│ │ ├── YamlRunFlow.kt
│ │ ├── YamlRunScript.kt
│ │ ├── YamlScrollUntilVisible.kt
│ │ ├── YamlSetAirplaneMode.kt
│ │ ├── YamlSetLocation.kt
│ │ ├── YamlStartRecording.kt
│ │ ├── YamlStopApp.kt
│ │ ├── YamlSwipe.kt
│ │ ├── YamlTakeScreenshot.kt
│ │ ├── YamlToggleAirplaneMode.kt
│ │ ├── YamlTravelCommand.kt
│ │ └── YamlWaitForAnimationToEndCommand.kt
│ └── test
│ ├── java
│ └── maestro
│ │ └── orchestra
│ │ ├── CommandDescriptionTest.kt
│ │ ├── LaunchArgumentsTest.kt
│ │ ├── MaestroCommandSerializationTest.kt
│ │ ├── MaestroCommandTest.kt
│ │ ├── android
│ │ ├── AndroidMediaStoreTest.kt
│ │ └── DadbExt.kt
│ │ ├── workspace
│ │ ├── ExecutionOrderPlannerTest.kt
│ │ ├── WorkspaceExecutionPlannerErrorsTest.kt
│ │ └── WorkspaceExecutionPlannerTest.kt
│ │ └── yaml
│ │ ├── YamlCommandReaderTest.kt
│ │ └── junit
│ │ ├── YamlCommandsExtension.kt
│ │ ├── YamlFile.kt
│ │ └── YamlResourceFile.kt
│ └── resources
│ ├── YamlCommandReaderTest
│ ├── 002_launchApp.yaml
│ ├── 003_launchApp_withClearState.yaml
│ ├── 008_config_unknownKeys.yaml
│ ├── 017_launchApp_otherPackage.yaml
│ ├── 018_backPress_string.yaml
│ ├── 019_scroll_string.yaml
│ ├── 020_config_name.yaml
│ ├── 022_on_flow_start_complete.yaml
│ ├── 023_image.png
│ ├── 023_labels.yaml
│ ├── 023_runScript_test.js
│ ├── 024_string_non_string_commands.yaml
│ ├── 025_killApp.yaml
│ ├── 027_waitToSettleTimeoutMs.yaml
│ ├── 028_command_descriptions.yaml
│ └── flow.zip
│ ├── media
│ ├── android
│ │ ├── add_media_gif.yaml
│ │ ├── add_media_jpeg.yaml
│ │ ├── add_media_jpg.yaml
│ │ ├── add_media_mp4.yaml
│ │ ├── add_media_png.yaml
│ │ └── add_multiple_media.yaml
│ └── ios
│ │ ├── add_media_gif.yaml
│ │ ├── add_media_jpeg.yaml
│ │ ├── add_media_jpg.yaml
│ │ ├── add_media_mp4.yaml
│ │ ├── add_media_png.yaml
│ │ └── add_multiple_media.yaml
│ └── workspaces
│ ├── .gitignore
│ ├── 000_individual_file
│ └── flow.yaml
│ ├── 001_simple
│ ├── flowA.yaml
│ ├── flowB.yaml
│ └── notAFlow.txt
│ ├── 002_subflows
│ ├── flowA.yaml
│ ├── flowB.yaml
│ └── subflows
│ │ └── subflow.yaml
│ ├── 003_include_tags
│ ├── flowA.yaml
│ ├── flowB.yaml
│ └── flowC.yaml
│ ├── 004_exclude_tags
│ ├── flowA.yaml
│ ├── flowB.yaml
│ └── flowC.yaml
│ ├── 005_custom_include_pattern
│ ├── config.yaml
│ ├── featureA
│ │ └── flowA.yaml
│ ├── featureB
│ │ └── flowB.yaml
│ ├── featureC
│ │ └── flowC.yaml
│ └── flowD.yaml
│ ├── 006_include_subfolders
│ ├── config.yaml
│ ├── featureA
│ │ └── flowA.yaml
│ ├── featureB
│ │ └── flowB.yaml
│ ├── featureC
│ │ └── subfolder
│ │ │ └── flowC.yaml
│ └── flowD.yaml
│ ├── 007_empty_config
│ ├── config.yml
│ ├── flowA.yaml
│ └── flowB.yaml
│ ├── 008_literal_pattern
│ ├── config.yaml
│ ├── featureA
│ │ └── flowA.yaml
│ └── featureB
│ │ └── flowB.yaml
│ ├── 009_custom_config_fields
│ ├── config.yml
│ ├── flowA.yaml
│ └── flowB.yaml
│ ├── 010_global_include_tags
│ ├── config.yaml
│ ├── flowA.yaml
│ ├── flowA_subflow.yaml
│ ├── flowB.yaml
│ ├── flowC.yaml
│ ├── flowD.yaml
│ └── flowE.yaml
│ ├── 011_global_exclude_tags
│ ├── config.yaml
│ ├── flowA.yaml
│ ├── flowA_subflow.yaml
│ ├── flowB.yaml
│ ├── flowC.yaml
│ ├── flowD.yaml
│ └── flowE.yaml
│ ├── 012_local_deterministic_order
│ ├── config.yaml
│ ├── flowA.yaml
│ ├── flowB.yaml
│ └── flowC.yaml
│ ├── 013_execution_order
│ ├── config.yaml
│ ├── flowA.yaml
│ ├── flowB.yaml
│ ├── flowCWithCustomName.yaml
│ └── flowD.yaml
│ ├── 014_config_not_null
│ ├── config.yaml
│ ├── config
│ │ └── another_config.yaml
│ ├── flowA.yaml
│ └── flowB.yaml
│ ├── e000_flow_path_does_not_exist
│ └── error.txt
│ ├── e001_directory_does_not_contain_flow_files
│ ├── error.txt
│ └── workspace
│ │ └── dummy
│ ├── e002_top_level_directory_does_not_contain_flow_files
│ ├── error.txt
│ └── workspace
│ │ └── subdir
│ │ └── Flow.yaml
│ ├── e003_flow_inclusion_pattern_does_not_match_any_flow_files
│ ├── error.txt
│ └── workspace
│ │ ├── FlowC.yaml
│ │ └── config.yaml
│ ├── e004_tags_config_does_not_match_any_flow_files
│ ├── error.txt
│ ├── excludeTags.txt
│ ├── includeTags.txt
│ └── workspace
│ │ ├── ConfigExclude.yaml
│ │ ├── ParameterExclude.yaml
│ │ └── config.yaml
│ ├── e005_single_flow_does_not_exist
│ ├── error.txt
│ └── singleFlow.txt
│ ├── e006_single_flow_invalid_string_command
│ ├── error.txt
│ ├── singleFlow.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e007_single_flow_malformatted_command
│ ├── error.txt
│ ├── singleFlow.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e008_subflow_invalid_string_command
│ ├── error.txt
│ └── workspace
│ │ ├── Flow.yaml
│ │ └── subflow
│ │ └── SubFlow.yaml
│ ├── e009_nested_subflow_invalid_string_command
│ ├── error.txt
│ └── workspace
│ │ ├── Flow.yaml
│ │ └── subflow
│ │ ├── SubFlowA.yaml
│ │ └── SubFlowB.yaml
│ ├── e010_missing_config_section
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e011_missing_dashes
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e012_invalid_subflow_path
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e013_invalid_media_file
│ ├── error.txt
│ └── workspace
│ │ ├── Flow.yaml
│ │ └── assets
│ │ └── android.png
│ ├── e014_invalid_media_file_outside
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e015_array_command
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e016_config_invalid_command_in_onFlowStart
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e017_config_invalid_tags
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e018_config_missing_appId
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e019_invalid_swipe_direction
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e020_missing_command_options
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e021_multiple_command_names
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e022_top_level_option
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e023_empty
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ ├── e023_empty_commands
│ ├── error.txt
│ └── workspace
│ │ └── Flow.yaml
│ └── e023_launchApp_empty_string
│ ├── error.txt
│ └── workspace
│ └── Flow.yaml
├── maestro-proto
├── build.gradle.kts
├── gradle.properties
└── src
│ └── main
│ └── proto
│ └── maestro_android.proto
├── maestro-studio
├── server
│ ├── .gitignore
│ ├── build.gradle
│ └── src
│ │ └── main
│ │ └── java
│ │ └── maestro
│ │ └── studio
│ │ ├── AuthService.kt
│ │ ├── DeviceService.kt
│ │ ├── HttpException.kt
│ │ ├── InsightService.kt
│ │ ├── KtorUtils.kt
│ │ ├── MaestroStudio.kt
│ │ ├── MockService.kt
│ │ └── Models.kt
└── web
│ ├── .gitignore
│ ├── .npmrc
│ ├── .nvmrc
│ ├── .storybook
│ ├── main.js
│ └── preview.js
│ ├── build.gradle
│ ├── package-lock.json
│ ├── package.json
│ ├── postcss.config.js
│ ├── public
│ ├── favicon.ico
│ └── index.html
│ ├── src
│ ├── App.tsx
│ ├── api
│ │ ├── api.ts
│ │ └── mocks.ts
│ ├── components
│ │ ├── commands
│ │ │ ├── CommandCreator.tsx
│ │ │ ├── CommandInput.tsx
│ │ │ ├── CommandList.tsx
│ │ │ ├── CommandRow.tsx
│ │ │ ├── ReplHeader.tsx
│ │ │ ├── ReplView.tsx
│ │ │ └── SaveFlowModal.tsx
│ │ ├── common
│ │ │ ├── AuthModal.tsx
│ │ │ ├── Banner.tsx
│ │ │ ├── ChatGptApiKeyModal.tsx
│ │ │ ├── ConfirmationDialog.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Modal.tsx
│ │ │ ├── PageSwitcher.tsx
│ │ │ └── theme.tsx
│ │ ├── design-system
│ │ │ ├── button.tsx
│ │ │ ├── checkbox.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── dropdown-menu.tsx
│ │ │ ├── icon.tsx
│ │ │ ├── input.tsx
│ │ │ ├── keyboard-key.tsx
│ │ │ ├── link.tsx
│ │ │ ├── spinner.tsx
│ │ │ ├── tabs.tsx
│ │ │ └── utils
│ │ │ │ ├── functions.tsx
│ │ │ │ └── images.tsx
│ │ ├── device-and-device-elements
│ │ │ ├── ActionModal.tsx
│ │ │ ├── AnnotatedScreenshot.tsx
│ │ │ ├── BrowserActionBar.tsx
│ │ │ ├── DeviceWrapperAspectRatio.tsx
│ │ │ ├── ElementsPanel.tsx
│ │ │ ├── InteractableDevice.tsx
│ │ │ └── SelectedElementViewer.tsx
│ │ └── interact
│ │ │ └── InteractPageLayout.tsx
│ ├── context
│ │ ├── AuthContext.tsx
│ │ ├── DeviceContext.tsx
│ │ └── ReplContext.tsx
│ ├── helpers
│ │ ├── commandExample.ts
│ │ ├── models.ts
│ │ └── sampleElements.ts
│ ├── index.tsx
│ ├── pages
│ │ └── InteractPage.tsx
│ ├── react-app-env.d.ts
│ ├── setupTests.ts
│ ├── storybook
│ │ ├── ActionModal.stories.tsx
│ │ ├── App.stories.tsx
│ │ ├── BrowserActionBar.stories.tsx
│ │ ├── ConfirmationDialog.stories.tsx
│ │ ├── Header.stories.tsx
│ │ ├── InteractPage.stories.tsx
│ │ ├── PageSwitcher.stories.tsx
│ │ ├── ReplView.stories.tsx
│ │ └── SaveFlowModal.stories.tsx
│ └── style
│ │ ├── fonts
│ │ ├── JetBrainsMono-Italic.ttf
│ │ └── JetBrainsMono.ttf
│ │ └── index.css
│ ├── storybook-assets
│ ├── mockServiceWorker.js
│ └── sample-screenshot.png
│ ├── tailwind.config.js
│ └── tsconfig.json
├── maestro-test
├── build.gradle.kts
└── src
│ ├── main
│ └── kotlin
│ │ └── maestro
│ │ └── test
│ │ └── drivers
│ │ ├── FakeDriver.kt
│ │ ├── FakeLayoutElement.kt
│ │ └── FakeTimer.kt
│ └── test
│ ├── kotlin
│ └── maestro
│ │ └── test
│ │ ├── GraalJsEngineTest.kt
│ │ ├── IntegrationTest.kt
│ │ ├── JsEngineTest.kt
│ │ └── RhinoJsEngineTest.kt
│ └── resources
│ ├── 001_assert_visible_by_id.yaml
│ ├── 002_assert_visible_by_text.yaml
│ ├── 003_assert_visible_by_size.yaml
│ ├── 004_assert_no_visible_element_with_id.yaml
│ ├── 005_assert_no_visible_element_with_text.yaml
│ ├── 006_assert_no_visible_element_with_size.yaml
│ ├── 007_assert_visible_by_size_with_tolerance.yaml
│ ├── 008_tap_on_element.yaml
│ ├── 009_skip_optional_elements.yaml
│ ├── 010_scroll.yaml
│ ├── 011_back_press.yaml
│ ├── 012_input_text.yaml
│ ├── 013_launch_app.yaml
│ ├── 014_tap_on_point.yaml
│ ├── 015_element_relative_position.yaml
│ ├── 016_multiline_text.yaml
│ ├── 017_swipe.yaml
│ ├── 018_contains_child.yaml
│ ├── 019_dont_wait_for_visibility.yaml
│ ├── 020_parse_config.yaml
│ ├── 021_launch_app_with_clear_state.yaml
│ ├── 022_launch_app_that_is_not_installed.yaml
│ ├── 025_element_relative_position_shortcut.yaml
│ ├── 026_assert_not_visible.yaml
│ ├── 027_open_link.yaml
│ ├── 028_env.yaml
│ ├── 029_long_press_on_element.yaml
│ ├── 030_long_press_on_point.yaml
│ ├── 031_traits.yaml
│ ├── 032_element_index.yaml
│ ├── 033_int_text.yaml
│ ├── 034_press_key.yaml
│ ├── 035_refresh_position_ignore_duplicates.yaml
│ ├── 036_erase_text.yaml
│ ├── 037_unicode_input.yaml
│ ├── 038_partial_id.yaml
│ ├── 039_hide_keyboard.yaml
│ ├── 040_escape_regex.yaml
│ ├── 041_take_screenshot.yaml
│ ├── 042_extended_wait.yaml
│ ├── 043_stop_app.yaml
│ ├── 044_clear_state.yaml
│ ├── 045_clear_keychain.yaml
│ ├── 046_run_flow.yaml
│ ├── 047_run_flow_nested.yaml
│ ├── 048_tapOn_clickable.yaml
│ ├── 049_run_flow_conditionally.yaml
│ ├── 051_set_location.yaml
│ ├── 052_text_random.yaml
│ ├── 053_repeat_times.yaml
│ ├── 054_enabled.yaml
│ ├── 055_compare_regex.yaml
│ ├── 056_ignore_error.yaml
│ ├── 057_runFlow_env.yaml
│ ├── 057_subflow.yaml
│ ├── 057_subflow_override.yaml
│ ├── 058_inline_env.yaml
│ ├── 058_subflow.yaml
│ ├── 059_directional_swipe_command.yaml
│ ├── 060_pass_env_to_env.yaml
│ ├── 060_subflow.yaml
│ ├── 061_launchApp_withoutStopping.yaml
│ ├── 062_copy_paste_text.yaml
│ ├── 063_js_injection.yaml
│ ├── 064_js_files.yaml
│ ├── 064_script.js
│ ├── 064_script_alt.js
│ ├── 064_script_with_args.js
│ ├── 064_subflow.yaml
│ ├── 065_subflow.yaml
│ ├── 065_when_true.yaml
│ ├── 066_copyText_jsVar.yaml
│ ├── 067_assertTrue_fail.yaml
│ ├── 067_assertTrue_pass.yaml
│ ├── 068_erase_all_text.yaml
│ ├── 069_wait_for_animation_to_end.yaml
│ ├── 070_evalScript.yaml
│ ├── 071_tapOnRelativePoint.yaml
│ ├── 072_searchDepthFirst.yaml
│ ├── 073_handle_linebreaks.yaml
│ ├── 074_directional_swipe_element.yaml
│ ├── 075_repeat_while.yaml
│ ├── 076_optional_assertion.yaml
│ ├── 077_env_special_characters.yaml
│ ├── 078_swipe_relative.yaml
│ ├── 079_scroll_until_visible.yaml
│ ├── 080_hierarchy_pruning_assert_visible.yaml
│ ├── 081_hierarchy_pruning_assert_not_visible.yaml
│ ├── 082_repeat_while_true.yaml
│ ├── 083_assert_properties.yaml
│ ├── 084_open_browser.yaml
│ ├── 085_open_link_auto_verify.yaml
│ ├── 086_launchApp_sets_all_permissions_to_allow.yaml
│ ├── 087_launchApp_with_all_permissions_to_deny.yaml
│ ├── 088_launchApp_with_all_permissions_to_deny_and_notification_to_allow.yaml
│ ├── 089_launchApp_with_sms_permission_group_to_allow.yaml
│ ├── 090_travel.yaml
│ ├── 091_assert_visible_by_index.yaml
│ ├── 092_log_messages.yaml
│ ├── 092_script.js
│ ├── 093_js_default_value.yaml
│ ├── 094_runFlow_inline.yaml
│ ├── 095_launch_arguments.yaml
│ ├── 096_platform_condition.yaml
│ ├── 097_contains_descendants.yaml
│ ├── 098_runScript.js
│ ├── 098_runscript_conditionals.yaml
│ ├── 099_screen_recording.yaml
│ ├── 100_tapOn_multiple_times.yaml
│ ├── 101_doubleTapOn.yaml
│ ├── 102_graaljs.yaml
│ ├── 102_graaljs_subflow.yaml
│ ├── 103_on_flow_start_complete_hooks.yaml
│ ├── 103_setup.js
│ ├── 103_teardown.js
│ ├── 104_on_flow_start_complete_hooks_flow_failed.yaml
│ ├── 105_on_flow_start_complete_when_js_output_set.yaml
│ ├── 105_setup.js
│ ├── 105_teardown.js
│ ├── 106_on_flow_start_complete_when_js_output_set_subflows.yaml
│ ├── 106_setup.js
│ ├── 106_subflow.yaml
│ ├── 106_teardown.js
│ ├── 107_define_variables_command_before_hooks.yaml
│ ├── 108_failed_start_hook.yaml
│ ├── 109_failed_complete_hook.yaml
│ ├── 110_add_media_device.yaml
│ ├── 111_add_multiple_media.yaml
│ ├── 112_scroll_until_visible_center.yaml
│ ├── 113_tap_on_element_settle_timeout.yaml
│ ├── 114_child_of_selector.yaml
│ ├── 115_airplane_mode.yaml
│ ├── 116_kill_app.yaml
│ ├── 117_scroll_until_visible_speed.js
│ ├── 117_scroll_until_visible_speed.yaml
│ ├── 118_scroll_until_visible_negative.yaml
│ ├── 119_retry_commands.yaml
│ ├── 120_tap_on_element_retryTapIfNoChange.yaml
│ └── media
│ └── abc.png
├── maestro-utils
├── build.gradle.kts
├── gradle.properties
└── src
│ ├── main
│ └── kotlin
│ │ ├── Collections.kt
│ │ ├── DepthTracker.kt
│ │ ├── HttpClient.kt
│ │ ├── Insight.kt
│ │ ├── Insights.kt
│ │ ├── MaestroTimer.kt
│ │ ├── Metrics.kt
│ │ ├── SocketUtils.kt
│ │ ├── Strings.kt
│ │ └── network
│ │ └── Errors.kt
│ └── test
│ └── kotlin
│ ├── CollectionsTest.kt
│ ├── DepthTrackerTest.kt
│ ├── InsightTest.kt
│ ├── MaestroTimerTest.kt
│ ├── SocketUtilsTest.kt
│ ├── StringsTest.kt
│ └── network
│ └── ErrorsTest.kt
├── maestro-web
├── build.gradle.kts
├── gradle.properties
└── src
│ └── main
│ └── kotlin
│ └── maestro
│ └── web
│ ├── record
│ ├── JcodecVideoEncoder.kt
│ ├── VideoEncoder.kt
│ └── WebScreenRecorder.kt
│ └── selenium
│ ├── ChromeSeleniumFactory.kt
│ └── SeleniumFactory.kt
├── recipes
├── googleplay
│ └── install_twitter.yaml
├── nowinandroid
│ └── pick_interests.yaml
├── square
│ └── pos
│ │ └── android
│ │ └── signup.yaml
├── twitter
│ └── android
│ │ ├── create_account.yaml
│ │ └── follow.yaml
└── web
│ └── xmas.yaml
├── scripts
└── install.sh
├── settings.gradle.kts
└── tmp.sh
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Copied from https://youtrack.jetbrains.com/issue/FL-15599/No-way-of-disabling-Java-Kotlin-wildcard-imports
2 |
3 | [*.java]
4 | ij_java_class_count_to_use_import_on_demand = 1024
5 | ij_java_names_count_to_use_import_on_demand = 1024
6 |
7 | [*.kt]
8 | ij_kotlin_name_count_to_use_star_import = 1024
9 | ij_kotlin_name_count_to_use_star_import_for_members = 1024
10 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | #
2 | # https://help.github.com/articles/dealing-with-line-endings/
3 | #
4 | # These are explicitly windows files and should use crlf
5 | *.bat text eol=crlf
6 |
7 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Proposed changes
2 |
3 | copilot:summary
4 |
5 | ## Testing
6 |
7 |
8 |
9 | ## Issues fixed
10 |
--------------------------------------------------------------------------------
/.github/workflows/warn_build_xctestrunner.yaml:
--------------------------------------------------------------------------------
1 | name: Warn Build XCTest runner
2 |
3 | on:
4 | pull_request:
5 | paths:
6 | - 'maestro-ios-xctest-runner/**'
7 |
8 | jobs:
9 | warn:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: mshick/add-pr-comment@v2
13 | with:
14 | message: "Make sure to run ./maestro-ios-xctest-runner/build-maestro-ios-runner.sh with every swift change"
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Ignore Gradle project-specific cache directory
4 | .gradle
5 |
6 | # Ignore Gradle build output directory
7 | build
8 |
9 | # Ignore Gradle local properties
10 | local.properties
11 |
12 | bin
13 |
14 | # media assets
15 | maestro-orchestra/src/test/resources/media/assets/*
16 |
17 | # Local files
18 | local/
--------------------------------------------------------------------------------
/.run/cli-version.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.run/cli.run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/assets/add_contact_android.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/assets/add_contact_android.gif
--------------------------------------------------------------------------------
/assets/edit_contacts_ios.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/assets/edit_contacts_ios.gif
--------------------------------------------------------------------------------
/assets/run-on-robin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/assets/run-on-robin.png
--------------------------------------------------------------------------------
/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/debug.keystore
--------------------------------------------------------------------------------
/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | apps/
2 |
3 | samples/
4 | samples.zip
5 |
--------------------------------------------------------------------------------
/e2e/manifest.txt:
--------------------------------------------------------------------------------
1 | https://storage.googleapis.com/mobile.dev/cli_e2e/wikipedia.apk
2 | https://storage.googleapis.com/mobile.dev/cli_e2e/wikipedia.zip
3 | https://storage.googleapis.com/mobile.dev/cli_e2e/nowinandroid.apk
4 | https://storage.googleapis.com/mobile.dev/cli_e2e/demo_app.apk
5 | https://storage.googleapis.com/mobile.dev/cli_e2e/demo_app.zip
6 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/ai_complex.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | - ai
5 | ---
6 | - launchApp:
7 | clearState: true
8 | - tapOn: Defects Test
9 | - assertNoDefectsWithAI:
10 | optional: true
11 | - assertWithAI: A picture of a cute bunny is visible
12 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/ai_simple.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | - ai
5 | ---
6 | - launchApp:
7 | clearState: true
8 | - assertWithAI:
9 | optional: true
10 | assertion: A login screen is visible
11 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/assertNotVisible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | - launchApp # For idempotence of sections
5 |
6 | - assertNotVisible: 'kwyjibo'
7 |
8 | - assertNotVisible:
9 | text: 'kwyjibo'
10 |
11 | - assertNotVisible:
12 | text: 'Form Test'
13 | enabled: false
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/assertTrue.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | - launchApp # For idempotence of sections
5 |
6 | - assertTrue: ${"test" == "test"}
7 |
8 | - assertTrue:
9 | condition: ${12 < 20}
10 |
11 | - assertTrue:
12 | condition: ${THING == "five"} # Using the env at the top of the file
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/assertVisible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | - launchApp # For idempotence of sections
5 |
6 | - assertVisible: 'Form Test'
7 |
8 | - assertVisible:
9 | text: 'Form Test'
10 |
11 | - assertVisible:
12 | id: 'fabAddIcon'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/back.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Form Test'
6 | - assertVisible: 'Login'
7 | - back
8 | - assertVisible: 'Form Test'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/copyTextFrom.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn:
6 | id: 'fabAddIcon'
7 | retryTapIfNoChange: false
8 | - copyTextFrom:
9 | text: '\d+'
10 | - assertTrue: ${maestro.copiedText == '1'}
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/eraseText.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Form Test'
6 | - tapOn: 'Email'
7 | - inputText: 'foo'
8 | - assertVisible: 'foo'
9 | - eraseText
10 | # Fix me this part is flaky on CI only not local, needs to be addressed why
11 | - assertNotVisible:
12 | text: 'foo'
13 | optional: true
14 |
15 | - inputText: 'testing'
16 | - assertVisible: 'testing'
17 | - eraseText: 3
18 | - assertNotVisible: 'testing'
19 | - assertVisible:
20 | text: 'test'
21 | optional: true # FIXME: This still takes an extra character sometimes, even after #2123
22 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/evalScript.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | - launchApp # For idempotence of sections
5 |
6 | - evalScript: ${output.test = 'foo'}
7 | - assertTrue: ${output.test == 'foo'}
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/extendedWaitUntil.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - extendedWaitUntil:
6 | timeout: 10000
7 | visible:
8 | text: 'Swipe Test'
9 |
10 | - extendedWaitUntil:
11 | timeout: 100
12 | notVisible:
13 | text: 'Non Existent Text'
14 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/hideKeyboard.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Form Test'
6 | - tapOn: 'Email'
7 | - assertVisible:
8 | id: com.google.android.inputmethod.latin:id/key_pos_shift # The shift key on the Android keyboard
9 | - hideKeyboard
10 | - assertNotVisible:
11 | id: com.google.android.inputmethod.latin:id/key_pos_shift
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/inputRandomEmail.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Input Test'
6 | - tapOn:
7 | id: 'textInput'
8 | - inputRandomEmail
9 | - assertVisible: '.+@.+'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/inputRandomNumber.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Input Test'
6 | - tapOn:
7 | id: 'textInput'
8 | - inputRandomNumber
9 | - assertVisible:
10 | text: '\d{8}' # The default length is 8
11 | id: 'textInput'
12 |
13 | - eraseText
14 | - inputRandomNumber:
15 | length: 4
16 | - assertVisible:
17 | text: '\d{4}'
18 | id: 'textInput'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/inputRandomPersonName.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Input Test'
6 | - tapOn:
7 | id: 'textInput'
8 | - inputRandomPersonName
9 | - assertVisible:
10 | text: '[A-Z][a-z]+ [A-Z][a-z]+'
11 | id: 'textInput'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/inputRandomText.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Input Test'
6 | - tapOn:
7 | id: 'textInput'
8 | - inputRandomText
9 | - assertVisible:
10 | text: '[a-z0-9]{8}' # The default length is 8
11 | id: 'textInput'
12 |
13 | - eraseText
14 | - inputRandomText:
15 | length: 4
16 | - assertVisible:
17 | text: '[a-z0-9]{4}'
18 | id: 'textInput'
19 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/inputText.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - tapOn: 'Input Test'
6 | - tapOn:
7 | id: 'textInput'
8 | - inputText: 'foo'
9 | - assertVisible: 'foo'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/killApp.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | - launchApp
5 | - assertVisible: 'Form Test'
6 | - killApp
7 | - assertNotVisible: 'Form Test'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/launchApp.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp:
4 | appId: com.example.example
5 | clearState: true
6 | clearKeychain: true
7 | stopApp: true
8 | permissions:
9 | all: allow
10 | - assertVisible: 'Form Test'
11 |
12 | - stopApp
13 | - launchApp
14 | - assertVisible: 'Form Test'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/pasteText.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - evalScript: ${maestro.copiedText = 'foo'}
6 | - tapOn: 'Input Test'
7 | - tapOn:
8 | id: 'textInput'
9 | - inputText: 'foo'
10 | - copyTextFrom:
11 | id: 'textInput'
12 | - pasteText
13 | - assertVisible: 'foofoo'
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/pressKey.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp
4 |
5 | - assertVisible: 'Form Test'
6 | - pressKey: 'Home'
7 | - assertNotVisible: 'Form Test'
8 |
9 | - launchApp
10 |
11 | - assertVisible: 'Form Test'
12 | - tapOn: 'Form Test'
13 | - assertNotVisible: 'Form Test'
14 | - pressKey: 'Back'
15 | - assertVisible: 'Form Test'
16 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/repeat.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp # For idempotence of sections
4 |
5 | - assertVisible: '0'
6 | - repeat:
7 | times: 3
8 | commands:
9 | - tapOn:
10 | id: 'fabAddIcon'
11 | - assertVisible: '3'
12 | - assertNotVisible: '0'
13 |
14 | - evalScript: ${output.counter = 0}
15 | - repeat:
16 | while:
17 | true: ${output.counter < 3}
18 | commands:
19 | - evalScript: ${output.counter = output.counter + 1}
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/retry.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 | - launchApp
4 |
5 | - retry:
6 | maxRetries: 3
7 | commands:
8 | - tapOn:
9 | id: 'fabAddIcon'
10 | retryTapIfNoChange: false
11 | - waitForAnimationToEnd
12 | - assertVisible: '2'
13 | - assertVisible: 'Flutter Demo Home Page'
14 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/runFlow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | # runFlow with file: isn't included since it's in the root flow
5 |
6 | - launchApp # For idempotence of sections
7 |
8 | - runFlow:
9 | commands:
10 | - evalScript: ${output.test = 'bar'}
11 | - assertTrue: ${output.test == 'bar'}
12 | - tapOn:
13 | id: 'fabAddIcon'
14 | - assertVisible: '1'
15 |
16 | - runFlow:
17 | env:
18 | THIS_THING: "six"
19 | commands:
20 | - assertTrue: ${THIS_THING == "six"}
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/runScript.js:
--------------------------------------------------------------------------------
1 | if (THIS_THING == "six"){
2 | output.something = "foo"
3 | } else {
4 | output.something = "bar"
5 | }
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/commands/runScript.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | ---
3 |
4 | - launchApp # For idempotence of sections
5 |
6 | - evalScript: ${output.something = 'baz'}
7 |
8 | - runScript: runScript.js
9 | - assertTrue: ${output.something == 'bar'}
10 |
11 | - evalScript: ${output.something = 'baz'}
12 |
13 | - runScript:
14 | file: runScript.js
15 | - assertTrue: ${output.something == 'bar'}
16 |
17 | - evalScript: ${output.something = 'baz'}
18 |
19 | - runScript:
20 | env:
21 | THIS_THING: "six"
22 | file: runScript.js
23 | - assertTrue: ${output.something == 'foo'}
24 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fail_fast.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - assertTrue:
8 | condition: ${ false }
9 | label: Fail the flow
10 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fail_launchApp.yaml:
--------------------------------------------------------------------------------
1 | appId: com.nonexistent
2 | tags:
3 | - failing
4 | ---
5 | - launchApp
6 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fail_launchApp_nonDefault.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | ---
5 | - launchApp:
6 | appId: com.nonexistent
7 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fail_not_found.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - tapOn:
8 | id: non-existent-id
9 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fail_visible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - assertVisible:
8 | id: non-existent-id
9 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fail_visible_extended.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - failing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - extendedWaitUntil:
8 | visible:
9 | id: non-existent-id
10 | timeout: 100
11 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/fill_form.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - passing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - tapOn: Form Test
8 | - tapOn: Email
9 | - inputText: correct@mobile.dev
10 | - tapOn: Password
11 | - inputText: maestro
12 | - tapOn:
13 | text: Login
14 | index: 1
15 | - assertVisible:
16 | text: Credentials are correct
17 | optional: true # Fix me this part is flaky on CI only not local, needs to be addressed why
18 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/long_input_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - passing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - tapOn: Form Test
8 | - tapOn: Email
9 | - inputText: veryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryveryverylongemail@mobile.dev
10 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/relatives.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - passing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - tapOn: Nesting Test
8 | - assertVisible:
9 | id: level-0
10 | - assertVisible:
11 | id: level-0
12 | containsChild:
13 | id: level-1
14 | containsChild:
15 | id: level-2
16 | rightOf:
17 | text: left side
18 | leftOf:
19 | text: right side
20 | below: top side
21 | above: bottom side
22 | - assertNotVisible:
23 | id: level-0
24 | containsChild:
25 | id: level-1
26 | below: bottom side
27 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/scrollUntilVisible_timeout.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - passing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - evalScript: ${maestro.startTime = new Date()}
8 | - scrollUntilVisible:
9 | element: non-existent
10 | timeout: 1000
11 | optional: true
12 | - evalScript: ${maestro.endTime = new Date()}
13 | - assertTrue: ${maestro.endTime - maestro.startTime < 12000} # Far less than the 20000 default, but enough to allow for processing time
14 |
--------------------------------------------------------------------------------
/e2e/workspaces/demo_app/swipe.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.example
2 | tags:
3 | - passing
4 | ---
5 | - launchApp:
6 | clearState: true
7 | - tapOn: Swipe Test
8 | - swipe:
9 | start: 50%, 15%
10 | end: 15%, 50%
11 | duration: 1000
12 | - swipe:
13 | start: 15%, 50%
14 | end: 85%, 85%
15 | duration: 1000
16 | - swipe:
17 | start: 85%, 85%
18 | end: 85%, 50%
19 | duration: 1000
20 | - tapOn:
21 | point: 85%, 50%
22 | - assertVisible: All green
23 |
--------------------------------------------------------------------------------
/e2e/workspaces/no-app/README.md:
--------------------------------------------------------------------------------
1 | # No App
2 |
3 | For tests that don't require an app to be launched.
4 |
5 | Tests of JavaScript or environment variables can be run here.
--------------------------------------------------------------------------------
/e2e/workspaces/no-app/environment-variables.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.notused
2 | tags:
3 | - passing
4 | ---
5 | # Relies on MAESTRO_EXAMPLE being set in the environment
6 | - assertTrue: ${MAESTRO_EXAMPLE == 'test-value'}
--------------------------------------------------------------------------------
/e2e/workspaces/nowinandroid/bookmarks.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.samples.apps.nowinandroid.demo.debug
2 | name: Bookmarks
3 | tags:
4 | - android
5 | - passing
6 | ---
7 | - launchApp:
8 | clearState: true
9 | - tapOn: Headlines
10 | - tapOn: Done
11 | - tapOn: Bookmark
12 | - tapOn: Saved
13 | - tapOn: Unbookmark
14 | - assertVisible: No saved updates
15 |
--------------------------------------------------------------------------------
/e2e/workspaces/nowinandroid/fail.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.samples.apps.nowinandroid.demo.debug
2 | name: Fail
3 | tags:
4 | - android
5 | - failing
6 | ---
7 | - launchApp:
8 | clearState: true
9 | - tapOn:
10 | id: non-existent-id-to-fail-this-test
11 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/android-advanced-flow.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | tags:
3 | - android
4 | - passing
5 | - advanced
6 | ---
7 | - runFlow: subflows/onboarding-android.yaml
8 | - tapOn:
9 | id: "org.wikipedia:id/search_container"
10 | - tapOn:
11 | text: "Non existent view"
12 | optional: true
13 | - runScript: scripts/getSearchQuery.js
14 | - inputText: ${output.result}
15 | - assertVisible: ${output.result}
16 | - runFlow: subflows/launch-clearstate-android.yaml
17 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/android-flow.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | tags:
3 | - android
4 | - passing
5 | ---
6 | - launchApp
7 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/ios-flow.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikimedia.wikipedia
2 | tags:
3 | - ios
4 | - passing
5 | ---
6 | - launchApp
7 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/scripts/getSearchQuery.js:
--------------------------------------------------------------------------------
1 | output.result = 'qwerty';
2 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/subflows/launch-clearstate-android.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - launchApp:
4 | clearState: true
5 | - assertVisible: "Continue"
6 | - assertVisible: "Skip"
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/subflows/launch-clearstate-ios.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikimedia.wikipedia
2 | ---
3 | - launchApp:
4 | clearState: true
5 | - assertVisible: "Next"
6 | - assertVisible: "Skip"
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/subflows/onboarding-android.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - launchApp:
4 | clearState: true
5 | - tapOn:
6 | text: "Non existent view"
7 | optional: true
8 | - tapOn:
9 | id: "org.wikipedia:id/fragment_onboarding_forward_button"
10 | - tapOn:
11 | id: "org.wikipedia:id/fragment_onboarding_forward_button"
12 | - tapOn:
13 | id: "org.wikipedia:id/fragment_onboarding_forward_button"
14 | - tapOn:
15 | id: "org.wikipedia:id/fragment_onboarding_done_button"
16 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/subflows/onboarding-ios.yaml:
--------------------------------------------------------------------------------
1 | appId: org.wikimedia.wikipedia
2 | ---
3 | - launchApp:
4 | clearState: true
5 | - repeat:
6 | times: 3
7 | commands:
8 | - swipe:
9 | direction: LEFT
10 | duration: 400
11 | - waitForAnimationToEnd
12 | - tapOn: Get started
13 | - tapOn:
14 | text: "Non existent view"
15 | optional: true
16 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/auth/login.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "More"
4 | - tapOn: "LOG IN.*"
5 | - tapOn:
6 | id: ".*create_account_login_button"
7 | - runScript: "../scripts/fetchTestUser.js"
8 | - tapOn: "Username"
9 | - inputText: "${output.test_user.username}"
10 | - tapOn: "Password"
11 | - inputText: "No provided"
12 | - tapOn: "LOG IN"
13 | - back
14 | - back
15 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/auth/signup.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "More"
4 | - tapOn: "LOG IN.*"
5 | - runScript: "../scripts/generateCredentials.js"
6 | - tapOn: "Username"
7 | - inputText: "${output.credentials.username}"
8 | - tapOn: "Password"
9 | - inputText: "${output.credentials.password}"
10 | - tapOn: "Repeat password"
11 | - inputText: "${output.credentials.password}"
12 | - tapOn: "Email.*"
13 | - inputText: "${output.credentials.email}"
14 |
15 | # We won't actually create the account
16 | - back
17 | - back
18 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/dashboard/copy-paste.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "Explore"
4 | - scrollUntilVisible:
5 | element: "Top read"
6 | - copyTextFrom:
7 | id: ".*view_list_card_item_title"
8 | index: 0
9 | - tapOn: "Explore"
10 | - tapOn: "Search Wikipedia"
11 | - inputText: "${maestro.copiedText}"
12 | - back
13 | - back
14 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/dashboard/feed.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "Explore"
4 | - scrollUntilVisible:
5 | element: "Today on Wikipedia.*"
6 | - tapOn: "Today on Wikipedia.*"
7 | - back
8 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/dashboard/main.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - runFlow: "search.yml"
4 | - runFlow: "saved.yml"
5 | - runFlow: "feed.yml"
6 | - runFlow: "copy-paste.yml"
7 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/dashboard/saved.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "Saved"
4 | - tapOn: "Default list for your saved articles"
5 | - assertVisible: "Sun"
6 | - assertVisible: "Star at the center of the Solar System"
7 | - back
8 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/dashboard/search.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "Search Wikipedia"
4 | - inputText: "Sun"
5 | - assertVisible: "Star at the center of the Solar System"
6 | - tapOn:
7 | id: ".*page_list_item_title"
8 | - tapOn:
9 | id: ".*page_save"
10 | - back
11 | - back
12 | - back
13 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/onboarding/add-language.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "ADD OR EDIT.*"
4 | - tapOn: "ADD LANGUAGE"
5 | - tapOn:
6 | id: ".*menu_search_language"
7 | - inputText: "Greek"
8 | - assertVisible: "Ελληνικά"
9 | - tapOn: "Ελληνικά"
10 | - tapOn: "Navigate up"
11 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/onboarding/main.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - runFlow: "add-language.yml"
4 | - runFlow: "remove-language.yml"
5 | - tapOn: "Continue"
6 | - assertVisible: "New ways to explore"
7 | - tapOn: "Continue"
8 | - assertVisible: "Reading lists with sync"
9 | - tapOn: "Continue"
10 | - assertVisible: "Send anonymous data"
11 | - tapOn: "Get started"
12 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/onboarding/remove-language.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | ---
3 | - tapOn: "ADD OR EDIT.*"
4 | - tapOn: "More options"
5 | - tapOn: "Remove language"
6 | - tapOn:
7 | id: ".*wiki_language_checkbox"
8 | index: 1
9 | - tapOn:
10 | id: ".*menu_delete_selected"
11 | - tapOn: "OK"
12 | - assertNotVisible: "Ελληνικά"
13 | - tapOn: "Navigate up"
14 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/run-test.yml:
--------------------------------------------------------------------------------
1 | appId: org.wikipedia
2 | tags:
3 | - android
4 | - passing
5 | ---
6 | - launchApp:
7 | clearState: true
8 | - runFlow: "onboarding/main.yml"
9 | - runFlow: "dashboard/main.yml"
10 | - runFlow: "auth/signup.yml"
11 | - runFlow: "auth/login.yml"
12 |
--------------------------------------------------------------------------------
/e2e/workspaces/wikipedia/wikipedia-android-advanced/scripts/fetchTestUser.js:
--------------------------------------------------------------------------------
1 | // Fetches test user from API
2 | function getTestUserFromApi() {
3 | const url = `https://jsonplaceholder.typicode.com/users/1`;
4 | var response = http.get(url);
5 | var data = json(response.body);
6 |
7 | return {
8 | username: data.username,
9 | email: data.email,
10 | };
11 | }
12 |
13 | output.test_user = getTestUserFromApi();
14 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionSha256Sum=bed1da33cca0f557ab13691c77f38bb67388119e4794d113e051039b80af9bb1
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip
5 | networkTimeout=10000
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/installLocally.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | ./gradlew :maestro-cli:installDist
4 |
5 | rm -rf ~/.maestro/bin
6 | rm -rf ~/.maestro/lib
7 |
8 | cp -r ./maestro-cli/build/install/maestro/bin ~/.maestro/bin
9 | cp -r ./maestro-cli/build/install/maestro/lib ~/.maestro/lib
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/logo.png
--------------------------------------------------------------------------------
/maestro:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | if [ -t 0 ]; then
6 | input=""
7 | else
8 | input=$(cat -)
9 | fi
10 |
11 | ./gradlew :maestro-cli:installDist -q && ./maestro-cli/build/install/maestro/bin/maestro "$@"
12 |
--------------------------------------------------------------------------------
/maestro-ai/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro AI
2 | POM_ARTIFACT_ID=maestro-ai
3 | POM_PACKAGING=jar
4 |
--------------------------------------------------------------------------------
/maestro-ai/src/main/java/maestro/ai/IAPredictionEngine.kt:
--------------------------------------------------------------------------------
1 | package maestro.ai
2 |
3 | import maestro.ai.cloud.Defect
4 |
5 | interface AIPredictionEngine {
6 | suspend fun findDefects(screen: ByteArray): List
7 | suspend fun performAssertion(screen: ByteArray, assertion: String): Defect?
8 | suspend fun extractText(screen: ByteArray, query: String): String
9 | }
10 |
--------------------------------------------------------------------------------
/maestro-ai/src/main/java/maestro/ai/anthropic/Common.kt:
--------------------------------------------------------------------------------
1 | package maestro.ai.anthropic
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Message(
8 | val role: String,
9 | val content: List,
10 | )
11 |
12 | @Serializable
13 | data class Content(
14 | val type: String,
15 | val text: String? = null,
16 | val source: ContentSource? = null,
17 | )
18 |
19 | @Serializable
20 | data class ContentSource(
21 | val type: String,
22 | @SerialName("media_type") val mediaType: String,
23 | val data: String,
24 | )
25 |
--------------------------------------------------------------------------------
/maestro-ai/src/main/java/maestro/ai/anthropic/Request.kt:
--------------------------------------------------------------------------------
1 | package maestro.ai.anthropic
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | @Serializable
7 | data class Request(
8 | val model: String,
9 | @SerialName("max_tokens") val maxTokens: Int,
10 | val messages: List,
11 | )
12 |
--------------------------------------------------------------------------------
/maestro-ai/src/main/java/maestro/ai/anthropic/Response.kt:
--------------------------------------------------------------------------------
1 | import kotlinx.serialization.Serializable
2 | import maestro.ai.anthropic.Content
3 |
4 | @Serializable
5 | data class Response(
6 | val content: List,
7 | )
8 |
--------------------------------------------------------------------------------
/maestro-ai/src/main/java/maestro/ai/common/Image.kt:
--------------------------------------------------------------------------------
1 | package maestro.ai.common
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | @Serializable
6 | data class Base64Image(
7 | val url: String,
8 | val detail: String,
9 | )
10 |
--------------------------------------------------------------------------------
/maestro-ai/src/main/resources/extractText_schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "extractText",
3 | "description": "Extracts text from an image based on a given query",
4 | "strict": true,
5 | "schema": {
6 | "type": "object",
7 | "required": [
8 | "text"
9 | ],
10 | "additionalProperties": false,
11 | "properties": {
12 | "text": {
13 | "type": "string"
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/maestro-android/src/androidTest/java/androidx/test/uiautomator/UiDeviceExt.kt:
--------------------------------------------------------------------------------
1 | package androidx.test.uiautomator
2 |
3 | object UiDeviceExt {
4 |
5 | /**
6 | * Fix for a UiDevice.click() method that discards taps that happen outside of the screen bounds.
7 | * The issue with the original method is that it was computing screen bounds incorrectly.
8 | */
9 | fun UiDevice.clickExt(x: Int, y: Int) {
10 | interactionController.clickNoSync(
11 | x, y
12 | )
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/maestro-android/src/androidTest/java/dev/mobile/maestro/location/MockLocationProvider.kt:
--------------------------------------------------------------------------------
1 | package dev.mobile.maestro.location
2 |
3 | import android.location.Location
4 |
5 | interface MockLocationProvider {
6 |
7 | fun setLocation(location: Location)
8 |
9 | fun enable()
10 |
11 | fun disable()
12 |
13 | fun getProviderName(): String
14 | }
--------------------------------------------------------------------------------
/maestro-android/src/androidTest/java/dev/mobile/maestro/location/PlayServices.kt:
--------------------------------------------------------------------------------
1 | package dev.mobile.maestro.location
2 |
3 | import android.content.Context
4 | import com.google.android.gms.common.ConnectionResult
5 | import com.google.android.gms.common.GoogleApiAvailability
6 |
7 | class PlayServices {
8 |
9 | fun isAvailable(context: Context): Boolean {
10 | val apiAvailability = GoogleApiAvailability.getInstance()
11 | val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
12 | return resultCode == ConnectionResult.SUCCESS
13 | }
14 | }
--------------------------------------------------------------------------------
/maestro-android/src/main/java/dev/mobile/maestro/receivers/HasAction.kt:
--------------------------------------------------------------------------------
1 | package dev.mobile.maestro.receivers
2 |
3 | interface HasAction {
4 | fun action(): String
5 | }
--------------------------------------------------------------------------------
/maestro-android/src/main/res/values/stub.xml:
--------------------------------------------------------------------------------
1 |
2 | Maestro Driver
3 |
4 |
--------------------------------------------------------------------------------
/maestro-cli/gradle.properties:
--------------------------------------------------------------------------------
1 | CLI_VERSION=1.40.3
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/CliError.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli
2 |
3 | class CliError(override val message: String) : RuntimeException(message)
4 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/Dependencies.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli
2 |
3 | import maestro.cli.util.Unpacker.binaryDependency
4 | import maestro.cli.util.Unpacker.unpack
5 |
6 | object Dependencies {
7 | private val appleSimUtils = binaryDependency("applesimutils")
8 |
9 | fun install() {
10 | unpack(
11 | jarPath = "deps/applesimutils",
12 | target = appleSimUtils,
13 | )
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/ShowHelpMixin.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli
2 |
3 | import picocli.CommandLine
4 |
5 | class ShowHelpMixin {
6 | @CommandLine.Option(
7 | names = ["-h", "--help"],
8 | usageHelp = true,
9 | description = ["Display help message"],
10 | )
11 | var help = false
12 | }
13 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/driver/DriverBuildConfig.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.driver
2 |
3 | import maestro.cli.api.CliVersion
4 |
5 | data class DriverBuildConfig(
6 | val teamId: String,
7 | val derivedDataPath: String,
8 | val sourceCodePath: String = "driver/ios",
9 | val sourceCodeRoot: String = System.getProperty("user.home"),
10 | val destination: String = "generic/platform=iphoneos",
11 | val architectures: String = "arm64",
12 | val configuration: String = "Debug",
13 | val cliVersion: CliVersion?
14 | )
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/driver/XcodeBuildProcessBuilderFactory.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.driver
2 |
3 | import java.io.File
4 |
5 | class XcodeBuildProcessBuilderFactory {
6 |
7 | fun createProcess(commands: List, workingDirectory: File, outputFile: File): Process {
8 | return ProcessBuilder(commands).directory(workingDirectory).redirectOutput(outputFile)
9 | .redirectError(outputFile)
10 | .start()
11 | }
12 | }
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/graphics/VideoRenderer.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.graphics
2 |
3 | import maestro.cli.runner.resultview.AnsiResultView
4 | import java.io.File
5 |
6 | interface VideoRenderer {
7 | fun render(
8 | screenRecording: File,
9 | textFrames: List,
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/model/DeviceStartOptions.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.model
2 |
3 | import maestro.device.Platform
4 |
5 | data class DeviceStartOptions(
6 | val platform: Platform,
7 | val osVersion: Int?,
8 | val forceCreate: Boolean
9 | )
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/model/RunningFlow.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.model
2 |
3 | import maestro.cli.api.UploadStatus
4 | import kotlin.time.Duration
5 |
6 | data class RunningFlows(
7 | val flows: MutableSet = mutableSetOf(),
8 | var duration: Duration? = null
9 | )
10 |
11 | data class RunningFlow(
12 | val name: String,
13 | var status: FlowStatus? = null,
14 | var startTime: Long? = null,
15 | var duration: Duration? = null,
16 | var reported: Boolean = false
17 | )
18 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/report/ReportFormat.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.report
2 |
3 | enum class ReportFormat(
4 | val fileExtension: String?
5 | ) {
6 |
7 | JUNIT(".xml"),
8 | HTML(".html"),
9 | NOOP(null),
10 |
11 | }
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/report/ReporterFactory.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.report
2 |
3 | import maestro.cli.model.TestExecutionSummary
4 | import okio.BufferedSink
5 |
6 | object ReporterFactory {
7 |
8 | fun buildReporter(format: ReportFormat, testSuiteName: String?): TestSuiteReporter {
9 | return when (format) {
10 | ReportFormat.JUNIT -> JUnitTestSuiteReporter.xml(testSuiteName)
11 | ReportFormat.NOOP -> TestSuiteReporter.NOOP
12 | ReportFormat.HTML -> HtmlTestSuiteReporter()
13 | }
14 | }
15 |
16 | }
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/runner/resultview/ResultView.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.runner.resultview
2 |
3 | interface ResultView {
4 | fun setState(state: UiState)
5 | }
6 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/runner/resultview/UiState.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.runner.resultview
2 |
3 | import maestro.device.Device
4 | import maestro.cli.runner.CommandState
5 |
6 | sealed class UiState {
7 |
8 | data class Error(val message: String) : UiState()
9 |
10 | data class Running(
11 | val flowName: String,
12 | val device: Device? = null,
13 | val onFlowStartCommands: List = emptyList(),
14 | val onFlowCompleteCommands: List = emptyList(),
15 | val commands: List,
16 | ) : UiState()
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/util/ResourceUtils.kt:
--------------------------------------------------------------------------------
1 | import kotlin.reflect.KClass
2 |
3 | fun readResourceAsText(cls: KClass<*>, path: String): String {
4 | val resourceStream = cls::class.java.getResourceAsStream(path)
5 | ?: throw IllegalStateException("Could not find $path in resources")
6 |
7 | return resourceStream.bufferedReader().use { it.readText() }
8 | }
9 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/util/ScreenReporter.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.util
2 |
3 | import maestro.cli.api.ApiClient
4 | import maestro.utils.DepthTracker
5 |
6 | object ScreenReporter {
7 |
8 | fun reportMaxDepth() {
9 | val maxDepth = DepthTracker.getMaxDepth()
10 |
11 | if (maxDepth == 0) return
12 |
13 | ApiClient(EnvUtils.BASE_API_URL).sendScreenReport(maxDepth = maxDepth)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/util/SocketUtils.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.util
2 |
3 | import java.net.ServerSocket
4 |
5 | fun getFreePort(): Int {
6 | (9999..11000).forEach { port ->
7 | try {
8 | ServerSocket(port).use { return it.localPort }
9 | } catch (ignore: Exception) {}
10 | }
11 | ServerSocket(0).use { return it.localPort }
12 | }
--------------------------------------------------------------------------------
/maestro-cli/src/main/java/maestro/cli/util/TimeUtils.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.util
2 |
3 | import kotlin.math.roundToLong
4 | import kotlin.time.Duration
5 | import kotlin.time.Duration.Companion.seconds
6 |
7 | object TimeUtils {
8 |
9 | fun durationInSeconds(startTimeInMillis: Long?, endTimeInMillis: Long?): Duration {
10 | if (startTimeInMillis == null || endTimeInMillis == null) return Duration.ZERO
11 | return ((endTimeInMillis - startTimeInMillis) / 1000f).roundToLong().seconds
12 | }
13 |
14 | fun durationInSeconds(durationInMillis: Long): Duration {
15 | return ((durationInMillis) / 1000f).roundToLong().seconds
16 | }
17 | }
--------------------------------------------------------------------------------
/maestro-cli/src/main/resources/deps/applesimutils:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-cli/src/main/resources/deps/applesimutils
--------------------------------------------------------------------------------
/maestro-cli/src/main/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [%-5level] %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/maestro-cli/src/main/resources/record-background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-cli/src/main/resources/record-background.jpg
--------------------------------------------------------------------------------
/maestro-cli/src/main/resources/tailwind.config.js:
--------------------------------------------------------------------------------
1 | tailwind.config = {
2 | darkMode: "media",
3 | theme: {
4 | extend: {
5 | colors: {
6 | "gray-dark": "#110c22", // text-gray-dark
7 | "gray-medium": "#4f4b5c", // text-gray-medium
8 | "gray-1": "#f8f8f8", // surface-gray-1
9 | "gray-0": "#110C22", // surface-gray-0
10 | "orange-2": "#ff9254", // surface-orange-2
11 | },
12 | },
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/maestro-cli/src/test/kotlin/maestro/cli/android/AndroidDeviceProvider.kt:
--------------------------------------------------------------------------------
1 | package maestro.cli.android
2 |
3 | import dadb.Dadb
4 | import dadb.adbserver.AdbServer
5 |
6 | class AndroidDeviceProvider {
7 |
8 | fun local(): Dadb {
9 | val dadb = AdbServer.createDadb(connectTimeout = 60_000, socketTimeout = 60_000)
10 |
11 | return dadb
12 | }
13 | }
--------------------------------------------------------------------------------
/maestro-cli/src/test/resources/location/assert_multiple_locations.yaml:
--------------------------------------------------------------------------------
1 | appId: "com.google.android.apps.maps"
2 | ---
3 | - launchApp
4 | - tapOn:
5 | text: Skip
6 | optional: true
7 | - setLocation:
8 | longitude: 2.295188
9 | latitude: 48.8578065
10 | - assertVisible: .*Eiffel.*
11 | - setLocation:
12 | latitude: 43.7230
13 | longitude: 10.3966
14 | - assertVisible: .*(Piazza|Pisa).*
--------------------------------------------------------------------------------
/maestro-cli/src/test/resources/travel/assert_travel_command.yaml:
--------------------------------------------------------------------------------
1 | appId: "com.google.android.apps.maps"
2 | ---
3 | - launchApp
4 | - tapOn:
5 | text: Skip
6 | optional: true
7 | - setLocation:
8 | latitude: 48.8578065
9 | longitude: 2.295188
10 | - assertVisible: .*Eiffel.*
11 | - travel:
12 | points:
13 | - 48.8578065, 2.295188
14 | - 46.2276, 5.9900
15 | - 43.7230, 10.3966
16 | - 41.8902, 12.4922
17 | speed: 1000
18 | - assertVisible: .*Colosseo.*
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/maestro-client/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro Client
2 | POM_ARTIFACT_ID=maestro-client
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/Capability.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | enum class Capability {
4 | FAST_HIERARCHY,
5 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/FindElementResult.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | data class FindElementResult(val element: UiElement, val hierarchy: ViewHierarchy)
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/Media.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | import okio.Source
4 |
5 | class NamedSource(val name: String, val source: Source, val extension: String, val path: String)
6 |
7 | enum class MediaExt(val extName: String) {
8 | PNG("png"),
9 | JPEG("jpeg"),
10 | JPG("jpg"),
11 | GIF("gif"),
12 | MP4("mp4"),
13 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/Platform.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | enum class Platform {
4 | ANDROID, IOS, WEB
5 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/ScreenRecording.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | interface ScreenRecording : AutoCloseable
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/ScrollDirection.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | enum class ScrollDirection {
4 | UP,
5 | DOWN,
6 | RIGHT,
7 | LEFT
8 | }
9 |
10 | fun ScrollDirection.toSwipeDirection(): SwipeDirection = when (this) {
11 | ScrollDirection.DOWN -> SwipeDirection.UP
12 | ScrollDirection.UP -> SwipeDirection.DOWN
13 | ScrollDirection.LEFT -> SwipeDirection.RIGHT
14 | ScrollDirection.RIGHT -> SwipeDirection.LEFT
15 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/SwipeDirection.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | enum class SwipeDirection {
4 | UP,
5 | DOWN,
6 | RIGHT,
7 | LEFT
8 | }
9 |
10 | inline fun > directionValueOfOrNull(input: String): SwipeDirection? {
11 | return enumValues().find { it.name == input || it.name.lowercase() == input }
12 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/TapRepeat.kt:
--------------------------------------------------------------------------------
1 | package maestro
2 |
3 | data class TapRepeat(
4 | val repeat: Int,
5 | val delay: Long // millis
6 | )
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/device/DeviceError.kt:
--------------------------------------------------------------------------------
1 | package maestro.device
2 |
3 | /**
4 | * Exception class specifically for device-related errors in the client module.
5 | * Functionally equivalent to CliError in maestro-cli.
6 | */
7 | class DeviceError(override val message: String) : RuntimeException(message)
8 |
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/device/Platform.kt:
--------------------------------------------------------------------------------
1 | package maestro.device
2 |
3 | enum class Platform(val description: String) {
4 | ANDROID("Android"),
5 | IOS("iOS"),
6 | WEB("Web");
7 |
8 | companion object {
9 | fun fromString(p: String?): Platform? {
10 | return values().firstOrNull { it.description.lowercase() == p?.lowercase() }
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/device/util/AvdDevice.kt:
--------------------------------------------------------------------------------
1 | package maestro.device.util
2 |
3 | data class AvdDevice(val numericId: String, val nameId: String, val name: String)
4 |
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/device/util/PrintUtils.kt:
--------------------------------------------------------------------------------
1 | package maestro.device.util
2 |
3 | import org.slf4j.LoggerFactory
4 |
5 | /**
6 | * Simplified version of PrintUtils for DeviceService
7 | */
8 | object PrintUtils {
9 | private val logger = LoggerFactory.getLogger(PrintUtils::class.java)
10 |
11 | fun message(message: String) {
12 | logger.info(message)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/device/util/SystemInfo.kt:
--------------------------------------------------------------------------------
1 | package maestro.device.util
2 |
3 | object SystemInfo {
4 | fun getJavaVersion(): Int {
5 | // Adapted from https://stackoverflow.com/a/2591122/7009800
6 | val version = System.getProperty("java.version")
7 | return if (version.startsWith("1.")) {
8 | version.substring(2, 3).toInt()
9 | } else {
10 | val dot = version.indexOf(".")
11 | if (dot != -1) version.substring(0, dot).toInt() else 0
12 | }
13 | }
14 | }
15 |
16 |
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/js/JsConsole.kt:
--------------------------------------------------------------------------------
1 | package maestro.js
2 |
3 | import org.mozilla.javascript.ScriptableObject
4 |
5 | class JsConsole(
6 | private val onLogMessage: (String) -> Unit,
7 | ) : ScriptableObject() {
8 |
9 | fun log(message: String) {
10 | onLogMessage(message)
11 | }
12 |
13 | override fun getClassName(): String {
14 | return "JsConsole"
15 | }
16 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/js/JsEngine.kt:
--------------------------------------------------------------------------------
1 | package maestro.js
2 |
3 | interface JsEngine : AutoCloseable {
4 | fun onLogMessage(callback: (String) -> Unit)
5 | fun enterScope()
6 | fun leaveScope()
7 | fun putEnv(key: String, value: String)
8 | fun setCopiedText(text: String?)
9 | fun evaluateScript(
10 | script: String,
11 | env: Map = emptyMap(),
12 | sourceName: String = "inline-script",
13 | runInSubScope: Boolean = false,
14 | ): Any?
15 | }
16 |
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/utils/StringUtils.kt:
--------------------------------------------------------------------------------
1 | package maestro.utils
2 |
3 | object StringUtils {
4 |
5 | fun String.toRegexSafe(option: RegexOption) = toRegexSafe(setOf(option))
6 |
7 | fun String.toRegexSafe(options: Set = emptySet()): Regex {
8 | return try {
9 | toRegex(options)
10 | } catch (e: Exception) {
11 | Regex.escape(this).toRegex(options)
12 | }
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/maestro-client/src/main/java/maestro/utils/TemporaryDirectory.kt:
--------------------------------------------------------------------------------
1 | package maestro.utils
2 |
3 | import java.nio.file.Files
4 | import java.nio.file.Path
5 |
6 | object TemporaryDirectory {
7 |
8 | inline fun use(block: (tmpDir: Path) -> T): T {
9 | val tmpDir = Files.createTempDirectory(null)
10 | return try {
11 | block(tmpDir)
12 | } finally {
13 | tmpDir.toFile().deleteRecursively()
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/maestro-client/src/main/resources/maestro-app.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-client/src/main/resources/maestro-app.apk
--------------------------------------------------------------------------------
/maestro-client/src/main/resources/maestro-server.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-client/src/main/resources/maestro-server.apk
--------------------------------------------------------------------------------
/maestro-client/src/test/java/maestro/utils/StringUtilsTest.kt:
--------------------------------------------------------------------------------
1 | package maestro.utils
2 |
3 | import com.google.common.truth.Truth.assertThat
4 | import maestro.utils.StringUtils.toRegexSafe
5 | import org.junit.jupiter.api.Test
6 |
7 | internal class StringUtilsTest {
8 |
9 | @Test
10 | internal fun `toRegexSafe should escape string if regex is invalid`() {
11 | // Given
12 | val input = "*Oświadczam, że zapoznałem się z treścią Regulaminu serwisu i akceptuję jego postanowienia."
13 |
14 | // When
15 | val regex = input.toRegexSafe()
16 |
17 | // Then
18 | assertThat(regex.matches(input)).isTrue()
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/maestro-client/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [%-5level] %logger{36} - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/maestro-ios-driver/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro XCUITest Driver
2 | POM_ARTIFACT_ID=maestro-ios-driver
3 | POM_PACKAGING=jar
4 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/util/PrintUtils.kt:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | object PrintUtils {
4 |
5 | fun log(message: String) {
6 | println(message)
7 | }
8 | }
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/XCTestClient.kt:
--------------------------------------------------------------------------------
1 | package xcuitest
2 |
3 | import okhttp3.HttpUrl
4 |
5 | class XCTestClient(
6 | val host: String,
7 | val port: Int,
8 | ) {
9 |
10 | fun xctestAPIBuilder(pathSegment: String): HttpUrl.Builder {
11 | return HttpUrl.Builder()
12 | .scheme("http")
13 | .host(host)
14 | .addPathSegment(pathSegment)
15 | .port(port)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/EraseTextRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class EraseTextRequest(
4 | val charactersToErase: Int,
5 | val appIds: Set,
6 | )
7 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/Error.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | import com.fasterxml.jackson.annotation.JsonProperty
4 |
5 | data class Error(
6 | @JsonProperty("errorMessage") val errorMessage: String,
7 | @JsonProperty("code") val errorCode: String,
8 | )
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/GetRunningAppIdResponse.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class GetRunningAppIdResponse(val runningAppBundleId: String)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/GetRunningAppRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class GetRunningAppRequest(val appIds: Set)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/InputTextRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class InputTextRequest(
4 | val text: String,
5 | val appIds: Set
6 | )
7 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/IsScreenStaticResponse.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class IsScreenStaticResponse(val isScreenStatic: Boolean)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/KeyboardInfoRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class KeyboardInfoRequest(val appIds: Set)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/KeyboardInfoResponse.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class KeyboardInfoResponse(val isKeyboardVisible: Boolean)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/LaunchAppRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class LaunchAppRequest(val bundleId: String)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/OkHttpClientInstance.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | import okhttp3.ConnectionPool
4 | import okhttp3.OkHttpClient
5 | import java.util.concurrent.TimeUnit
6 |
7 | object OkHttpClientInstance {
8 |
9 | fun get(): OkHttpClient {
10 | return OkHttpClient.Builder()
11 | .connectionPool(ConnectionPool(225, 10, TimeUnit.MINUTES))
12 | .connectTimeout(1, TimeUnit.SECONDS)
13 | .readTimeout(200, TimeUnit.SECONDS)
14 | .build()
15 | }
16 | }
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/PressButtonRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class PressButtonRequest(val button: String)
4 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/PressKeyRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class PressKeyRequest(val key: String)
4 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/SetPermissionsRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class SetPermissionsRequest(val permissions: Map)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/SwipeRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class SwipeRequest(
4 | val appId: String? = null,
5 | val startX: Double,
6 | val startY: Double,
7 | val endX: Double,
8 | val endY: Double,
9 | val duration: Double,
10 | val appIds: Set? = null
11 | )
12 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/TerminateAppRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class TerminateAppRequest(val appId: String)
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/TouchRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class TouchRequest(
4 | val x: Float,
5 | val y: Float,
6 | val duration: Double?
7 | )
8 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/api/ViewHierarchyRequest.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.api
2 |
3 | data class ViewHierarchyRequest(val appIds: Set, val excludeKeyboardElements: Boolean)
4 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/kotlin/xcuitest/installer/XCTestInstaller.kt:
--------------------------------------------------------------------------------
1 | package xcuitest.installer
2 |
3 | import xcuitest.XCTestClient
4 |
5 | interface XCTestInstaller: AutoCloseable {
6 | fun start(): XCTestClient
7 |
8 | /**
9 | * Attempts to uninstall the XCTest Runner.
10 | *
11 | * @return true if the XCTest Runner was uninstalled, false otherwise.
12 | */
13 | fun uninstall(): Boolean
14 |
15 | fun isChannelAlive(): Boolean
16 | }
17 |
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/resources/driver-iPhoneSimulator/Debug-iphonesimulator/maestro-driver-ios.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-ios-driver/src/main/resources/driver-iPhoneSimulator/Debug-iphonesimulator/maestro-driver-ios.zip
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/resources/driver-iPhoneSimulator/Debug-iphonesimulator/maestro-driver-iosUITests-Runner.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-ios-driver/src/main/resources/driver-iPhoneSimulator/Debug-iphonesimulator/maestro-driver-iosUITests-Runner.zip
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/resources/driver-iphoneos/Debug-iphoneos/maestro-driver-ios.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-ios-driver/src/main/resources/driver-iphoneos/Debug-iphoneos/maestro-driver-ios.zip
--------------------------------------------------------------------------------
/maestro-ios-driver/src/main/resources/driver-iphoneos/Debug-iphoneos/maestro-driver-iosUITests-Runner.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-ios-driver/src/main/resources/driver-iphoneos/Debug-iphoneos/maestro-driver-iosUITests-Runner.zip
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | xcuserdata/
3 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/build-maestro-ios-runner-all.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | DESTINATION="generic/platform=iOS Simulator" DERIVED_DATA_DIR="driver-iPhoneSimulator" ARCHS="x86_64 arm64" $PWD/maestro-ios-xctest-runner/build-maestro-ios-runner.sh
4 | DESTINATION="generic/platform=iphoneos" DERIVED_DATA_DIR="driver-iphoneos" ARCHS="arm64" $PWD/maestro-ios-xctest-runner/build-maestro-ios-runner.sh
5 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-ios.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "fcc5a9e047bc89c422e9ca5571e5b664446abc3c4c0b838c23d066c241e1a3f6",
3 | "pins" : [
4 | {
5 | "identity" : "flyingfox",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/swhitty/FlyingFox",
8 | "state" : {
9 | "revision" : "3ad076e081749cef043e25ac01719a503b772113",
10 | "version" : "0.22.0"
11 | }
12 | }
13 | ],
14 | "version" : 3
15 | }
16 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-ios/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-ios/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-ios/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // maestro-driver-ios
4 | //
5 | //
6 | //
7 |
8 | import UIKit
9 |
10 | class ViewController: UIViewController {
11 |
12 | override func viewDidLoad() {
13 | super.viewDidLoad()
14 | // Do any additional setup after loading the view.
15 | }
16 |
17 |
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Categories/XCUIApplication+Helper.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface XCUIApplication (Helper)
4 |
5 | + (NSArray *> *)activeAppsInfo;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Categories/maestro-driver-iosUITests-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | #import "XCUIApplication+FBQuiescence.h"
6 | #import "XCUIApplication+Helper.h"
7 | #import "XCAXClient_iOS+FBSnapshotReqParams.h"
8 | #import "AXClientProxy.h"
9 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/NSString-XCTAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface NSString (XCTAdditions)
10 | - (id)xct_quotedSwiftStringRepresentation;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/NSValue-XCTestAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface NSValue (XCTestAdditions)
10 | - (id)xct_contentDescription;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/UIGestureRecognizer-RecordingAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface UIGestureRecognizer (RecordingAdditions)
10 | - (id)_automationName;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/UILongPressGestureRecognizer-RecordingAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface UILongPressGestureRecognizer (RecordingAdditions)
10 | - (id)_automationName;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/UIPanGestureRecognizer-RecordingAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface UIPanGestureRecognizer (RecordingAdditions)
10 | - (id)_automationName;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/UIPinchGestureRecognizer-RecordingAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface UIPinchGestureRecognizer (RecordingAdditions)
10 | - (id)_automationName;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/UISwipeGestureRecognizer-RecordingAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface UISwipeGestureRecognizer (RecordingAdditions)
10 | - (id)_automationName;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/UITapGestureRecognizer-RecordingAdditions.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface UITapGestureRecognizer (RecordingAdditions)
10 | - (id)_automationName;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCApplicationMonitor_iOS.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface XCApplicationMonitor_iOS : XCApplicationMonitor
10 | {
11 | }
12 |
13 | - (void)_terminateApplicationProcess:(id)arg1;
14 | - (id)monitoredApplicationWithProcessIdentifier:(int)arg1;
15 | - (void)_beginMonitoringApplication:(id)arg1;
16 | - (id)init;
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCDebugLogDelegate-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class NSString;
8 |
9 | @protocol XCDebugLogDelegate
10 | - (void)logDebugMessage:(NSString *)arg1;
11 | @end
12 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCSourceCodeTreeNodeEnumerator.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class NSMutableArray;
8 |
9 | @interface XCSourceCodeTreeNodeEnumerator : NSObject
10 | {
11 | NSMutableArray *_remainingNodes;
12 | }
13 |
14 | - (id)nextObject;
15 | - (id)initWithNode:(id)arg1;
16 |
17 | @end
18 |
19 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCSymbolicatorHolder.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @interface XCSymbolicatorHolder : NSObject
8 | {
9 | struct _CSTypeRef _symbolicator;
10 | }
11 |
12 | @property struct _CSTypeRef symbolicator; // @synthesize symbolicator=_symbolicator;
13 |
14 | @end
15 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTAXClient-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | @class NSData;
10 |
11 | @protocol XCTAXClient
12 | - (void)handleAccessibilityNotification:(int)arg1 withPayload:(NSData *)arg2;
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTAsyncActivity-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | @class NSError;
10 |
11 | @protocol XCTAsyncActivity
12 | @property(readonly) BOOL timedOut;
13 | @property(readonly) NSError *error;
14 | - (void)finishWithError:(NSError *)arg1;
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTAutomationTarget-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | @protocol XCTAutomationTarget
10 | - (void)requestHostAppExecutableNameWithReply:(void (^)(NSString *))arg1;
11 | @end
12 |
13 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTNSPredicateExpectationObject-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | @class XCTNSPredicateExpectation;
10 |
11 | @protocol XCTNSPredicateExpectationObject
12 |
13 | @optional
14 | - (BOOL)evaluatePredicateForExpectation:(XCTNSPredicateExpectation *)arg1 debugMessage:(id *)arg2;
15 | @end
16 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTRunnerAutomationSession.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | #import "XCTRunnerAutomationSession.h"
10 |
11 | @class NSString, NSXPCConnection;
12 |
13 | @interface XCTRunnerAutomationSession : NSObject
14 | {
15 | NSXPCConnection *_connection;
16 | }
17 | @property NSXPCConnection *connection; // @synthesize connection=_connection;
18 |
19 | - (id)initWithEndpoint:(id)arg1;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTWaiterDelegatePrivate-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class XCTWaiter;
8 |
9 | @protocol XCTWaiterDelegatePrivate
10 | - (void)nestedWaiter:(XCTWaiter *)arg1 wasInterruptedByTimedOutWaiter:(XCTWaiter *)arg2;
11 | @end
12 |
13 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTWaiterManagement-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | @protocol XCTWaiterManagement
10 | @property(readonly, getter=isInProgress) BOOL inProgress;
11 | - (void)interruptForWaiter:(id )arg1;
12 | @end
13 |
14 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestCaseSuite.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface XCTestCaseSuite : XCTestSuite
10 | {
11 | Class _testCaseClass;
12 | }
13 |
14 | + (id)emptyTestSuiteForTestCaseClass:(Class)arg1;
15 | - (void)tearDown;
16 | - (void)setUp;
17 | - (id)initWithTestCaseClass:(Class)arg1;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestContextScope.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class NSMutableArray;
8 |
9 | @interface XCTestContextScope : NSObject
10 | {
11 | XCTestContextScope *_parentScope;
12 | NSMutableArray *_handlers;
13 | }
14 | @property(copy) NSMutableArray *handlers; // @synthesize handlers=_handlers;
15 | @property(readonly) XCTestContextScope *parentScope; // @synthesize parentScope=_parentScope;
16 |
17 | - (id)initWithParentScope:(id)arg1;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestExpectationDelegate-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import "NSObject.h"
8 |
9 | @class XCTestExpectation;
10 |
11 | @protocol XCTestExpectationDelegate
12 | - (void)didFulfillExpectation:(XCTestExpectation *)arg1;
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestExpectationWaiter.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface XCTestExpectationWaiter : XCTWaiter
10 | {
11 | }
12 |
13 | - (long long)wait:(double)arg1 forExpectations:(id)arg2 enforceOrder:(BOOL)arg3;
14 | - (long long)wait:(double)arg1 forExpectations:(id)arg2;
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestManager_TestsInterface-Protocol.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class NSData, NSString;
8 |
9 | @protocol XCTestManager_TestsInterface
10 | - (void)_XCT_receivedAccessibilityNotification:(int)arg1 withPayload:(NSData *)arg2;
11 | - (void)_XCT_applicationWithBundleID:(NSString *)arg1 didUpdatePID:(int)arg2 andState:(unsigned long long)arg3;
12 | @end
13 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestProbe.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @interface XCTestProbe : NSObject
8 | {
9 | }
10 |
11 | + (BOOL)isTesting;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCTestWaiter.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | #import
8 |
9 | @interface XCTestWaiter : XCTWaiter
10 | {
11 | }
12 |
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/XCUIRecorderTimingMessage.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class NSString;
8 |
9 | @interface XCUIRecorderTimingMessage : NSObject
10 | {
11 | double _start;
12 | NSString *_message;
13 | }
14 | @property(copy) NSString *message; // @synthesize message=_message;
15 | @property double start; // @synthesize start=_start;
16 |
17 | + (id)descriptionForTimingMessages:(id)arg1;
18 | + (id)messageWithString:(id)arg1;
19 |
20 | @end
21 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/_XCTestCaseInterruptionException.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @interface _XCTestCaseInterruptionException : NSException
8 | {
9 | }
10 |
11 | + (void)interruptTest;
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/_XCTestImplementation.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class XCTestRun;
8 |
9 | @interface _XCTestImplementation : NSObject
10 | {
11 | XCTestRun *_testRun;
12 | }
13 | @property(retain) XCTestRun *testRun; // @synthesize testRun=_testRun;
14 | @end
15 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/PrivateHeaders/XCTest/_XCTestObservationCenterImplementation.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated by class-dump 3.5 (64 bit).
3 | //
4 | // class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2013 by Steve Nygard.
5 | //
6 |
7 | @class NSMutableSet;
8 |
9 | @interface _XCTestObservationCenterImplementation : NSObject
10 | {
11 | NSMutableArray *_observers;
12 | BOOL _suspended;
13 | }
14 |
15 | @property BOOL suspended; // @synthesize suspended=_suspended;
16 | @property(retain) NSMutableArray *observers; // @synthesize observers=_observers;
17 | - (id)init;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Extensions/Logger.swift:
--------------------------------------------------------------------------------
1 |
2 | import Foundation
3 | import os
4 |
5 | extension Logger {
6 | func measure(message: String, _ block: () throws -> T) rethrows -> T {
7 | let start = Date()
8 | info("\(message) - start")
9 |
10 | let result = try block()
11 |
12 | let duration = Date().timeIntervalSince(start)
13 | NSLog("\(message) - duration \(duration)")
14 |
15 | return result
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Extensions/StringExtensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 |
4 | extension String {
5 | func toUInt16() -> UInt16? {
6 | return UInt16(self)
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Extensions/XCUIElement+Extensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import XCTest
3 |
4 | extension XCUIElement {
5 | func setText(text: String, application: XCUIApplication) {
6 | UIPasteboard.general.string = text
7 | doubleTap()
8 | application.menuItems["Paste"].tap()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/DeviceInfoResponse.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct DeviceInfoResponse: Codable {
4 | let widthPoints: Int
5 | let heightPoints: Int
6 | let widthPixels: Int
7 | let heightPixels: Int
8 | }
9 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/EraseTextRequest.swift:
--------------------------------------------------------------------------------
1 |
2 | import Foundation
3 |
4 | struct EraseTextRequest: Codable {
5 | let charactersToErase: Int
6 | let appIds: [String]
7 | }
8 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/GetRunningAppRequest.swift:
--------------------------------------------------------------------------------
1 | struct RunningAppRequest: Codable {
2 | let appIds: [String]
3 | }
4 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/InputTextRequest.swift:
--------------------------------------------------------------------------------
1 | struct InputTextRequest: Codable {
2 | let text: String
3 | let appIds: [String]
4 | }
5 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/KeyboardHandlerRequest.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct KeyboardHandlerRequest: Codable {
4 | let appIds: [String]
5 | }
6 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/KeyboardHandlerResponse.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct KeyboardHandlerResponse: Codable {
4 | let isKeyboardVisible: Bool
5 | }
6 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/LaunchAppRequest.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct LaunchAppRequest : Codable {
4 | let bundleId: String
5 | }
6 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/PressButtonRequest.swift:
--------------------------------------------------------------------------------
1 |
2 | import Foundation
3 | import XCTest
4 |
5 | struct PressButtonRequest: Codable {
6 | enum Button: String, Codable {
7 | case home
8 | case lock
9 | }
10 |
11 | let button: Button
12 | }
13 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/SetPermissionsRequest.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | enum PermissionValue: String, Codable {
4 | case allow
5 | case deny
6 | case unset
7 | case unknown
8 |
9 | init(from decoder: Decoder) throws {
10 | self = try PermissionValue(rawValue: decoder.singleValueContainer().decode(RawValue.self)) ?? .unknown
11 | }
12 | }
13 |
14 | struct SetPermissionsRequest: Codable {
15 | let permissions: [String : PermissionValue]
16 | }
17 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/StatusResponse.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct StatusResponse: Codable {
4 | let status: String
5 | }
6 |
7 | enum Status: Codable {
8 | case ok
9 | }
10 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/TerminateAppRequest.swift:
--------------------------------------------------------------------------------
1 | struct TerminateAppRequest: Codable {
2 | let appId: String
3 | }
4 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/TouchRequest.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct TouchRequest : Codable {
4 | let x: Float
5 | let y: Float
6 | let duration: TimeInterval?
7 | }
8 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/Models/ViewHierarchyRequest.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | struct ViewHierarchyRequest: Codable {
4 | let appIds: [String]
5 | let excludeKeyboardElements: Bool
6 | }
7 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Routes/XCTest/KeyModifierFlags.swift:
--------------------------------------------------------------------------------
1 |
2 | struct KeyModifierFlags: OptionSet {
3 | let rawValue: UInt64
4 | static let capsLock = KeyModifierFlags(rawValue: 1 << 0)
5 | static let shift = KeyModifierFlags(rawValue: 1 << 1)
6 | static let control = KeyModifierFlags(rawValue: 1 << 2)
7 | static let option = KeyModifierFlags(rawValue: 1 << 3)
8 | static let command = KeyModifierFlags(rawValue: 1 << 4)
9 | static let function = KeyModifierFlags(rawValue: 1 << 5)
10 | }
11 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Utilities/AXClientProxy.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "XCAccessibilityElement.h"
3 |
4 | @interface AXClientProxy : NSObject
5 |
6 | + (instancetype)sharedClient;
7 |
8 | - (NSArray> *)activeApplications;
9 |
10 | - (NSDictionary *)defaultParameters;
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/maestro-ios-xctest-runner/maestro-driver-iosUITests/Utilities/XCTestDaemonsProxy.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import "XCSynthesizedEventRecord.h"
3 |
4 |
5 | @protocol XCTestManager_ManagerInterface;
6 |
7 | @interface XCTestDaemonsProxy : NSObject
8 |
9 | + (id)testRunnerProxy;
10 |
11 |
12 | @end
13 |
--------------------------------------------------------------------------------
/maestro-ios/README.md:
--------------------------------------------------------------------------------
1 | # iOS Device Config
2 |
3 | A wrapper around `simctl` and XCTest to communicate with iOS devices.
4 |
5 | ## Prerequisites
6 |
7 | ### Xcode
8 |
9 | Install the latest Xcode (Command Line Tools are not enough, install the full IDE).
10 |
11 | ### IntelliJ setup
12 |
13 | If you are working with this subproject, update your IntelliJ config (Help -> Edit Custom Properties) by including the following lines:
14 |
15 | ```
16 | # Needed for working with idb.proto definition
17 | idea.max.intellisense.filesize=4000
18 | ```
19 |
20 | Then restart the IDE.
21 |
--------------------------------------------------------------------------------
/maestro-ios/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro iOS
2 | POM_ARTIFACT_ID=maestro-ios
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/maestro-ios/src/main/java/ios/IOSDeviceErrors.kt:
--------------------------------------------------------------------------------
1 | package ios
2 |
3 | sealed class IOSDeviceErrors : Throwable() {
4 | data class AppCrash(val errorMessage: String): IOSDeviceErrors()
5 | }
--------------------------------------------------------------------------------
/maestro-orchestra-models/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Orchestra Models
2 | POM_ARTIFACT_ID=maestro-orchestra-models
3 | POM_PACKAGING=jar
4 |
--------------------------------------------------------------------------------
/maestro-orchestra-models/src/main/java/maestro/orchestra/ElementTrait.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra
2 |
3 | enum class ElementTrait(val description: String) {
4 | TEXT("Has text"),
5 | SQUARE("Is square"),
6 | LONG_TEXT("Has long text"),
7 | }
--------------------------------------------------------------------------------
/maestro-orchestra-models/src/test/kotlin/LitmusTest.kt:
--------------------------------------------------------------------------------
1 | import com.google.common.truth.Truth.assertThat
2 | import org.junit.jupiter.api.Test
3 |
4 | class LitmusTest {
5 | @Test
6 | fun `junit and truth are setup`() {
7 | assertThat(true)
8 | .isTrue()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/maestro-orchestra/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro Orchestra
2 | POM_ARTIFACT_ID=maestro-orchestra
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/error/InvalidFlowFile.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.error
2 |
3 | import java.nio.file.Path
4 |
5 | class InvalidFlowFile(
6 | override val message: String,
7 | val flowPath: Path
8 | ) : RuntimeException()
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/error/MediaFileNotFound.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.error
2 |
3 | import java.nio.file.Path
4 |
5 | class MediaFileNotFound(
6 | override val message: String,
7 | val mediaPath: Path
8 | ): ValidationError(message)
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/error/NoInputException.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.error
2 |
3 | object NoInputException : RuntimeException()
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/error/SyntaxError.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.error
2 |
3 | class SyntaxError(override val message: String, cause: Throwable? = null) : ValidationError(message, cause)
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/error/UnicodeNotSupportedError.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.error
2 |
3 | data class UnicodeNotSupportedError(
4 | val text: String,
5 | ) : RuntimeException("Unicode not supported: $text")
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/error/ValidationError.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.error
2 |
3 | open class ValidationError(override val message: String, cause: Throwable? = null) : RuntimeException(message, cause)
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/filter/FilterWithDescription.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.filter
2 |
3 | import maestro.ElementFilter
4 |
5 | data class FilterWithDescription(
6 | val description: String,
7 | val filterFunc: ElementFilter,
8 | )
9 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlAddMedia.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlAddMedia(
6 | val files: List? = null,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ) {
10 | companion object {
11 |
12 | @JvmStatic
13 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
14 | fun parse(files: List) = YamlAddMedia(
15 | files = files,
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlAssertNoDefectsWithAI.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlAssertNoDefectsWithAI(
4 | val optional: Boolean = true,
5 | val label: String? = null,
6 | )
7 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlClearState.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlClearState(
6 | val appId: String? = null,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ) {
10 | companion object {
11 | @JvmStatic
12 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
13 | fun parse(appId: String) = YamlClearState(
14 | appId = appId,
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlCondition.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonFormat
4 | import maestro.Platform
5 |
6 | data class YamlCondition(
7 | @JsonFormat(with = [JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES])
8 | val platform: Platform? = null,
9 | val visible: YamlElementSelectorUnion? = null,
10 | val notVisible: YamlElementSelectorUnion? = null,
11 | val `true`: String? = null,
12 | val label: String? = null,
13 | val optional: Boolean = false,
14 | )
15 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlEraseTextUnion.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlEraseText(
6 | val charactersToErase: Int? = null,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ) {
10 |
11 | companion object {
12 |
13 | @JvmStatic
14 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
15 | fun parse(charactersToRemove: Int): YamlEraseText {
16 | return YamlEraseText(
17 | charactersToErase = charactersToRemove
18 | )
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlExtendedWaitUntil.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlExtendedWaitUntil(
4 | val visible: YamlElementSelectorUnion? = null,
5 | val notVisible: YamlElementSelectorUnion? = null,
6 | val timeout: String? = null,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | )
10 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlKillApp.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlKillApp(
6 | val appId: String? = null,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ) {
10 |
11 | companion object {
12 |
13 | @JvmStatic
14 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
15 | fun parse(appId: String) = YamlKillApp(
16 | appId = appId,
17 | )
18 |
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlOnFlowComplete.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlOnFlowComplete(val commands: List) {
6 | companion object {
7 | @JvmStatic
8 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
9 | fun parse(commands: List) = YamlOnFlowComplete(
10 | commands = commands
11 | )
12 | }
13 | }
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlOnFlowStart.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlOnFlowStart(val commands: List) {
6 | companion object {
7 | @JvmStatic
8 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
9 | fun parse(commands: List) = YamlOnFlowStart(
10 | commands = commands
11 | )
12 | }
13 | }
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlPressKey.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlPressKey (
6 | val key: String,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ){
10 | companion object {
11 | @JvmStatic
12 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
13 | fun parse(key: String) = YamlPressKey(
14 | key = key,
15 | )
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlRepeatCommand.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlRepeatCommand(
4 | val times: String? = null,
5 | val `while`: YamlCondition? = null,
6 | val commands: List,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | )
10 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlRetry.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlRetryCommand(
4 | val maxRetries: String? = null,
5 | val file: String? = null,
6 | val commands: List? = null,
7 | val env: Map = emptyMap(),
8 | val label: String? = null,
9 | val optional: Boolean = false,
10 | )
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlRunScript.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlRunScript(
6 | val file: String,
7 | val env: Map = emptyMap(),
8 | val `when`: YamlCondition? = null,
9 | val label: String? = null,
10 | val optional: Boolean = false,
11 | ) {
12 |
13 | companion object {
14 |
15 | @JvmStatic
16 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
17 | fun parse(file: String) = YamlRunScript(
18 | file = file,
19 | )
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlSetLocation.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlSetLocation @JsonCreator constructor(
6 | val latitude: String,
7 | val longitude: String,
8 | val label: String? = null,
9 | val optional: Boolean = false,
10 | )
11 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlStopApp.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlStopApp(
6 | val appId: String? = null,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ) {
10 |
11 | companion object {
12 |
13 | @JvmStatic
14 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
15 | fun parse(appId: String) = YamlStopApp(
16 | appId = appId,
17 | )
18 |
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlTakeScreenshot.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | import com.fasterxml.jackson.annotation.JsonCreator
4 |
5 | data class YamlTakeScreenshot(
6 | val path: String,
7 | val label: String? = null,
8 | val optional: Boolean = false,
9 | ) {
10 |
11 | companion object {
12 |
13 | @JvmStatic
14 | @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
15 | fun parse(path: String): YamlTakeScreenshot {
16 | return YamlTakeScreenshot(
17 | path = path,
18 | )
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlToggleAirplaneMode.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlToggleAirplaneMode(
4 | val label: String? = null,
5 | val optional: Boolean = false,
6 | )
7 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlTravelCommand.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlTravelCommand(
4 | val points: List,
5 | val speed: Double? = null,
6 | val label: String? = null,
7 | val optional: Boolean = false,
8 | )
9 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/main/java/maestro/orchestra/yaml/YamlWaitForAnimationToEndCommand.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml
2 |
3 | data class YamlWaitForAnimationToEndCommand(
4 | val timeout: Long?,
5 | val label: String? = null,
6 | val optional: Boolean = false,
7 | )
8 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/java/maestro/orchestra/yaml/junit/YamlFile.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml.junit
2 |
3 | @Retention(AnnotationRetention.RUNTIME)
4 | @Target(AnnotationTarget.VALUE_PARAMETER)
5 | annotation class YamlFile(val name: String)
6 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/java/maestro/orchestra/yaml/junit/YamlResourceFile.kt:
--------------------------------------------------------------------------------
1 | package maestro.orchestra.yaml.junit
2 |
3 | import java.nio.file.Path
4 | import java.nio.file.Paths
5 |
6 | class YamlResourceFile(val name: String) {
7 | val path: Path get() {
8 | val resource = this::class.java.getResource("/YamlCommandReaderTest/${name}")!!
9 | return Paths.get(resource.toURI())
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/002_launchApp.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/003_launchApp_withClearState.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | clearState: true
5 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/008_config_unknownKeys.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | extra: True
3 | extraMap:
4 | keyA: valueB
5 | extraArray:
6 | - itemA
7 | ---
8 | - launchApp
9 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/017_launchApp_otherPackage.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp: com.other.app
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/018_backPress_string.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - back
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/019_scroll_string.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scroll
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/020_config_name.yaml:
--------------------------------------------------------------------------------
1 | name: Example Flow
2 | appId: com.example.app
3 | ---
4 | - launchApp
5 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/022_on_flow_start_complete.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - back
4 | onFlowComplete:
5 | - scroll
6 | ---
7 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/023_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-orchestra/src/test/resources/YamlCommandReaderTest/023_image.png
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/023_runScript_test.js:
--------------------------------------------------------------------------------
1 | const myNumber = 1 + 1;
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/025_killApp.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - killApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/027_waitToSettleTimeoutMs.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scrollUntilVisible:
4 | element:
5 | id: "maybe-later"
6 | waitToSettleTimeoutMs: 50
7 | direction: DOWN
8 | - swipe:
9 | start: 90%, 50%
10 | end: 10%, 50%
11 | waitToSettleTimeoutMs: 50
12 | - swipe:
13 | direction: LEFT
14 | waitToSettleTimeoutMs: 50
15 | - swipe:
16 | from:
17 | id: "feeditem_identifier"
18 | direction: LEFT
19 | waitToSettleTimeoutMs: 50
20 | - swipe:
21 | start: 100, 200
22 | end: 300, 400
23 | waitToSettleTimeoutMs: 50
24 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/028_command_descriptions.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: "maybe-later"
5 | label: "Scroll to find the maybe-later button"
6 |
7 | - inputText:
8 | text: ${username}
9 |
10 | - assertVisible:
11 | text: "Hello ${username}"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/YamlCommandReaderTest/flow.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-orchestra/src/test/resources/YamlCommandReaderTest/flow.zip
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/android/add_media_gif.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.android.apps.photos
2 | ---
3 | - addMedia:
4 | - "../assets/android_gif.gif"
5 | - launchApp:
6 | appId: com.google.android.apps.photos
7 | - runFlow:
8 | when:
9 | visible: Update Now
10 | commands:
11 | - tapOn:
12 | text: Update Now
13 | optional: true
14 | - back
15 | # assert that photo is taken
16 | - assertVisible: "Photo taken on.*"
17 | - tapOn: "Photo taken on.*"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/android/add_media_jpeg.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.android.apps.photos
2 | ---
3 | - addMedia:
4 | - "../assets/android_jpeg.jpeg"
5 | - launchApp:
6 | appId: com.google.android.apps.photos
7 | - runFlow:
8 | when:
9 | visible: Update Now
10 | commands:
11 | - tapOn:
12 | text: Update Now
13 | optional: true
14 | - back
15 | # assert that photo is taken
16 | - assertVisible: "Photo taken on.*"
17 | - tapOn: "Photo taken on.*"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/android/add_media_jpg.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.android.apps.photos
2 | ---
3 | - addMedia:
4 | - "../assets/android_jpg.jpg"
5 | - launchApp:
6 | appId: com.google.android.apps.photos
7 | - runFlow:
8 | when:
9 | visible: Update Now
10 | commands:
11 | - tapOn:
12 | text: Update Now
13 | optional: true
14 | - back
15 | # assert that photo is taken
16 | - tapOn: "Photo taken on.*"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/android/add_media_mp4.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.android.apps.photos
2 | ---
3 | - addMedia:
4 | - "../assets/sample_video.mp4"
5 | - launchApp:
6 | appId: com.google.android.apps.photos
7 | - runFlow:
8 | when:
9 | visible: Update Now
10 | commands:
11 | - tapOn:
12 | text: Update Now
13 | optional: true
14 | - back
15 | # assert that photo is taken
16 | - assertVisible: "Video taken on.*"
17 | - tapOn: "Video taken on.*"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/android/add_media_png.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.android.apps.photos
2 | ---
3 | - addMedia:
4 | - "../assets/android.png"
5 | - launchApp:
6 | appId: com.google.android.apps.photos
7 | - runFlow:
8 | when:
9 | visible: Update Now
10 | commands:
11 | - tapOn:
12 | text: Update Now
13 | optional: true
14 | - back
15 | # assert that photo is taken
16 | - assertVisible: "Photo taken on.*"
17 | - tapOn: "Photo taken on.*"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/ios/add_media_gif.yaml:
--------------------------------------------------------------------------------
1 | appId: com.apple.mobileslideshow
2 | ---
3 | - addMedia:
4 | - "./maestro-orchestra/src/test/resources/media/assets/android.gif"
5 | - launchApp
6 | - assertVisible: All Photos
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/ios/add_media_jpeg.yaml:
--------------------------------------------------------------------------------
1 | appId: com.apple.mobileslideshow
2 | ---
3 | - addMedia:
4 | - "../assets/android_jpeg.jpeg"
5 | - launchApp
6 | - assertVisible: All Photos
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/ios/add_media_jpg.yaml:
--------------------------------------------------------------------------------
1 | appId: com.apple.mobileslideshow
2 | ---
3 | - addMedia:
4 | - "./maestro-orchestra/src/test/resources/media/assets/android_jpg.jpg"
5 | - launchApp
6 | - assertVisible: All Photos
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/ios/add_media_mp4.yaml:
--------------------------------------------------------------------------------
1 | appId: com.apple.mobileslideshow
2 | ---
3 | - addMedia:
4 | - "./maestro-orchestra/src/test/resources/media/assets/sample_video.mp4"
5 | - launchApp
6 | - assertVisible: All Photos
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/ios/add_media_png.yaml:
--------------------------------------------------------------------------------
1 | appId: com.apple.mobileslideshow
2 | ---
3 | - addMedia:
4 | - "./maestro-orchestra/src/test/resources/media/assets/android.png"
5 | - launchApp
6 | - assertVisible: All Photos
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/media/ios/add_multiple_media.yaml:
--------------------------------------------------------------------------------
1 | appId: com.apple.mobileslideshow
2 | ---
3 | - addMedia:
4 | - "./maestro-orchestra/src/test/resources/media/assets/android.gif"
5 | - "./maestro-orchestra/src/test/resources/media/assets/android_jpeg.jpeg"
6 | - "./maestro-orchestra/src/test/resources/media/assets/sample_video.mp4"
7 | - "./maestro-orchestra/src/test/resources/media/assets/android.png"
8 | - launchApp
9 | - assertVisible: All Photos
10 | - tapOn:
11 | point: "16%,19%"
12 | - tapOn:
13 | point: "50%,19%"
14 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/.gitignore:
--------------------------------------------------------------------------------
1 | error.actual.txt
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/000_individual_file/flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/001_simple/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/001_simple/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/001_simple/notAFlow.txt:
--------------------------------------------------------------------------------
1 | This file is not a flow
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/002_subflows/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/002_subflows/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 | - runFlow: subflows/subflow.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/002_subflows/subflows/subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/003_include_tags/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | - someOtherTag
5 | ---
6 | - launchApp
7 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/003_include_tags/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - notIncluded
4 | ---
5 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/003_include_tags/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/004_exclude_tags/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | - someOtherTag
5 | ---
6 | - launchApp
7 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/004_exclude_tags/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - excluded
4 | - someOtherTag
5 | ---
6 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/004_exclude_tags/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/005_custom_include_pattern/config.yaml:
--------------------------------------------------------------------------------
1 | flows:
2 | - featureA/*
3 | - featureB/*
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/005_custom_include_pattern/featureA/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/005_custom_include_pattern/featureB/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/005_custom_include_pattern/featureC/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/005_custom_include_pattern/flowD.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/006_include_subfolders/config.yaml:
--------------------------------------------------------------------------------
1 | flows:
2 | - "**"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/006_include_subfolders/featureA/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/006_include_subfolders/featureB/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/006_include_subfolders/featureC/subfolder/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/006_include_subfolders/flowD.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/007_empty_config/config.yml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-orchestra/src/test/resources/workspaces/007_empty_config/config.yml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/007_empty_config/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/007_empty_config/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/008_literal_pattern/config.yaml:
--------------------------------------------------------------------------------
1 | flows: featureA/*
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/008_literal_pattern/featureA/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/008_literal_pattern/featureB/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/009_custom_config_fields/config.yml:
--------------------------------------------------------------------------------
1 | customField: Custom Value
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/009_custom_config_fields/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/009_custom_config_fields/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/config.yaml:
--------------------------------------------------------------------------------
1 | includeTags:
2 | - featureA
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | - featureA
5 | ---
6 | - launchApp
7 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/flowA_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - featureA
4 | ---
5 | - launchApp
6 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | - featureB
5 | ---
6 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | ---
5 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/flowD.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - notIncluded
4 | ---
5 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/010_global_include_tags/flowE.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/config.yaml:
--------------------------------------------------------------------------------
1 | excludeTags: notIncluded
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | - featureA
5 | ---
6 | - launchApp
7 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/flowA_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - featureA
4 | ---
5 | - launchApp
6 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | - featureB
5 | ---
6 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | ---
5 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/flowD.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - notIncluded
4 | ---
5 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/011_global_exclude_tags/flowE.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/012_local_deterministic_order/config.yaml:
--------------------------------------------------------------------------------
1 | local:
2 | deterministicOrder: true
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/012_local_deterministic_order/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/012_local_deterministic_order/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/012_local_deterministic_order/flowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/013_execution_order/config.yaml:
--------------------------------------------------------------------------------
1 | executionOrder:
2 | flowsOrder:
3 | - flowB
4 | - flowC
5 | - flowD
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/013_execution_order/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/013_execution_order/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/013_execution_order/flowCWithCustomName.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | name: flowC
3 | ---
4 | - launchApp
5 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/013_execution_order/flowD.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/014_config_not_null/config.yaml:
--------------------------------------------------------------------------------
1 | includeTags:
2 | - included
3 | - excluded
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/014_config_not_null/config/another_config.yaml:
--------------------------------------------------------------------------------
1 | includeTags:
2 | - included
3 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/014_config_not_null/flowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - included
4 | ---
5 | - launchApp
6 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/014_config_not_null/flowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags:
3 | - excluded
4 | ---
5 | - launchApp
6 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e000_flow_path_does_not_exist/error.txt:
--------------------------------------------------------------------------------
1 | Flow path does not exist: /tmp/WorkspaceExecutionPlannerErrorsTest_workspace/workspace
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e001_directory_does_not_contain_flow_files/error.txt:
--------------------------------------------------------------------------------
1 | Flow directories do not contain any Flow files: /tmp/WorkspaceExecutionPlannerErrorsTest_workspace/workspace
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e001_directory_does_not_contain_flow_files/workspace/dummy:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-orchestra/src/test/resources/workspaces/e001_directory_does_not_contain_flow_files/workspace/dummy
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e002_top_level_directory_does_not_contain_flow_files/error.txt:
--------------------------------------------------------------------------------
1 | Top-level directories do not contain any Flows: /tmp/WorkspaceExecutionPlannerErrorsTest_workspace/workspace
2 | To configure Maestro to run Flows in subdirectories, check out the following resources:
3 | * https://maestro.mobile.dev/cli/test-suites-and-reports#inclusion-patterns
4 | * https://blog.mobile.dev/maestro-best-practices-structuring-your-test-suite-54ec390c5c82
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e002_top_level_directory_does_not_contain_flow_files/workspace/subdir/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e003_flow_inclusion_pattern_does_not_match_any_flow_files/error.txt:
--------------------------------------------------------------------------------
1 | Flow inclusion pattern(s) did not match any Flow files:
2 | - FlowA.yaml
3 | - FlowB.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e003_flow_inclusion_pattern_does_not_match_any_flow_files/workspace/FlowC.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e003_flow_inclusion_pattern_does_not_match_any_flow_files/workspace/config.yaml:
--------------------------------------------------------------------------------
1 | flows:
2 | - FlowA.yaml
3 | - FlowB.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e004_tags_config_does_not_match_any_flow_files/error.txt:
--------------------------------------------------------------------------------
1 | Include / Exclude tags did not match any Flows:
2 |
3 | Include Tags:
4 | - parameterInclude
5 | - configInclude
6 |
7 | Exclude Tags:
8 | - parameterExclude
9 | - configExclude
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e004_tags_config_does_not_match_any_flow_files/excludeTags.txt:
--------------------------------------------------------------------------------
1 | parameterExclude
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e004_tags_config_does_not_match_any_flow_files/includeTags.txt:
--------------------------------------------------------------------------------
1 | parameterInclude
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e004_tags_config_does_not_match_any_flow_files/workspace/ConfigExclude.yaml:
--------------------------------------------------------------------------------
1 | tags:
2 | - configInclude
3 | - configExclude
4 | appId: com.example
5 | ---
6 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e004_tags_config_does_not_match_any_flow_files/workspace/ParameterExclude.yaml:
--------------------------------------------------------------------------------
1 | tags:
2 | - parameterInclude
3 | - parameterExclude
4 | appId: com.example
5 | ---
6 | - launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e004_tags_config_does_not_match_any_flow_files/workspace/config.yaml:
--------------------------------------------------------------------------------
1 | includeTags:
2 | - configInclude
3 | excludeTags:
4 | - configExclude
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e005_single_flow_does_not_exist/error.txt:
--------------------------------------------------------------------------------
1 | Flow path does not exist: /tmp/WorkspaceExecutionPlannerErrorsTest_workspace/workspace/Flow.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e005_single_flow_does_not_exist/singleFlow.txt:
--------------------------------------------------------------------------------
1 | Flow.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e006_single_flow_invalid_string_command/singleFlow.txt:
--------------------------------------------------------------------------------
1 | Flow.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e006_single_flow_invalid_string_command/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - invalidCommand
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e007_single_flow_malformatted_command/singleFlow.txt:
--------------------------------------------------------------------------------
1 | Flow.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e007_single_flow_malformatted_command/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - launchApp:
4 | invalidOption: true
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e008_subflow_invalid_string_command/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - runFlow: ./subflow/SubFlow.yaml
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e008_subflow_invalid_string_command/workspace/subflow/SubFlow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - invalidCommand
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e009_nested_subflow_invalid_string_command/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - runFlow: ./subflow/SubFlowA.yaml
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e009_nested_subflow_invalid_string_command/workspace/subflow/SubFlowA.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - runFlow: ./SubFlowB.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e009_nested_subflow_invalid_string_command/workspace/subflow/SubFlowB.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - invalidCommand
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e010_missing_config_section/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | - launchApp
2 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e011_missing_dashes/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | launchApp
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e012_invalid_subflow_path/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - runFlow: invalidpath.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e013_invalid_media_file/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - addMedia:
4 | - "./assets/invalid_android.png"
5 | - launchApp
6 | - tapOn: "Add to Cart"
7 | - assertVisible: "Added"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e013_invalid_media_file/workspace/assets/android.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-orchestra/src/test/resources/workspaces/e013_invalid_media_file/workspace/assets/android.png
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e014_invalid_media_file_outside/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example
2 | ---
3 | - addMedia:
4 | - "../../e013_invalid_media_file/workspace/assets/android.png"
5 | - launchApp
6 | - tapOn: "Add to Cart"
7 | - assertVisible: "Added"
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e015_array_command/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - [foo, bar, baz]
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e016_config_invalid_command_in_onFlowStart/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - inp
4 | ---
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e017_config_invalid_tags/error.txt:
--------------------------------------------------------------------------------
1 | > Incorrect Format: tags
2 |
3 | /tmp/WorkspaceExecutionPlannerErrorsTest_workspace/workspace/Flow.yaml:2
4 | ╭──────────────────────────────────────╮
5 | │ 1 | appId: com.example.app │
6 | │ 2 | tags: foo, bar │
7 | │ ^ │
8 | │ ╭──────────────────────────────────╮ │
9 | │ │ The format for tags is incorrect │ │
10 | │ ╰──────────────────────────────────╯ │
11 | │ 3 | --- │
12 | │ 4 | │
13 | ╰──────────────────────────────────────╯
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e017_config_invalid_tags/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | tags: foo, bar
3 | ---
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e018_config_missing_appId/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | name: MyFlow
2 | ---
3 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e019_invalid_swipe_direction/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - swipe:
4 | direction: diagonal
5 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e020_missing_command_options/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn
4 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e021_multiple_command_names/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: foo
4 | inputText: bar
5 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e022_top_level_option/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: foo
4 | optional: true
5 |
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e023_empty/workspace/Flow.yaml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-orchestra/src/test/resources/workspaces/e023_empty/workspace/Flow.yaml
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e023_empty_commands/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
--------------------------------------------------------------------------------
/maestro-orchestra/src/test/resources/workspaces/e023_launchApp_empty_string/workspace/Flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 |
--------------------------------------------------------------------------------
/maestro-proto/build.gradle.kts:
--------------------------------------------------------------------------------
1 | import com.vanniktech.maven.publish.SonatypeHost
2 |
3 | plugins {
4 | id("maven-publish")
5 | java
6 | alias(libs.plugins.mavenPublish)
7 | }
8 |
9 | mavenPublishing {
10 | publishToMavenCentral(SonatypeHost.S01)
11 | }
12 |
13 | java {
14 | sourceCompatibility = JavaVersion.VERSION_1_8
15 | targetCompatibility = JavaVersion.VERSION_1_8
16 | }
17 |
18 | tasks.named("jar") {
19 | from("src/main/proto/maestro_android.proto")
20 | }
21 |
--------------------------------------------------------------------------------
/maestro-proto/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Orchestra Proto
2 | POM_ARTIFACT_ID=maestro-proto
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/maestro-studio/server/.gitignore:
--------------------------------------------------------------------------------
1 | /src/main/resources/web
--------------------------------------------------------------------------------
/maestro-studio/server/src/main/java/maestro/studio/HttpException.kt:
--------------------------------------------------------------------------------
1 | package maestro.studio
2 |
3 | import io.ktor.http.HttpStatusCode
4 |
5 | data class HttpException(
6 | val statusCode: HttpStatusCode,
7 | val errorMessage: String,
8 | ) : RuntimeException("$statusCode: $errorMessage")
9 |
--------------------------------------------------------------------------------
/maestro-studio/web/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 | yarn.lock
--------------------------------------------------------------------------------
/maestro-studio/web/.npmrc:
--------------------------------------------------------------------------------
1 | legacy-peer-deps=true
2 |
--------------------------------------------------------------------------------
/maestro-studio/web/.nvmrc:
--------------------------------------------------------------------------------
1 | v16.14.0
2 |
--------------------------------------------------------------------------------
/maestro-studio/web/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "stories": [
3 | "../src/**/*.stories.mdx",
4 | "../src/**/*.stories.@(js|jsx|ts|tsx)"
5 | ],
6 | "addons": [
7 | "@storybook/addon-links",
8 | "@storybook/addon-essentials",
9 | "@storybook/addon-interactions",
10 | "@storybook/preset-create-react-app",
11 | "storybook-addon-react-router-v6"
12 | ],
13 | "framework": "@storybook/react",
14 | "core": {
15 | "builder": "@storybook/builder-webpack5"
16 | },
17 | "staticDirs": ['../public', '../storybook-assets'],
18 | }
--------------------------------------------------------------------------------
/maestro-studio/web/.storybook/preview.js:
--------------------------------------------------------------------------------
1 | import { withRouter } from "storybook-addon-react-router-v6";
2 |
3 | import "../src/style/index.css";
4 | import { installMocks } from "../src/api/mocks";
5 |
6 | installMocks();
7 |
8 | export const decorators = [withRouter];
9 |
10 | export const parameters = {
11 | actions: { argTypesRegex: "^on[A-Z].*" },
12 | controls: {
13 | matchers: {
14 | color: /(background|color)$/i,
15 | date: /Date$/,
16 | },
17 | },
18 | };
19 |
--------------------------------------------------------------------------------
/maestro-studio/web/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | }
7 |
--------------------------------------------------------------------------------
/maestro-studio/web/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-studio/web/public/favicon.ico
--------------------------------------------------------------------------------
/maestro-studio/web/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Maestro Studio
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/components/design-system/keyboard-key.tsx:
--------------------------------------------------------------------------------
1 | import { ReactNode } from "react";
2 |
3 | interface KeyboardKeyProps {
4 | children: ReactNode;
5 | }
6 |
7 | const KeyboardKey = ({ children }: KeyboardKeyProps) => {
8 | return (
9 |
10 | {children}
11 |
12 | );
13 | };
14 |
15 | export default KeyboardKey;
16 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/components/design-system/utils/functions.tsx:
--------------------------------------------------------------------------------
1 | export const checkImageUrl = async (imageUrl: string) => {
2 | try {
3 | const response = await fetch(imageUrl);
4 | if (
5 | response.ok &&
6 | response.headers.get("content-type")?.startsWith("image/")
7 | ) {
8 | return true;
9 | } else {
10 | return false;
11 | }
12 | } catch (error) {
13 | return false;
14 | }
15 | };
16 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { BrowserRouter as Router } from "react-router-dom";
4 | import "./style/index.css";
5 | import App from "./App";
6 |
7 | const root = ReactDOM.createRoot(
8 | document.getElementById("root") as HTMLElement
9 | );
10 | root.render(
11 |
12 |
13 |
14 |
15 |
16 | );
17 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/pages/InteractPage.tsx:
--------------------------------------------------------------------------------
1 | import { DeviceProvider } from "../context/DeviceContext";
2 | import InteractPageLayout from "../components/interact/InteractPageLayout";
3 | import { ReplProvider } from '../context/ReplContext';
4 |
5 | export default function InteractPage() {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | );
13 | }
14 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/storybook/App.stories.tsx:
--------------------------------------------------------------------------------
1 | import App from "../App";
2 |
3 | export default {
4 | title: "App",
5 | parameters: {
6 | layout: "fullscreen",
7 | },
8 | };
9 |
10 | export const MainStory = () => {
11 | return ;
12 | };
13 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/storybook/ConfirmationDialog.stories.tsx:
--------------------------------------------------------------------------------
1 | import { ConfirmationDialog } from "../components/common/ConfirmationDialog";
2 |
3 | export default {
4 | title: "ConfirmationDialog",
5 | };
6 |
7 | export const Main = () => {
8 | return (
9 |
10 |
13 |
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/storybook/Header.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import Header from "../components/common/Header";
3 |
4 | export default {
5 | title: "Header",
6 | };
7 |
8 | export const Main = () => {
9 | return ;
10 | };
11 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/storybook/InteractPage.stories.tsx:
--------------------------------------------------------------------------------
1 | import InteractPage from "../pages/InteractPage";
2 |
3 | export default {
4 | title: "InteractPage",
5 | parameters: {
6 | layout: "fullscreen",
7 | },
8 | };
9 |
10 | export const Main = () => {
11 | return ;
12 | };
13 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/storybook/ReplView.stories.tsx:
--------------------------------------------------------------------------------
1 | import ReplView from "../components/commands/ReplView";
2 | import { DeviceProvider } from "../context/DeviceContext";
3 |
4 | export default {
5 | title: "ReplView",
6 | };
7 |
8 | export const Main = () => {
9 | return (
10 |
11 |
12 |
13 | );
14 | };
15 |
--------------------------------------------------------------------------------
/maestro-studio/web/src/style/fonts/JetBrainsMono-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-studio/web/src/style/fonts/JetBrainsMono-Italic.ttf
--------------------------------------------------------------------------------
/maestro-studio/web/src/style/fonts/JetBrainsMono.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-studio/web/src/style/fonts/JetBrainsMono.ttf
--------------------------------------------------------------------------------
/maestro-studio/web/storybook-assets/sample-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-studio/web/storybook-assets/sample-screenshot.png
--------------------------------------------------------------------------------
/maestro-studio/web/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "esModuleInterop": true,
8 | "allowSyntheticDefaultImports": true,
9 | "strict": true,
10 | "forceConsistentCasingInFileNames": true,
11 | "noFallthroughCasesInSwitch": true,
12 | "module": "esnext",
13 | "moduleResolution": "node",
14 | "resolveJsonModule": true,
15 | "isolatedModules": true,
16 | "noEmit": true,
17 | "jsx": "react-jsx"
18 | },
19 | "include": ["src"]
20 | }
21 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/001_assert_visible_by_id.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | id: "element_id"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/002_assert_visible_by_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | text: "Element Text"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/003_assert_visible_by_size.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | width: 100
5 | height: 100
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/004_assert_no_visible_element_with_id.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | id: "element_id"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/005_assert_no_visible_element_with_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | text: "Element Text"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/006_assert_no_visible_element_with_size.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | width: 100
5 | height: 100
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/007_assert_visible_by_size_with_tolerance.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | width: 100
5 | height: 100
6 | tolerance: 1
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/008_tap_on_element.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: ".*button.*"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/009_skip_optional_elements.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: "Optional Element"
5 | optional: true
6 | - assertVisible:
7 | text: "Non Optional"
8 | optional: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/010_scroll.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scroll
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/011_back_press.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - back
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/012_input_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: "Hello World"
4 | - inputText: user@example.com
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/013_launch_app.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/014_tap_on_point.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | point: 100,200
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/016_multiline_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: "Hello world.*"
5 | retryTapIfNoChange: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/017_swipe.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - swipe:
4 | start: 100,500
5 | end: 100,200
6 | duration: 3000
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/018_contains_child.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | retryTapIfNoChange: false
5 | containsChild:
6 | text: "Child"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/019_dont_wait_for_visibility.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: "Button"
5 | retryTapIfNoChange: false
6 | waitUntilVisible: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/020_parse_config.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/021_launch_app_with_clear_state.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | clearState: true
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/022_launch_app_that_is_not_installed.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.nonexistent
2 | ---
3 | - launchApp:
4 | clearState: true
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/026_assert_not_visible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertNotVisible:
4 | id: "element_id"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/027_open_link.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - openLink: https://example.com
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/029_long_press_on_element.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - longPressOn:
4 | text: ".*button.*"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/030_long_press_on_point.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - longPressOn:
4 | point: 100,200
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/031_traits.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | traits: text
5 | retryTapIfNoChange: false
6 | - tapOn:
7 | traits: square
8 | retryTapIfNoChange: false
9 | - tapOn:
10 | traits: long-text
11 | retryTapIfNoChange: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/032_element_index.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: Item.*
5 | index: 0
6 | retryTapIfNoChange: false
7 | - tapOn:
8 | text: Item.*
9 | index: ${0 + 1}
10 | retryTapIfNoChange: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/033_int_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: 2022
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/035_refresh_position_ignore_duplicates.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | below: Item
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/036_erase_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: Hello World
4 | - eraseText: 6
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/037_unicode_input.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: Tést inpüt
4 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/038_partial_id.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | id: "keyboard_area"
5 | retryTapIfNoChange: false
6 | - tapOn:
7 | id: ".*keyboard_area"
8 | retryTapIfNoChange: false
9 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/039_hide_keyboard.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - hideKeyboard
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/040_escape_regex.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: \+123456
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/041_take_screenshot.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - takeScreenshot: ${MAESTRO_FILENAME}_with_filename
4 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/042_extended_wait.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | TIMEOUT: 1000
4 | ---
5 | - extendedWaitUntil:
6 | visible: Item
7 | timeout: ${TIMEOUT}
8 | - extendedWaitUntil:
9 | notVisible: Another item
10 | timeout: 1000
11 | - extendedWaitUntil:
12 | visible: Item
13 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/043_stop_app.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - stopApp
4 | - stopApp: another.app
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/044_clear_state.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - clearState
4 | - clearState: another.app
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/045_clear_keychain.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - clearKeychain
4 | - launchApp:
5 | appId: com.example.app
6 | clearKeychain: true
7 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/046_run_flow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - runFlow: 013_launch_app.yaml
4 | - tapOn: Primary button
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/047_run_flow_nested.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - runFlow: 046_run_flow.yaml
4 | - tapOn: Secondary Button
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/048_tapOn_clickable.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: Button
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/049_run_flow_conditionally.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - runFlow:
4 | when:
5 | visible: Not Clicked
6 | file: 008_tap_on_element.yaml
7 | - assertVisible: Clicked
8 | - runFlow:
9 | when:
10 | visible: ${NOT_CLICKED}
11 | file: 008_tap_on_element.yaml
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/051_set_location.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | appId: com.example.app
5 | - setLocation:
6 | latitude: 12.5266
7 | longitude: 78.2150
8 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/052_text_random.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputRandomText
4 | - inputRandomNumber
5 | - inputRandomText:
6 | length: 5
7 | - inputRandomNumber:
8 | length: 5
9 | - inputRandomEmail
10 | - inputRandomPersonName
11 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/053_repeat_times.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - repeat:
4 | times: 3
5 | commands:
6 | - tapOn: Button
7 | - assertVisible: "3"
8 | - evalScript: ${output.list = [1, 2, 3]}
9 | - repeat:
10 | times: ${output.list.length}
11 | commands:
12 | - tapOn: Button
13 | - assertVisible: "6"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/054_enabled.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - assertVisible:
4 | text: Button
5 | enabled: true
6 | - tapOn: Button
7 | - assertNotVisible:
8 | text: Button
9 | enabled: true
10 | - assertVisible:
11 | text: Button
12 | enabled: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/055_compare_regex.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: (Secondary button)
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/056_ignore_error.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: Non existent text
4 | - repeat:
5 | times: 2
6 | commands:
7 | - tapOn: Non existent text
8 | - tapOn: Button
9 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/057_runFlow_env.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - runFlow:
4 | file: 057_subflow.yaml
5 | env:
6 | INNER_ENV: Inner Parameter
7 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/057_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: ${INNER_ENV}
4 | - inputText: ${OUTER_ENV}
5 | - runFlow:
6 | file: 057_subflow_override.yaml
7 | env:
8 | INNER_ENV: Overriden Parameter
9 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/057_subflow_override.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: ${INNER_ENV}
4 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/058_inline_env.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | INLINE_ENV: Inline Parameter
4 | ---
5 | - inputText: ${INLINE_ENV}
6 | - runFlow: 058_subflow.yaml
7 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/058_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | INLINE_ENV: Overriden Parameter
4 | ---
5 | - inputText: ${INLINE_ENV}
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/059_directional_swipe_command.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - swipe:
4 | direction: RIGHT
5 | duration: 500
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/060_pass_env_to_env.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | PARAM_INLINE: ${PARAM}
4 | PARAM: ${PARAM}
5 | ---
6 | - runFlow:
7 | file: 060_subflow.yaml
8 | env:
9 | PARAM_FLOW: ${PARAM_INLINE}
10 | PARAM: ${PARAM}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/060_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: ${PARAM_FLOW}
4 | - inputText: ${PARAM_INLINE}
5 | - inputText: ${PARAM}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/061_launchApp_withoutStopping.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | stopApp: false
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/062_copy_paste_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - copyTextFrom:
4 | id: "myId"
5 | - pasteText
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/063_js_injection.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | A: 1
4 | B: 2
5 | ---
6 | - inputText: ${A}
7 | - inputText: ${B}
8 | - inputText: ${A + B}
9 | - inputText: ${parseInt(A) + parseInt(B)}
10 | - inputText: \${A} \${B} ${A} ${B}
11 |
12 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/064_script.js:
--------------------------------------------------------------------------------
1 | output.sharedResult = 'Main'
2 | output.mainFlow = {
3 | result: 'Main'
4 | }
5 | output.fileName = MAESTRO_FILENAME
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/064_script_alt.js:
--------------------------------------------------------------------------------
1 | output.sharedResult = 'Sub'
2 | output.subFlow = {
3 | result: 'Sub'
4 | }
5 | output.subFlowFileName = MAESTRO_FILENAME
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/064_script_with_args.js:
--------------------------------------------------------------------------------
1 | output.resultWithParameters = 'Hello, ' + parameter + '!'
2 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/064_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - runScript: 064_script_alt.js
4 | - inputText: ${output.sharedResult}
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/065_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: ${name}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/066_copyText_jsVar.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - copyTextFrom:
4 | id: Field
5 | - inputText: ${'Hello, ' + maestro.copiedText}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/067_assertTrue_fail.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertTrue: ${1-1}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/067_assertTrue_pass.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertTrue: ${1+1}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/068_erase_all_text.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - inputText: Hello World
4 | - eraseText
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/069_wait_for_animation_to_end.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - waitForAnimationToEnd:
4 | timeout: 500
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/070_evalScript.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - evalScript: ${output.number = 1 + 1}
4 | - evalScript: "${output.text = 'Result is: ' + output.number}"
5 | - inputText: ${output.number}
6 | - inputText: ${output.text}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/071_tapOnRelativePoint.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | QUARTER: 25%
4 | QUARTER_FLOAT: 0.25
5 | ---
6 | - tapOn:
7 | point: 0%,0%
8 | - tapOn:
9 | point: 100%,100%
10 | - tapOn:
11 | point: 50%,50%
12 | - tapOn:
13 | point: ${QUARTER},${QUARTER}
14 | - tapOn:
15 | point: ${relativePoint(QUARTER_FLOAT,QUARTER_FLOAT)}
16 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/072_searchDepthFirst.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: "Element"
4 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/073_handle_linebreaks.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn: Hello World
4 | - tapOn: Hello\nWorld
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/074_directional_swipe_element.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - swipe:
4 | direction: RIGHT
5 | from:
6 | text: "swiping element"
7 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/075_repeat_while.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - repeat:
4 | while:
5 | notVisible: "Value 3"
6 | commands:
7 | - tapOn: Button
8 | - assertVisible: "Value 3"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/076_optional_assertion.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scrollUntilVisible:
4 | timeout: 1
5 | element:
6 | id: "not_found"
7 | optional: true
8 | - assertTrue:
9 | condition: "false"
10 | optional: true
11 | - extendedWaitUntil:
12 | visible:
13 | id: "not_found"
14 | timeout: 1
15 | optional: true
16 | - assertVisible:
17 | text: "Button"
18 | optional: true
19 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/077_env_special_characters.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | INNER: |
4 | !@#$&*()_+{}|:"<>?[]\\;',./
5 | ---
6 | - inputText: ${OUTER}
7 | - inputText: ${INNER}
8 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/078_swipe_relative.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - swipe:
4 | start: "50%,30%"
5 | end: "50%,60%"
6 | duration: 3000
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/079_scroll_until_visible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scrollUntilVisible:
4 | element:
5 | text: "Test"
6 | speed: 100
7 | visibilityPercentage: 100
8 | direction: DOWN
9 | timeout: 10
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/080_hierarchy_pruning_assert_visible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | id: "visible_1"
5 | - assertVisible:
6 | id: "visible_2"
7 | - assertVisible:
8 | id: "visible_3"
9 | - assertVisible:
10 | id: "visible_4"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/081_hierarchy_pruning_assert_not_visible.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertNotVisible:
4 | id: "not_visible_1"
5 | - assertNotVisible:
6 | id: "not_visible_2"
7 | - assertNotVisible:
8 | id: "not_visible_3"
9 | - assertNotVisible:
10 | id: "not_visible_4"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/082_repeat_while_true.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - evalScript: ${output.value = 0}
4 | - repeat:
5 | while:
6 | true: ${output.value < 3}
7 | commands:
8 | - evalScript: ${output.value = output.value + 1}
9 | - inputText: ${output.value}
10 | - tapOn: Button
11 | - assertVisible: "Value 3"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/084_open_browser.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - openLink:
4 | link: https://example.com
5 | browser: true
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/085_open_link_auto_verify.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - openLink:
4 | link: https://example.com
5 | autoVerify: true
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/086_launchApp_sets_all_permissions_to_allow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp
4 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/087_launchApp_with_all_permissions_to_deny.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | permissions: { all: deny }
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/088_launchApp_with_all_permissions_to_deny_and_notification_to_allow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | permissions:
5 | all: deny
6 | notifications: allow
7 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/089_launchApp_with_sms_permission_group_to_allow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - launchApp:
4 | permissions:
5 | sms: allow
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/090_travel.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - travel:
4 | points:
5 | - 0.0,0.0
6 | - 0.1,0.0
7 | - 0.1,0.1
8 | - 0.0,0.1
9 | speed: 7900 # 7.9 km/s aka orbital velocity
10 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/091_assert_visible_by_index.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | text: Item
5 | index: 0
6 | - assertVisible:
7 | text: Item
8 | index: 1
9 | - assertNotVisible:
10 | text: Item
11 | index: 2
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/092_log_messages.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - evalScript: ${console.log('Log from evalScript')}
4 | - runScript: 092_script.js
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/092_script.js:
--------------------------------------------------------------------------------
1 | console.log('Log from runScript')
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/093_js_default_value.yaml:
--------------------------------------------------------------------------------
1 | appId: ${APP_ID}
2 | env:
3 | APP_ID: ${APP_ID || 'com.example.default'}
4 | ---
5 | - launchApp
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/094_runFlow_inline.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - runFlow:
4 | env:
5 | INNER_ENV: Inner Parameter
6 | commands:
7 | - inputText: ${INNER_ENV}
8 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/095_launch_arguments.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | env:
3 | ARGUMENT_B: argumentB
4 | BOOL_VALUE: true
5 | ---
6 | - launchApp:
7 | arguments:
8 | argumentA: true
9 | ${ARGUMENT_B}: 4
10 | argumentC: 4.0
11 | argumentD: "Hello String Value ${BOOL_VALUE}"
12 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/096_platform_condition.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - runFlow:
4 | when:
5 | platform: iOS
6 | commands:
7 | - inputText: Hello iOS
8 | - runFlow:
9 | when:
10 | platform: ios
11 | commands:
12 | - inputText: Hello ios
13 | - runFlow:
14 | when:
15 | platform: Android
16 | commands:
17 | - inputText: Hello Android
18 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/097_contains_descendants.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | id: id1
5 | containsDescendants:
6 | - text: "Child 1"
7 | - text: "Child 2"
8 | enabled: false
9 | - assertVisible:
10 | id: id2
11 | containsDescendants:
12 | - text: "Child 1"
13 | - assertNotVisible:
14 | id: id1
15 | containsDescendants:
16 | - text: "Child 1"
17 | - text: "Child 3"
18 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/098_runScript.js:
--------------------------------------------------------------------------------
1 | console.log('Log from runScript')
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/098_runscript_conditionals.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - runScript:
4 | when:
5 | visible: Click me
6 | file: 098_runScript.js
7 | - tapOn: Click me
8 | - assertVisible: Clicked
9 | - assertNotVisible: Click me
10 | - runScript:
11 | when:
12 | visible: Not Clicked
13 | file: 098_runScript.js
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/099_screen_recording.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - startRecording: ${MAESTRO_FILENAME}
4 | - stopRecording
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/100_tapOn_multiple_times.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - tapOn:
4 | text: Button
5 | repeat: 3
6 | delay: 1
7 | retryTapIfNoChange: false
8 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/101_doubleTapOn.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - doubleTapOn:
4 | text: Button
5 | retryTapIfNoChange: false
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/102_graaljs.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | jsEngine: graaljs
3 | ---
4 | # Note: The ?? operator is an example of an ES2020 feature and is not supported by RhinoJS
5 | - inputText: ${null ?? 'foo'}
6 | - runFlow: 102_graaljs_subflow.yaml
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/102_graaljs_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | # Still uses graaljs in a subflow based on the top-level flow config
4 | - inputText: ${null ?? 'bar'}
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/103_on_flow_start_complete_hooks.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - runScript: "103_setup.js"
4 | - inputText: "test1"
5 | onFlowComplete:
6 | - runScript: "103_teardown.js"
7 | - inputText: "test2"
8 | ---
9 | - tapOn:
10 | point: 100,200
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/103_setup.js:
--------------------------------------------------------------------------------
1 | console.log('setup');
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/103_teardown.js:
--------------------------------------------------------------------------------
1 | console.log('teardown');
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/104_on_flow_start_complete_hooks_flow_failed.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - inputText: "test1"
4 | onFlowComplete:
5 | - inputText: "test2"
6 | ---
7 | - assertVisible:
8 | id: "element_id"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/105_on_flow_start_complete_when_js_output_set.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - runScript: "105_setup.js"
4 | onFlowComplete:
5 | - runScript: "105_teardown.js"
6 | - evalScript: ${ console.log(output.teardown_result); }
7 | ---
8 | - evalScript: ${ console.log(output.setup_result); }
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/105_setup.js:
--------------------------------------------------------------------------------
1 | output.setup_result = 'setup';
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/105_teardown.js:
--------------------------------------------------------------------------------
1 | output.teardown_result = 'teardown';
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/106_on_flow_start_complete_when_js_output_set_subflows.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - runFlow: "106_subflow.yaml"
4 | - evalScript: ${ console.log(output.setup_subflow_result); }
5 | - evalScript: ${ console.log(output.teardown_subflow_result); }
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/106_setup.js:
--------------------------------------------------------------------------------
1 | output.setup_subflow_result = 'setup subflow';
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/106_subflow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - runScript: "106_setup.js"
4 | onFlowComplete:
5 | - runScript: "106_teardown.js"
6 | ---
7 | - evalScript: ${ console.log('subflow'); }
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/106_teardown.js:
--------------------------------------------------------------------------------
1 | output.teardown_subflow_result = 'teardown subflow';
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/107_define_variables_command_before_hooks.yaml:
--------------------------------------------------------------------------------
1 | appId: ${MAESTRO_APP_ID}
2 | env:
3 | MAESTRO_APP_ID: com.example.app
4 | onFlowStart:
5 | - launchApp:
6 | appId: ${MAESTRO_APP_ID}
7 | ---
8 | - evalScript: ${ console.log(MAESTRO_APP_ID); }
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/108_failed_start_hook.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - evalScript: ${ console.log('on start'); }
4 | - assertTrue: ${1-1}
5 | onFlowComplete:
6 | - evalScript: ${ console.log('on complete'); }
7 | ---
8 | - evalScript: ${ console.log('main flow'); }
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/109_failed_complete_hook.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | onFlowStart:
3 | - evalScript: ${ console.log('on start'); }
4 | onFlowComplete:
5 | - evalScript: ${ console.log('on complete'); }
6 | - assertTrue: ${1-1}
7 | ---
8 | - evalScript: ${ console.log('main flow'); }
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/110_add_media_device.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.id
2 | ---
3 | - addMedia:
4 | - "./media/abc.png"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/111_add_multiple_media.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.id
2 | ---
3 | - addMedia:
4 | - "./media/abc.png"
5 | - "./media/abc.png"
6 | - "./media/abc.png"
7 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/112_scroll_until_visible_center.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scrollUntilVisible:
4 | centerElement: true
5 | element:
6 | text: "Test"
7 | speed: 100
8 | visibilityPercentage: 100
9 | direction: DOWN
10 | timeout: 10
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/113_tap_on_element_settle_timeout.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: "The time is.*"
5 | waitToSettleTimeoutMs: 100
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/114_child_of_selector.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - assertVisible:
4 | text: "child_id"
5 | childOf:
6 | text: "parent_id_1"
7 | - assertNotVisible:
8 | text: "child_id"
9 | childOf:
10 | text: "parent_id_3"
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/115_airplane_mode.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - setAirplaneMode: enabled
4 | - setAirplaneMode: disabled
5 | - toggleAirplaneMode
6 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/116_kill_app.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - killApp
4 | - killApp: another.app
5 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/117_scroll_until_visible_speed.js:
--------------------------------------------------------------------------------
1 | output.speed = {
2 | slow: 40
3 | }
4 |
5 | output.timeout = {
6 | slow: 20000
7 | }
8 |
9 | output.element = {
10 | id: "maestro"
11 | }
12 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/117_scroll_until_visible_speed.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - runScript: 117_scroll_until_visible_speed.js
4 | - scrollUntilVisible:
5 | element:
6 | id: ${output.element.id}
7 | direction: DOWN
8 | speed: ${output.speed.slow}
9 | timeout: ${output.timeout.slow}
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/118_scroll_until_visible_negative.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - scrollUntilVisible:
4 | element:
5 | id: "maestro"
6 | direction: DOWN
7 | speed: 110
8 | timeout: -200
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/119_retry_commands.yaml:
--------------------------------------------------------------------------------
1 | appId: com.other.app
2 | ---
3 | - retry:
4 | maxRetries: 3
5 | commands:
6 | - scroll
7 | - tapOn:
8 | text: Button
9 | waitToSettleTimeoutMs: 40
10 | - scroll
11 |
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/120_tap_on_element_retryTapIfNoChange.yaml:
--------------------------------------------------------------------------------
1 | appId: com.example.app
2 | ---
3 | - tapOn:
4 | text: ".*button.*"
5 | retryTapIfNoChange: true
--------------------------------------------------------------------------------
/maestro-test/src/test/resources/media/abc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mobile-dev-inc/Maestro/9092b081c35cb420a9030cbecf282006696dd9c2/maestro-test/src/test/resources/media/abc.png
--------------------------------------------------------------------------------
/maestro-utils/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro Utils
2 | POM_ARTIFACT_ID=maestro-utils
3 | POM_PACKAGING=jar
4 |
--------------------------------------------------------------------------------
/maestro-utils/src/main/kotlin/Collections.kt:
--------------------------------------------------------------------------------
1 | package maestro.utils
2 |
3 | import java.io.File
4 | import java.nio.file.Path
5 | import kotlin.io.path.isRegularFile
6 |
7 | val Collection.isSingleFile get() =
8 | size == 1 && first().isDirectory().not()
9 |
10 | val Collection.isRegularFile get() =
11 | size == 1 && first().isRegularFile()
12 |
--------------------------------------------------------------------------------
/maestro-utils/src/main/kotlin/DepthTracker.kt:
--------------------------------------------------------------------------------
1 | package maestro.utils
2 |
3 | object DepthTracker {
4 |
5 | private var currentDepth: Int = 0
6 | private var maxDepth: Int = 0
7 |
8 | fun trackDepth(depth: Int) {
9 | currentDepth = depth
10 | if (currentDepth > maxDepth) {
11 | maxDepth = currentDepth
12 | }
13 | }
14 |
15 | fun getMaxDepth(): Int = maxDepth
16 | }
--------------------------------------------------------------------------------
/maestro-utils/src/main/kotlin/Insight.kt:
--------------------------------------------------------------------------------
1 | package maestro.utils
2 |
3 | object CliInsights: Insights {
4 |
5 | private var insight: Insight = Insight("", Insight.Level.NONE)
6 | private val listeners = mutableListOf<(Insight) -> Unit>()
7 |
8 | override fun report(insight: Insight) {
9 | CliInsights.insight = insight
10 | listeners.forEach { it.invoke(insight) }
11 | }
12 |
13 | override fun onInsightsUpdated(callback: (Insight) -> Unit) {
14 | listeners.add(callback)
15 | }
16 |
17 | override fun unregisterListener(callback: (Insight) -> Unit) {
18 | listeners.remove(callback)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/maestro-web/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Maestro Web
2 | POM_ARTIFACT_ID=maestro-web
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/maestro-web/src/main/kotlin/maestro/web/record/VideoEncoder.kt:
--------------------------------------------------------------------------------
1 | package maestro.web.record
2 |
3 | import okio.Sink
4 |
5 | interface VideoEncoder : AutoCloseable {
6 |
7 | fun start(out: Sink)
8 |
9 | fun encodeFrame(frame: ByteArray)
10 |
11 | }
--------------------------------------------------------------------------------
/maestro-web/src/main/kotlin/maestro/web/selenium/SeleniumFactory.kt:
--------------------------------------------------------------------------------
1 | package maestro.web.selenium
2 |
3 | import org.openqa.selenium.WebDriver
4 |
5 | interface SeleniumFactory {
6 |
7 | fun create(): WebDriver
8 |
9 | }
--------------------------------------------------------------------------------
/recipes/googleplay/install_twitter.yaml:
--------------------------------------------------------------------------------
1 | # Note that this flow expects that you already have a
2 | # Google account on your device
3 |
4 | appId: com.android.vending
5 | name: Install Twitter
6 | ---
7 | - launchApp
8 | - tapOn: Search for apps & games
9 | - inputText: Twitter
10 | - tapOn:
11 | text: twitter
12 | index: 1
13 | - tapOn: Install
14 |
--------------------------------------------------------------------------------
/recipes/nowinandroid/pick_interests.yaml:
--------------------------------------------------------------------------------
1 | appId: com.google.samples.apps.nowinandroid.demo.debug
2 | name: Pick Interests
3 | ---
4 | - launchApp:
5 | clearState: true
6 | - tapOn: Headlines
7 | - tapOn: Testing
8 | - tapOn: Done
9 |
--------------------------------------------------------------------------------
/recipes/square/pos/android/signup.yaml:
--------------------------------------------------------------------------------
1 | appId: com.squareup
2 | name: Sign Up
3 | ---
4 | - launchApp
5 | - tapOn: Create account
6 | - tapOn:
7 | text: Accept all cookies
8 | optional: true
9 | - inputText: ${EMAIL}
10 | - tapOn: Continue
11 | - inputText: ${PASSWORD}
12 | - tapOn: Continue
13 | - tapOn:
14 | rightOf: I agree to.*
15 | retryTapIfNoChange: false
16 | - tapOn: Continue
17 | - tapOn: Confirm
18 |
--------------------------------------------------------------------------------
/recipes/twitter/android/create_account.yaml:
--------------------------------------------------------------------------------
1 | # Gets user all the way up to OTP step in Twitter signup
2 |
3 | appId: com.twitter.android
4 | name: Create Account
5 | ---
6 | - launchApp:
7 | clearState: true
8 | - tapOn: Create account
9 | - tapOn: Skip for now
10 | - inputText: ${NAME}
11 | - tapOn:
12 | id: com.twitter.android:id/phone_or_email_field
13 | - tapOn: Use email instead
14 | - inputText: ${EMAIL}
15 | - tapOn: Date of birth
16 | - longPressOn: 2021
17 | - tapOn: Next
18 | - tapOn: Next
19 | - tapOn: Sign up
20 |
--------------------------------------------------------------------------------
/recipes/twitter/android/follow.yaml:
--------------------------------------------------------------------------------
1 | appId: com.twitter.android
2 | ---
3 | - launchApp
4 | - tapOn: Search and Explore
5 | - tapOn: Search Twitter
6 | - inputText: "@mobile__dev"
7 | - tapOn: mobile.dev
8 | - tapOn: Follow
9 | - assertVisible: Following
10 |
--------------------------------------------------------------------------------
/tmp.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -e
4 |
5 | if [ -t 0 ]; then
6 | input=""
7 | else
8 | input=$(cat -)
9 | fi
10 |
11 | echo "Hello $input"
--------------------------------------------------------------------------------