Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AppleTV support #2067

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
457e1a9
create a new xcode target for tvOS
maxphillipsdev Sep 20, 2024
9002f77
enable maestro-driver-iosUITests to run against tvOS target
maxphillipsdev Sep 20, 2024
5896b72
update ScreenSizeHelper to compile on tvOS
maxphillipsdev Sep 20, 2024
0342d49
update EventRecord to compile on tvOS
maxphillipsdev Sep 20, 2024
fdd81b4
fix typo
maxphillipsdev Sep 20, 2024
eddeb59
update SystemPermissionHelper to compile on tvOS
maxphillipsdev Sep 20, 2024
563785e
update XCUIElement extension to compile on tvOS
maxphillipsdev Sep 20, 2024
33f8636
add tvOS bindings for Remote Dpad controls
maxphillipsdev Sep 20, 2024
c4dbed1
migrate maestro-driver to swiftui so tvOS and ios can share a target
maxphillipsdev Sep 20, 2024
de24aef
run build script for ios driver
maxphillipsdev Sep 22, 2024
d3bc0cb
update xcode build script, remove old targets, run new ios / tvos build
maxphillipsdev Sep 22, 2024
0d62edc
update bundle id
maxphillipsdev Sep 22, 2024
bdc809c
update springboard references for tvOS
maxphillipsdev Sep 25, 2024
bcb2041
add new ios and tvos drivers
maxphillipsdev Sep 25, 2024
8bc3dd5
update maestro-ios-driver to install separate drivers for tvOS and iOS
maxphillipsdev Sep 25, 2024
6a717a4
run apple build scripts
maxphillipsdev Jan 7, 2025
8a93210
remove duplicate bundleid
maxphillipsdev Jan 7, 2025
908aae9
update docs
maxphillipsdev Jan 7, 2025
2ad31ea
fix test run scripts to support the tvos changes
maxphillipsdev Jan 7, 2025
16b2353
update xctestrun
maxphillipsdev Jan 7, 2025
868d367
undo change of the object version
maxphillipsdev Jan 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ They are placed in `maestro-android/build/outputs/apk`, and are copied over to `

Maestro requires 3 artifacts to run on iOS:

- `maestro-driver-ios` - the host app for the test runner. Does nothing and is not installed.
- `maestro-driver-iosUITests-Runner.app` - the test runner app. Starts an HTTP server inside an infinite XCTest.
- `maestro-driver-ios-config.xctestrun` - the configuration file required to run the test runner app.
- `maestro-driver` - the host app for the test runner. Does nothing and is not installed.
- `maestro-driverUITests-Runner.app` - the test runner app. Starts an HTTP server inside an infinite XCTest.
- `maestro-driver-config.xctestrun` - the configuration file required to run the test runner app.

These artifacts are built by the `build-maestro-ios-runner.sh` script. It places them in `maestro-ios-driver/src/main/resources`.

Expand Down
20 changes: 1 addition & 19 deletions maestro-client/src/main/java/maestro/drivers/IOSDriver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -130,25 +130,7 @@ class IOSDriver(

override fun pressKey(code: KeyCode) {
metrics.measured("operation", mapOf("command" to "pressKey")) {
val keyCodeNameMap = mapOf(
KeyCode.BACKSPACE to "delete",
KeyCode.ENTER to "return",
)

val buttonNameMap = mapOf(
KeyCode.HOME to "home",
KeyCode.LOCK to "lock",
)

runDeviceCall("pressKey") {
keyCodeNameMap[code]?.let { name ->
iosDevice.pressKey(name)
}

buttonNameMap[code]?.let { name ->
iosDevice.pressButton(name)
}
}
iosDevice.pressButton(code.description)
}
}

Expand Down
1 change: 1 addition & 0 deletions maestro-ios-driver/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ dependencies {
implementation(project(":maestro-utils"))

api(libs.square.okhttp)
api(libs.google.gson)
api(libs.square.okhttp.logs)
api(libs.jackson.module.kotlin)
api(libs.jarchivelib)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import okio.source
import org.apache.commons.io.FileUtils
import org.rauschig.jarchivelib.ArchiverFactory
import org.slf4j.LoggerFactory
import util.LocalSimulatorUtils
import util.XCRunnerCLIUtils
import xcuitest.XCTestClient
import java.io.File
Expand Down Expand Up @@ -176,25 +177,41 @@ class LocalXCTestInstaller(
return checkSuccessful
}

private fun isTV(deviceId: String): Boolean {
val list = LocalSimulatorUtils.list()
for (entry in list.devices.entries) {
for (device in entry.value) {
if (device.udid != deviceId) continue
if (device.deviceTypeIdentifier == null) continue
logger.trace("Device is an AppleTV")
return device.deviceTypeIdentifier.contains("Apple-TV")
}
}

return false
}

private fun startXCTestRunner() {
if (isChannelAlive()) {
logger.info("UI Test runner already running, returning")
return
}

val isTV = isTV(deviceId)

logger.info("[Start] Writing xctest run file")
val tempDir = File(tempDir).apply { mkdir() }
val xctestRunFile = File("$tempDir/maestro-driver-ios-config.xctestrun")
writeFileToDestination(XCTEST_RUN_PATH, xctestRunFile)
val xctestRunFile = File("${tempDir}/maestro-driver-config.xctestrun")
writeFileToDestination(XCTEST_RUN_PATH, xctestRunFile, isTV)
logger.info("[Done] Writing xctest run file")

logger.info("[Start] Writing maestro-driver-iosUITests-Runner app")
extractZipToApp("maestro-driver-iosUITests-Runner", UI_TEST_RUNNER_PATH)
logger.info("[Done] Writing maestro-driver-iosUITests-Runner app")
logger.info("[Start] Writing maestro-driverUITests-Runner app")
extractZipToApp("maestro-driverUITests-Runner", UI_TEST_RUNNER_PATH, isTV)
logger.info("[Done] Writing maestro-driverUITests-Runner app")

logger.info("[Start] Writing maestro-driver-ios app")
extractZipToApp("maestro-driver-ios", UI_TEST_HOST_PATH)
logger.info("[Done] Writing maestro-driver-ios app")
logger.info("[Start] Writing maestro-driver app")
extractZipToApp("maestro-driver", UI_TEST_HOST_PATH, isTV)
logger.info("[Done] Writing maestro-driver app")

logger.info("[Start] Running XcUITest with `xcodebuild test-without-building`")
xcTestProcess = XCRunnerCLIUtils.runXcTestWithoutBuild(
Expand All @@ -218,30 +235,36 @@ class LocalXCTestInstaller(
logger.info("[Done] Cleaning up the ui test runner files")
}

private fun extractZipToApp(appFileName: String, srcAppPath: String) {
val appFile = File("$tempDir/Debug-iphonesimulator").apply { mkdir() }
private fun extractZipToApp(appFileName: String, srcAppPath: String, isTV: Boolean) {
val appFile = when(isTV) {
true -> File("$tempDir/Debug-appletvsimulator").apply { mkdir() }
false -> File("$tempDir/Debug-iphonesimulator").apply { mkdir() }
}
val appZip = File("$tempDir/$appFileName.zip")

writeFileToDestination(srcAppPath, appZip)
writeFileToDestination(srcAppPath, appZip, isTV)
ArchiverFactory.createArchiver(appZip).apply {
extract(appZip, appFile)
}
}

private fun writeFileToDestination(srcPath: String, destFile: File) {
LocalXCTestInstaller::class.java.getResourceAsStream(srcPath)?.let {
private fun writeFileToDestination(srcPath: String, destFile: File, isTV: Boolean) {
val prefix = when (isTV) {
true -> "/tvos/"
false -> "/ios/"
}
LocalXCTestInstaller::class.java.getResourceAsStream(prefix + srcPath)?.let {
val bufferedSink = destFile.sink().buffer()
bufferedSink.writeAll(it.source())
bufferedSink.flush()
}
}

companion object {
private const val UI_TEST_RUNNER_PATH = "/maestro-driver-iosUITests-Runner.zip"
private const val XCTEST_RUN_PATH = "/maestro-driver-ios-config.xctestrun"
private const val UI_TEST_HOST_PATH = "/maestro-driver-ios.zip"
private const val UI_TEST_RUNNER_APP_BUNDLE_ID =
"dev.mobile.maestro-driver-iosUITests.xctrunner"
private const val UI_TEST_RUNNER_PATH = "maestro-driverUITests-Runner.zip"
private const val XCTEST_RUN_PATH = "maestro-driver-config.xctestrun"
private const val UI_TEST_HOST_PATH = "maestro-driver.zip"
private const val UI_TEST_RUNNER_APP_BUNDLE_ID = "dev.mobile.maestro-driverUITests.xctrunner"

private const val SERVER_LAUNCH_TIMEOUT_MS = 120000L
private const val MAESTRO_DRIVER_STARTUP_TIMEOUT = "MAESTRO_DRIVER_STARTUP_TIMEOUT"
Expand Down
Loading
Loading