From 49757b52a8bd1c19a740e4c1695157f6a7d60251 Mon Sep 17 00:00:00 2001 From: "xucong.053" Date: Thu, 27 Oct 2022 18:07:17 +0800 Subject: [PATCH 1/6] fix: failed to catch logcat --- hrp/pkg/uixt/android_device.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index e2a30d93..e86206c6 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -310,6 +310,9 @@ func ConvertPoints(data string) (eps []ExportPoint) { for _, line := range lines { if strings.Contains(line, "ext") { idx := strings.Index(line, "{") + if idx == -1 { + continue + } line = line[idx:] p := ExportPoint{} err := json.Unmarshal([]byte(line), &p) From 315610e0a29ba6c19f4f37814daf7205dee57f76 Mon Sep 17 00:00:00 2001 From: "xucong.053" Date: Wed, 9 Nov 2022 15:42:10 +0800 Subject: [PATCH 2/6] fix: failed to find text in scope --- hrp/pkg/uixt/ocr_vedem.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 3f456045..1bcc96ad 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -179,16 +179,16 @@ func (s *veDEMOCRService) FindText(text string, imageBuf []byte, options ...Data } rects = append(rects, rect) - } - // contains text while not match exactly - if ocrResult.Text != text { - continue - } + // contains text while not match exactly + if ocrResult.Text != text { + continue + } - // match exactly, and not specify index, return the first one - if data.Index == 0 { - return rect, nil + // match exactly, and not specify index, return the first one + if data.Index == 0 { + return rect, nil + } } } From f9aa6e54edee1eb84f99b6a886aba74d22ff6adb Mon Sep 17 00:00:00 2001 From: "xucong.053" Date: Wed, 9 Nov 2022 16:04:14 +0800 Subject: [PATCH 3/6] fix: failed to set duration option of swipe action --- hrp/pkg/uixt/interface.go | 2 +- hrp/pkg/uixt/ios_driver.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index c31612c6..f3bc173b 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -891,7 +891,7 @@ func NewData(data map[string]interface{}, options ...DataOption) *DataOptions { } if _, ok := dataOptions.Data["duration"]; !ok { - dataOptions.Data["duration"] = 1.0 // default duration + dataOptions.Data["duration"] = 0 // default duration } if _, ok := dataOptions.Data["frequency"]; !ok { diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index 4796d577..96caad8d 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -440,12 +440,10 @@ func (wd *wdaDriver) DragFloat(fromX, fromY, toX, toY float64, options ...DataOp } func (wd *wdaDriver) Swipe(fromX, fromY, toX, toY int, options ...DataOption) error { - options = append(options, WithDataPressDuration(0)) return wd.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...) } func (wd *wdaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error { - options = append(options, WithDataPressDuration(0)) return wd.DragFloat(fromX, fromY, toX, toY, options...) } From 623b455082f2d75fd0122cb3142ca3ac212831c2 Mon Sep 17 00:00:00 2001 From: "xucong.053" Date: Wed, 9 Nov 2022 16:36:36 +0800 Subject: [PATCH 4/6] change: add back action of mobile ui automation --- hrp/pkg/uixt/android_driver.go | 5 ++++- hrp/pkg/uixt/ext.go | 11 +++++++---- hrp/pkg/uixt/interface.go | 3 +++ hrp/pkg/uixt/ios_driver.go | 21 +++++++++++++++++++++ hrp/step_mobile_ui.go | 12 ++++++++++++ 5 files changed, 47 insertions(+), 5 deletions(-) diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go index 255e9757..3f642c5c 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_driver.go @@ -233,9 +233,12 @@ func (ud *uiaDriver) Scale() (scale float64, err error) { } // PressBack simulates a short press on the BACK button. -func (ud *uiaDriver) PressBack() (err error) { +func (ud *uiaDriver) PressBack(options ...DataOption) (err error) { // register(postHandler, new PressBack("/wd/hub/session/:sessionId/back")) _, err = ud.httpPOST(nil, "/session", ud.sessionId, "back") + if err != nil { + _, err = ud.adbDevice.RunShellCommand("input", "keyevent", "4") + } return } diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 82e11553..ffa9a4e6 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -53,6 +53,7 @@ const ( ACTION_DoubleTap MobileMethod = "double_tap" ACTION_Swipe MobileMethod = "swipe" ACTION_Input MobileMethod = "input" + ACTION_Back MobileMethod = "back" // custom actions ACTION_SwipeToTapApp MobileMethod = "swipe_to_tap_app" // swipe left & right to find app and tap @@ -618,6 +619,8 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { options = append(options, WithDataIdentifier(action.Identifier)) } return dExt.Driver.Input(param, options...) + case ACTION_Back: + return dExt.Driver.PressBack() case CtlSleep: if param, ok := action.Params.(json.Number); ok { seconds, _ := param.Float64() @@ -651,10 +654,10 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { } func (dExt *DriverExt) getAbsScope(x1, y1, x2, y2 float64) (int, int, int, int) { - return int(x1 * float64(dExt.windowSize.Width) * dExt.scale), - int(y1 * float64(dExt.windowSize.Height) * dExt.scale), - int(x2 * float64(dExt.windowSize.Width) * dExt.scale), - int(y2 * float64(dExt.windowSize.Height) * dExt.scale) + return int(x1 * float64(dExt.windowSize.Width)), + int(y1 * float64(dExt.windowSize.Height)), + int(x2 * float64(dExt.windowSize.Width)), + int(y2 * float64(dExt.windowSize.Height)) } func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...string) bool { diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index f3bc173b..77ae84cb 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -1046,6 +1046,9 @@ type WebDriver interface { // PressButton Presses the corresponding hardware button on the device PressButton(devBtn DeviceButton) error + // PressBack Presses the back button + PressBack(options ...DataOption) error + // IOHIDEvent Emulated triggering of the given low-level IOHID device event. // duration: The event duration in float seconds (XCTest uses 0.005 for a single press event) IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) error diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index 96caad8d..a14c08c8 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -526,6 +526,27 @@ func (wd *wdaDriver) KeyboardDismiss(keyNames ...string) (err error) { return } +// PressBack simulates a short press on the BACK button. +func (wd *wdaDriver) PressBack(options ...DataOption) (err error) { + windowSize, err := wd.WindowSize() + if err != nil { + return + } + + data := map[string]interface{}{ + "fromX": float64(windowSize.Width) * 0, + "fromY": float64(windowSize.Height) * 0.5, + "toX": float64(windowSize.Width) * 0.6, + "toY": float64(windowSize.Height) * 0.5, + } + + // new data options in post data for extra WDA configurations + d := NewData(data, options...) + + _, err = wd.httpPOST(d.Data, "/session", wd.sessionId, "/wda/dragfromtoforduration") + return +} + func (wd *wdaDriver) PressButton(devBtn DeviceButton) (err error) { // [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)] data := map[string]interface{}{"name": devBtn} diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 6f9fe7fb..4dcf2be8 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -181,6 +181,18 @@ func (s *StepMobile) DoubleTap(params string, options ...uixt.ActionOption) *Ste return &StepMobile{step: s.step} } +func (s *StepMobile) Back(options ...uixt.ActionOption) *StepMobile { + action := uixt.MobileAction{ + Method: uixt.ACTION_Back, + Params: nil, + } + for _, option := range options { + option(&action) + } + s.mobileStep().Actions = append(s.mobileStep().Actions, action) + return &StepMobile{step: s.step} +} + func (s *StepMobile) Swipe(sx, sy, ex, ey float64, options ...uixt.ActionOption) *StepMobile { action := uixt.MobileAction{ Method: uixt.ACTION_Swipe, From 85ff696f479c7c7e7a4ad85100c65b75d805556e Mon Sep 17 00:00:00 2001 From: "xucong.053" Date: Wed, 9 Nov 2022 18:19:57 +0800 Subject: [PATCH 5/6] fix: failed to set steps option of android swipe action --- hrp/pkg/uixt/android_driver.go | 1 - hrp/pkg/uixt/ext.go | 23 +++++++++++++++++++++-- hrp/step.go | 2 ++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go index 3f642c5c..eae15617 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_driver.go @@ -581,7 +581,6 @@ func (ud *uiaDriver) _swipe(startX, startY, endX, endY interface{}, options ...D // per step. So for a 100 steps, the swipe will take about 1/2 second to complete. // `steps` is the number of move steps sent to the system func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY int, options ...DataOption) error { - options = append(options, WithDataSteps(12)) return ud.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...) } diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index ffa9a4e6..119a03f7 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -68,6 +68,8 @@ type MobileAction struct { Identifier string `json:"identifier,omitempty" yaml:"identifier,omitempty"` // used to identify the action in log MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times WaitTime float64 `json:"wait_time,omitempty" yaml:"wait_time,omitempty"` // wait time between swipe and ocr, unit: second + Duration float64 `json:"duration,omitempty" yaml:"duration,omitempty"` // used to set duration of ios swipe action + Steps int `json:"steps,omitempty" yaml:"steps,omitempty"` // used to set steps of android swipe action Direction interface{} `json:"direction,omitempty" yaml:"direction,omitempty"` // used by swipe to tap text or app Scope []float64 `json:"scope,omitempty" yaml:"scope,omitempty"` // used by ocr to get text position in the scope Offset []int `json:"offset,omitempty" yaml:"offset,omitempty"` // used to tap offset of point @@ -99,6 +101,18 @@ func WithWaitTime(sec float64) ActionOption { } } +func WithDuration(duration float64) ActionOption { + return func(o *MobileAction) { + o.Duration = duration + } +} + +func WithSteps(steps int) ActionOption { + return func(o *MobileAction) { + o.Steps = steps + } +} + // WithDirection inputs direction (up, down, left, right) func WithDirection(direction string) ActionOption { return func(o *MobileAction) { @@ -585,6 +599,11 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { return fmt.Errorf("invalid %s params: %v", ACTION_DoubleTap, action.Params) case ACTION_Swipe: identifierOption := WithDataIdentifier(action.Identifier) + durationOption := WithDataPressDuration(action.Duration) + if action.Steps == 0 { + action.Steps = 10 + } + stepsOption := WithDataSteps(action.Steps) if positions, ok := action.Params.([]interface{}); ok { // relative fromX, fromY, toX, toY of window size: [0.5, 0.9, 0.5, 0.1] if len(positions) != 4 { @@ -594,10 +613,10 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { fromY, _ := positions[1].(float64) toX, _ := positions[2].(float64) toY, _ := positions[3].(float64) - return dExt.SwipeRelative(fromX, fromY, toX, toY, identifierOption) + return dExt.SwipeRelative(fromX, fromY, toX, toY, identifierOption, durationOption, stepsOption) } if direction, ok := action.Params.(string); ok { - return dExt.SwipeTo(direction, identifierOption) + return dExt.SwipeTo(direction, identifierOption, durationOption, stepsOption) } return fmt.Errorf("invalid %s params: %v", ACTION_Swipe, action.Params) case ACTION_Input: diff --git a/hrp/step.go b/hrp/step.go index 6c41f9d5..c9e96023 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -29,6 +29,8 @@ var ( WithText = uixt.WithText WithID = uixt.WithID WithDescription = uixt.WithDescription + WithDuration = uixt.WithDuration + WIthSteps = uixt.WithSteps WithDirection = uixt.WithDirection WithCustomDirection = uixt.WithCustomDirection WithScope = uixt.WithScope From 5e00895f76afae88bc5db44475ed231463f7f2b5 Mon Sep 17 00:00:00 2001 From: "xucong.053" Date: Thu, 10 Nov 2022 13:14:12 +0800 Subject: [PATCH 6/6] change: update annotation --- hrp/pkg/uixt/android_driver.go | 17 ++++++++++++++--- hrp/pkg/uixt/ext.go | 8 ++++---- hrp/step.go | 10 +++++----- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go index eae15617..5ebf69dd 100644 --- a/hrp/pkg/uixt/android_driver.go +++ b/hrp/pkg/uixt/android_driver.go @@ -18,6 +18,17 @@ import ( "github.com/httprunner/httprunner/v4/hrp/pkg/gadb" ) +// See https://developer.android.com/reference/android/view/KeyEvent +const ( + KEYCODE_BACK string = "4" + KEYCODE_CAMERA string = "27" + KEYCODE_ALT_LEFT string = "57" + KEYCODE_ALT_RIGHT string = "58" + KEYCODE_MENU string = "82" + KEYCODE_BREAK string = "121" + KEYCODE_ALL_APPS string = "284" +) + var errDriverNotImplemented = errors.New("driver method not implemented") type uiaDriver struct { @@ -237,7 +248,7 @@ func (ud *uiaDriver) PressBack(options ...DataOption) (err error) { // register(postHandler, new PressBack("/wd/hub/session/:sessionId/back")) _, err = ud.httpPOST(nil, "/session", ud.sessionId, "back") if err != nil { - _, err = ud.adbDevice.RunShellCommand("input", "keyevent", "4") + _, err = ud.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_BACK) } return } @@ -260,7 +271,7 @@ func (ud *uiaDriver) StartCamera() (err error) { return err } time.Sleep(5 * time.Second) - if _, err = ud.adbDevice.RunShellCommand("input", "keyevent", "27"); err != nil { + if _, err = ud.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil { return err } return @@ -269,7 +280,7 @@ func (ud *uiaDriver) StartCamera() (err error) { return err } time.Sleep(5 * time.Second) - if _, err = ud.adbDevice.RunShellCommand("input", "keyevent", "27"); err != nil { + if _, err = ud.adbDevice.RunShellCommand("input", "keyevent", KEYCODE_CAMERA); err != nil { return err } return diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 119a03f7..531f95b8 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -673,10 +673,10 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { } func (dExt *DriverExt) getAbsScope(x1, y1, x2, y2 float64) (int, int, int, int) { - return int(x1 * float64(dExt.windowSize.Width)), - int(y1 * float64(dExt.windowSize.Height)), - int(x2 * float64(dExt.windowSize.Width)), - int(y2 * float64(dExt.windowSize.Height)) + return int(x1 * float64(dExt.windowSize.Width) * dExt.scale), + int(y1 * float64(dExt.windowSize.Height) * dExt.scale), + int(x2 * float64(dExt.windowSize.Width) * dExt.scale), + int(y2 * float64(dExt.windowSize.Height) * dExt.scale) } func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...string) bool { diff --git a/hrp/step.go b/hrp/step.go index c9e96023..0df0347a 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -22,18 +22,18 @@ const ( var ( WithIdentifier = uixt.WithIdentifier WithMaxRetryTimes = uixt.WithMaxRetryTimes - WithWaitTime = uixt.WithWaitTime - WithIndex = uixt.WithIndex + WithWaitTime = uixt.WithWaitTime // only applicable to SwipeToTap* action + WithIndex = uixt.WithIndex // index of the target element, should start from 1, only applicable to ocr actions WithTimeout = uixt.WithTimeout WithIgnoreNotFoundError = uixt.WithIgnoreNotFoundError WithText = uixt.WithText WithID = uixt.WithID WithDescription = uixt.WithDescription - WithDuration = uixt.WithDuration - WIthSteps = uixt.WithSteps + WithDuration = uixt.WithDuration // only applicable to ios swipe action + WithSteps = uixt.WithSteps // only applicable to android swipe action WithDirection = uixt.WithDirection WithCustomDirection = uixt.WithCustomDirection - WithScope = uixt.WithScope + WithScope = uixt.WithScope // only applicable to ocr actions WithOffset = uixt.WithOffset )