Merge pull request #1522 from httprunner/bugfix

fix: failed to catch logcat
fix: failed to find text in scope
fix: failed to set duration option of swipe action
change: add back action of mobile ui automation
fix: failed to set steps option of android swipe action
This commit is contained in:
debugtalk
2022-11-10 13:19:38 +08:00
committed by GitHub
8 changed files with 94 additions and 20 deletions

View File

@@ -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)

View File

@@ -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 {
@@ -233,9 +244,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", KEYCODE_BACK)
}
return
}
@@ -257,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
@@ -266,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
@@ -578,7 +592,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...)
}

View File

@@ -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
@@ -67,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
@@ -98,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) {
@@ -584,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 {
@@ -593,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:
@@ -618,6 +638,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()

View File

@@ -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 {
@@ -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

View File

@@ -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...)
}
@@ -528,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}

View File

@@ -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
}
}
}

View File

@@ -22,16 +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 // 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
)

View File

@@ -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,