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

Revert keyboard change that throws error if keyboard is not shown #2222

Merged
merged 4 commits into from
Dec 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion maestro-client/src/main/java/maestro/Errors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ sealed class MaestroException(override val message: String) : RuntimeException(m

class UnableToClearState(message: String) : MaestroException(message)

class UnableToProcessCommand(message: String, val command: String): MaestroException(message)
class UnableToPullState(message: String) : MaestroException(message)

class UnableToPushState(message: String) : MaestroException(message)

class AppCrash(message: String): MaestroException(message)

Expand Down
6 changes: 1 addition & 5 deletions maestro-client/src/main/java/maestro/drivers/IOSDriver.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,13 @@ package maestro.drivers
import com.github.michaelbull.result.expect
import com.github.michaelbull.result.getOrThrow
import com.github.michaelbull.result.onSuccess
import com.github.michaelbull.result.runCatching
import hierarchy.AXElement
import ios.IOSDevice
import ios.IOSDeviceErrors
import maestro.*
import maestro.UiElement.Companion.toUiElement
import maestro.UiElement.Companion.toUiElementOrNull
import maestro.utils.*
import maestro.utils.network.XCUITestServerError
import okio.Sink
import okio.source
import org.slf4j.LoggerFactory
Expand Down Expand Up @@ -531,10 +529,8 @@ class IOSDriver(
} catch (socketTimeoutException: SocketTimeoutException) {
LOGGER.error("Got socket timeout processing $callName command", socketTimeoutException)
throw socketTimeoutException
} catch (badRequest: XCUITestServerError.BadRequest) {
LOGGER.error("Bad request for $callName, reason: ${badRequest.errorResponse}")
throw MaestroException.UnableToProcessCommand(command = callName, message = badRequest.error.errorMessage)
} catch (appCrashException: IOSDeviceErrors.AppCrash) {
LOGGER.error("Detected app crash during $callName command", appCrashException)
throw MaestroException.AppCrash(appCrashException.errorMessage)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import maestro.ios.MockXCTestInstaller
import maestro.utils.network.XCUITestServerError
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import maestro.utils.network.Error
import okhttp3.mockwebserver.SocketPolicy
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import xcuitest.XCTestClient
import xcuitest.XCTestDriverClient
import xcuitest.api.DeviceInfo
import xcuitest.api.Error
import java.net.InetAddress

class XCTestDriverClientTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ object XCRunnerCLIUtils {
val mapper = jacksonObjectMapper()
val appsMap = mapper.readValue(json, Map::class.java) as Map<String, Any>

return appsMap.keys + runningApps(deviceId).keys
return appsMap.keys
}

fun setProxy(host: String, port: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package xcuitest
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import hierarchy.ViewHierarchy
import maestro.utils.HttpClient
import maestro.utils.network.Error
import maestro.utils.network.XCUITestServerError
import okhttp3.*
import okhttp3.MediaType.Companion.toMediaType
Expand Down Expand Up @@ -255,7 +254,7 @@ class XCTestDriverClient(
logger.error("Request for $pathString failed with bad request ${code}, body: $responseBodyAsString")
throw XCUITestServerError.BadRequest(
"Request for $pathString failed with bad request ${code}, body: $responseBodyAsString",
error
responseBodyAsString
)
}
error.errorMessage.contains("Lost connection to the application.*".toRegex()) -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package maestro.utils.network
package xcuitest.api

import com.fasterxml.jackson.annotation.JsonProperty

Expand Down
Binary file modified maestro-ios-driver/src/main/resources/maestro-driver-ios.zip
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ struct EraseTextHandler: HTTPHandler {
do {
let start = Date()

if let errorResponse = await waitUntilKeyboardIsPresented(appIds: requestBody.appIds) {
return errorResponse
}
let appId = RunningApp.getForegroundAppId(requestBody.appIds)
await waitUntilKeyboardIsPresented(appId: appId)

let deleteText = String(repeating: XCUIKeyboardKey.delete.rawValue, count: requestBody.charactersToErase)

Expand All @@ -36,20 +35,11 @@ struct EraseTextHandler: HTTPHandler {
}
}

private func waitUntilKeyboardIsPresented(appIds: [String]) async -> HTTPResponse? {
let foregroundAppIds = RunningApp.getForegroundAppIds(appIds)
logger.info("Foreground apps \(foregroundAppIds)")

let isKeyboardPresented: Bool = (try? await TimeoutHelper.repeatUntil(timeout: 1, delta: 0.2) {
return foregroundAppIds.contains { appId in
XCUIApplication(bundleIdentifier: appId).keyboards.firstMatch.exists
}
}) ?? false
private func waitUntilKeyboardIsPresented(appId: String?) async {
try? await TimeoutHelper.repeatUntil(timeout: 1, delta: 0.2) {
guard let appId = appId else { return true }

// Return an error response if the keyboard is not presented
if !isKeyboardPresented {
return AppError(type: .timeout, message: "Keyboard not presented within 1 second timeout for erase command").httpResponse
return XCUIApplication(bundleIdentifier: appId).keyboards.firstMatch.exists
}
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ struct InputTextRouteHandler : HTTPHandler {
do {
let start = Date()

if let errorResponse = await waitUntilKeyboardIsPresented(appIds: requestBody.appIds) {
return errorResponse
}
let appId = RunningApp.getForegroundAppId(requestBody.appIds)
await waitUntilKeyboardIsPresented(appId: appId)

try await TextInputHelper.inputText(requestBody.text)

Expand All @@ -31,20 +30,11 @@ struct InputTextRouteHandler : HTTPHandler {
}
}

private func waitUntilKeyboardIsPresented(appIds: [String]) async -> HTTPResponse? {
let foregroundAppIds = RunningApp.getForegroundAppIds(appIds)
logger.info("Foreground apps \(foregroundAppIds)")

let isKeyboardPresented: Bool = (try? await TimeoutHelper.repeatUntil(timeout: 1, delta: 0.2) {
return foregroundAppIds.contains { appId in
XCUIApplication(bundleIdentifier: appId).keyboards.firstMatch.exists
}
}) ?? false
private func waitUntilKeyboardIsPresented(appId: String?) async {
try? await TimeoutHelper.repeatUntil(timeout: 1, delta: 0.2) {
guard let appId = appId else { return true }

// Return an error response if the keyboard is not presented
if !isKeyboardPresented {
return AppError(type: .timeout, message: "Keyboard not presented within 1 second timeout for input command").httpResponse
return XCUIApplication(bundleIdentifier: appId).keyboards.firstMatch.exists
}
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import FlyingFox
enum AppErrorType: String, Codable {
case `internal`
case precondition
case timeout
}

struct AppError: Error, Codable {
Expand All @@ -16,7 +15,6 @@ struct AppError: Error, Codable {
switch type {
case .internal: return .internalServerError
case .precondition: return .badRequest
case .timeout: return .badRequest
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,4 @@ struct TimeoutHelper {
}
}
}

static func repeatUntil(timeout: TimeInterval, delta: TimeInterval, block: () -> Bool) async throws -> Bool {
guard delta >= 0 else {
throw NSError(domain: "Invalid value", code: 1, userInfo: [NSLocalizedDescriptionKey: "Delta cannot be negative"])
}

let timeout = Date().addingTimeInterval(timeout)

while Date() < timeout {
do {
try await Task.sleep(nanoseconds: UInt64(1_000_000_000 * delta))
} catch {
throw NSError(domain: "Failed to sleep task", code: 2, userInfo: [NSLocalizedDescriptionKey: "Task could not be put to sleep"])
}

if (block()) {
return true
}
}
return false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,5 @@ struct RunningApp {
} ?? RunningApp.springboardBundleId
}

static func getForegroundAppIds(_ appIds: [String]) -> [String] {
// springboard is always on foreground
let allAppIds = appIds + ["com.apple.springboard"]


return allAppIds.filter { appId in
let app = XCUIApplication(bundleIdentifier: appId)
return app.state == .runningForeground
}
}

}
1 change: 0 additions & 1 deletion maestro-utils/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ dependencies {
api(libs.square.okio)
implementation(libs.square.okhttp)
implementation(libs.micrometer.core)
api(libs.jackson.module.kotlin)
implementation(libs.micrometer.observation)

testImplementation(libs.mockk)
Expand Down
2 changes: 1 addition & 1 deletion maestro-utils/src/main/kotlin/network/Errors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ sealed class XCUITestServerError: Throwable() {
data class UnknownFailure(val errorResponse: String) : XCUITestServerError()
data class NetworkError(val errorResponse: String): XCUITestServerError()
data class AppCrash(val errorResponse: String): XCUITestServerError()
data class BadRequest(val errorResponse: String, val error: Error): XCUITestServerError()
data class BadRequest(val errorResponse: String, val clientMessage: String): XCUITestServerError()
}
3 changes: 1 addition & 2 deletions maestro-utils/src/test/kotlin/network/ErrorsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,9 @@ class ErrorsTest {
fun `XCUITestServerError BadRequest should have correct messages`() {
val errorMessage = "Bad request"
val clientMessage = "Client error"
val error = Error(errorMessage = clientMessage, errorCode = "bad-request-precondition")

assertThrows<XCUITestServerError.BadRequest>(errorMessage) {
throw XCUITestServerError.BadRequest(errorMessage, error)
throw XCUITestServerError.BadRequest(errorMessage, clientMessage)
}
}
}
Loading