-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
feat: add device.tap()
and device.longPress()
.
#4542
base: master
Are you sure you want to change the base?
Changes from 31 commits
a208592
2114892
cc494b9
cb93597
66a7ecc
760960b
f5f6267
a6058c7
c5aec9b
5c1dc73
b7c7252
6cb90ea
ae6b2b9
aa7974d
3895b6d
37fdfbd
9eaf52f
166c680
4082fcd
a8173e3
083b836
c4c214c
e708f58
fb1ebc0
6210ec0
e45373b
6ded4cb
1d8f092
adbed53
cf4c84e
d82c1ee
38c4e58
5f6c1c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -1,8 +1,13 @@ | ||||||||||||
package com.wix.detox.espresso; | ||||||||||||
|
||||||||||||
import android.annotation.SuppressLint; | ||||||||||||
import android.content.Context; | ||||||||||||
import android.os.Handler; | ||||||||||||
import android.util.DisplayMetrics; | ||||||||||||
import android.util.Log; | ||||||||||||
import android.util.TypedValue; | ||||||||||||
import android.view.Choreographer; | ||||||||||||
import android.view.View; | ||||||||||||
|
||||||||||||
import com.wix.detox.common.UIThread; | ||||||||||||
import com.wix.detox.espresso.action.common.utils.UiControllerUtils; | ||||||||||||
|
@@ -111,4 +116,10 @@ public void doFrame(long frameTimeNanos) { | |||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
@SuppressLint({"DiscouragedApi", "InternalInsetResource"}) | ||||||||||||
public static int getStatusBarHeight(View view) { | ||||||||||||
Context context = view.getContext(); | ||||||||||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android"); | ||||||||||||
return (int) (context.getResources().getDimensionPixelSize(resourceId) / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT)); | ||||||||||||
} | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to avoid confusion, could you please exctrat the method with the same name from
Lines 140 to 144 in 8fc7c6e
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just a scout rule that'll make the code cleaner 🙏 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed the names, do you mean to move one of the functions? |
||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,48 +7,96 @@ import Foundation | |
import XCTest | ||
|
||
class ActionHandler { | ||
func handle(from params: InvocationParams, on element: XCUIElement) throws { | ||
|
||
let exists = element.waitForExistence(timeout: .defaultTimeout) | ||
DTXAssert( | ||
exists, | ||
"Action failed, element with matcher `\(params.matcherDescription)` does not exist" | ||
) | ||
|
||
func findElement(from params: InvocationParams, predicateHandler: PredicateHandler) -> XCUIElement { | ||
|
||
let element = predicateHandler.findElement(using: params) | ||
|
||
let exists = element.waitForExistence(timeout: .defaultTimeout) | ||
DTXAssert( | ||
exists, | ||
"Action failed, element with matcher `\(params.matcherDescription)` does not exist" | ||
) | ||
return element; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please reindent |
||
|
||
func getNormalizedCoordinate(from params: InvocationParams) throws -> XCUICoordinate { | ||
do { | ||
guard let x = Int(params.params?.first ?? "100"), let y = Int(params.params?[1] ?? "100") else { | ||
throw Error.missingTypeTextParam | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you're throwing this error but catching it and hiding it (with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also this is not the right error to throw, you might want to make this error more generic by introducing params to it, e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I removed the error because we want to allow this case, I just need the fallback |
||
} | ||
|
||
let screenFrame = try XCUIApplication.appUnderTest().frame | ||
let normalizedX = CGFloat(x) / screenFrame.width | ||
let normalizedY = CGFloat(y) / screenFrame.height | ||
let normalizedPoint = CGVector(dx: normalizedX, dy: normalizedY) | ||
let coordinate = try XCUIApplication.appUnderTest().coordinate(withNormalizedOffset: normalizedPoint) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. let's avoid repeated calls to |
||
|
||
return coordinate; | ||
} catch { | ||
throw Error.failedToTapDeviceByCoordinates | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you're going to throw this error anyway if you catch anything ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You may avoid the try-catch here and just let it be caught by the caller, or remove the duplicated try-catch from the caller(s). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not even sure if we need any of these catches since they might hide other errors that are more specific. Please revisit the error throwing logic. |
||
} | ||
|
||
func handle(from params: InvocationParams, predicateHandler: PredicateHandler) throws { | ||
|
||
guard let action = params.action else { return } | ||
switch action { | ||
case .tap: | ||
let element = findElement(from: params, predicateHandler: predicateHandler); | ||
element.tap() | ||
|
||
case .typeText: | ||
guard let text = params.params?.first else { | ||
throw Error.missingTypeTextParam | ||
} | ||
|
||
let element = findElement(from: params, predicateHandler: predicateHandler); | ||
element.typeTextOnEnd(text) | ||
|
||
case .replaceText: | ||
guard let text = params.params?.first else { | ||
throw Error.missingTypeTextParam | ||
} | ||
|
||
let element = findElement(from: params, predicateHandler: predicateHandler); | ||
element.replaceText(text) | ||
|
||
case .clearText: | ||
let element = findElement(from: params, predicateHandler: predicateHandler); | ||
element.clearText() | ||
|
||
case .coordinateTap: | ||
do { | ||
try getNormalizedCoordinate(from: params).tap(); | ||
} catch { | ||
throw Error.failedToTapDeviceByCoordinates | ||
} | ||
case .coordinateLongPress: | ||
guard let pressDuration = Double(params.params?[2] ?? "1") else { throw Error.missingTypeTextParam | ||
} | ||
|
||
do { | ||
try getNormalizedCoordinate(from: params).press(forDuration: pressDuration); | ||
} catch { | ||
throw Error.failedToTapDeviceByCoordinates | ||
} | ||
} | ||
} | ||
} | ||
|
||
extension ActionHandler { | ||
enum Error: Swift.Error, LocalizedError { | ||
case missingTypeTextParam | ||
case failedToTapDeviceByCoordinates | ||
|
||
var errorDescription: String? { | ||
switch self { | ||
case .missingTypeTextParam: | ||
return "Missing text param for type action" | ||
} | ||
case .failedToTapDeviceByCoordinates: | ||
return "Failed to perform tap action by coordinates" | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's extract that to private method that will be shared with the
longPress
's perform (reuse & separation of concerns) → e.g.calculateAdjustedY(y, shouldIgnoreStatusBar)
or something similar.