diff --git a/examples/uitest/demo_weixin_test.go b/examples/uitest/demo_weixin_test.go index 05200abe..1031aefa 100644 --- a/examples/uitest/demo_weixin_test.go +++ b/examples/uitest/demo_weixin_test.go @@ -9,7 +9,7 @@ import ( func TestIOSWeixinLive(t *testing.T) { testCase := &hrp.TestCase{ - Config: hrp.NewConfig("通过 feed 卡片进入微信直播间"), + Config: hrp.NewConfig("通过 feed 卡片进入微信直播间"), // .SetIOS(hrp.WDADevice{Port: 8700, MjpegPort: 8800}) TestSteps: []hrp.IStep{ hrp.NewStep("启动微信"). IOS(). @@ -27,11 +27,11 @@ func TestIOSWeixinLive(t *testing.T) { TapByOCR("我知道了", hrp.WithIgnoreNotFoundError(true)), hrp.NewStep("在推荐页上划,直到出现「轻触进入直播间」"). IOS(). - SwipeToTapText("轻触进入直播间", hrp.WithMaxRetryTimes(10)), + SwipeToTapText("轻触进入直播间", hrp.WithMaxRetryTimes(10), hrp.WithIdentifier("进入直播间")), hrp.NewStep("向上滑动,等待 10s"). IOS(). - SwipeUp().Sleep(10).ScreenShot(). // 上划 1 次,等待 10s,截图保存 - SwipeUp().Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s,截图保存 + SwipeUp(hrp.WithIdentifier("第一次上划")).Sleep(10).ScreenShot(). // 上划 1 次,等待 10s,截图保存 + SwipeUp(hrp.WithIdentifier("第二次上划")).Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s,截图保存 }, } diff --git a/hrp/internal/uixt/tap.go b/hrp/internal/uixt/tap.go index ca8176c1..9f072481 100644 --- a/hrp/internal/uixt/tap.go +++ b/hrp/internal/uixt/tap.go @@ -6,7 +6,20 @@ import ( "github.com/electricbubble/gwda" ) -func (dExt *DriverExt) TapXY(x, y float64) error { +func (dExt *DriverExt) tapFloat(x, y float64, identifier string) error { + if len(identifier) > 0 { + option := gwda.DataOption{ + "log": map[string]interface{}{ + "enable": true, + "data": identifier, + }, + } + return dExt.WebDriver.TapFloat(x, y, option) + } + return dExt.WebDriver.TapFloat(x, y) +} + +func (dExt *DriverExt) TapXY(x, y float64, identifier string) error { // tap on coordinate: [x, y] should be relative if x > 1 || y > 1 { return fmt.Errorf("x, y percentage should be < 1, got x=%v, y=%v", x, y) @@ -14,10 +27,11 @@ func (dExt *DriverExt) TapXY(x, y float64) error { x = x * float64(dExt.windowSize.Width) y = y * float64(dExt.windowSize.Height) - return dExt.WebDriver.TapFloat(x, y) + + return dExt.tapFloat(x, y, identifier) } -func (dExt *DriverExt) TapByOCR(ocrText string, ignoreNotFoundError bool) error { +func (dExt *DriverExt) TapByOCR(ocrText string, identifier string, ignoreNotFoundError bool) error { x, y, width, height, err := dExt.FindTextByOCR(ocrText) if err != nil { if ignoreNotFoundError { @@ -26,10 +40,10 @@ func (dExt *DriverExt) TapByOCR(ocrText string, ignoreNotFoundError bool) error return err } - return dExt.WebDriver.TapFloat(x+width*0.5, y+height*0.5) + return dExt.tapFloat(x+width*0.5, y+height*0.5, identifier) } -func (dExt *DriverExt) TapByCV(imagePath string, ignoreNotFoundError bool) error { +func (dExt *DriverExt) TapByCV(imagePath string, identifier string, ignoreNotFoundError bool) error { x, y, width, height, err := dExt.FindImageRectInUIKit(imagePath) if err != nil { if ignoreNotFoundError { @@ -38,14 +52,14 @@ func (dExt *DriverExt) TapByCV(imagePath string, ignoreNotFoundError bool) error return err } - return dExt.WebDriver.TapFloat(x+width*0.5, y+height*0.5) + return dExt.tapFloat(x+width*0.5, y+height*0.5, identifier) } -func (dExt *DriverExt) Tap(param string, ignoreNotFoundError bool) error { - return dExt.TapOffset(param, 0.5, 0.5, ignoreNotFoundError) +func (dExt *DriverExt) Tap(param string, identifier string, ignoreNotFoundError bool) error { + return dExt.TapOffset(param, 0.5, 0.5, identifier, ignoreNotFoundError) } -func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, ignoreNotFoundError bool) (err error) { +func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, identifier string, ignoreNotFoundError bool) (err error) { // click on element, find by name attribute ele, err := dExt.FindUIElement(param) if err == nil { @@ -60,7 +74,7 @@ func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, ignoreN return err } - return dExt.WebDriver.TapFloat(x+width*xOffset, y+height*yOffset) + return dExt.tapFloat(x+width*xOffset, y+height*yOffset, identifier) } func (dExt *DriverExt) DoubleTapXY(x, y float64) error { diff --git a/hrp/internal/uixt/tap_test.go b/hrp/internal/uixt/tap_test.go index a25f8946..9c8c2e09 100644 --- a/hrp/internal/uixt/tap_test.go +++ b/hrp/internal/uixt/tap_test.go @@ -23,7 +23,7 @@ func TestDriverExt_TapXY(t *testing.T) { driverExt, err := InitWDAClient() checkErr(t, err) - err = driverExt.TapXY(0.4, 0.5) + err = driverExt.TapXY(0.4, 0.5, "") checkErr(t, err) } @@ -32,6 +32,6 @@ func TestDriverExt_TapWithOCR(t *testing.T) { checkErr(t, err) // 需要点击文字上方的图标 - err = driverExt.TapOffset("抖音", 0.5, -1, false) + err = driverExt.TapOffset("抖音", 0.5, -1, "", false) checkErr(t, err) } diff --git a/hrp/step.go b/hrp/step.go index 2a219bb7..02a43262 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -59,13 +59,20 @@ type MobileAction struct { Method MobileMethod `json:"method,omitempty" yaml:"method,omitempty"` Params interface{} `json:"params,omitempty" yaml:"params,omitempty"` - MaxRetryTimes int `json:"max_retry_times,omitempty" yaml:"max_retry_times,omitempty"` // max retry times - Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action - IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found + 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 + Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` // TODO: wait timeout in seconds for mobile action + IgnoreNotFoundError bool `json:"ignore_NotFoundError,omitempty" yaml:"ignore_NotFoundError,omitempty"` // ignore error if target element not found } type ActionOption func(o *MobileAction) +func WithIdentifier(identifier string) ActionOption { + return func(o *MobileAction) { + o.Identifier = identifier + } +} + func WithMaxRetryTimes(maxRetryTimes int) ActionOption { return func(o *MobileAction) { o.MaxRetryTimes = maxRetryTimes diff --git a/hrp/step_ios_ui.go b/hrp/step_ios_ui.go index 312da3d7..a8018462 100644 --- a/hrp/step_ios_ui.go +++ b/hrp/step_ios_ui.go @@ -80,11 +80,15 @@ func (s *StepIOS) Home() *StepIOS { } // TapXY taps the point {X,Y}, X & Y is percentage of coordinates -func (s *StepIOS) TapXY(x, y float64) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ +func (s *StepIOS) TapXY(x, y float64, options ...ActionOption) *StepIOS { + action := MobileAction{ Method: uiTapXY, Params: []float64{x, y}, - }) + } + for _, option := range options { + option(&action) + } + s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } @@ -148,43 +152,63 @@ func (s *StepIOS) DoubleTap(params string, options ...ActionOption) *StepIOS { return &StepIOS{step: s.step} } -func (s *StepIOS) Swipe(sx, sy, ex, ey int) *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ +func (s *StepIOS) Swipe(sx, sy, ex, ey int, options ...ActionOption) *StepIOS { + action := MobileAction{ Method: uiSwipe, Params: []int{sx, sy, ex, ey}, - }) + } + for _, option := range options { + option(&action) + } + s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } -func (s *StepIOS) SwipeUp() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ +func (s *StepIOS) SwipeUp(options ...ActionOption) *StepIOS { + action := MobileAction{ Method: uiSwipe, Params: "up", - }) + } + for _, option := range options { + option(&action) + } + s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } -func (s *StepIOS) SwipeDown() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ +func (s *StepIOS) SwipeDown(options ...ActionOption) *StepIOS { + action := MobileAction{ Method: uiSwipe, Params: "down", - }) + } + for _, option := range options { + option(&action) + } + s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } -func (s *StepIOS) SwipeLeft() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ +func (s *StepIOS) SwipeLeft(options ...ActionOption) *StepIOS { + action := MobileAction{ Method: uiSwipe, Params: "left", - }) + } + for _, option := range options { + option(&action) + } + s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } -func (s *StepIOS) SwipeRight() *StepIOS { - s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ +func (s *StepIOS) SwipeRight(options ...ActionOption) *StepIOS { + action := MobileAction{ Method: uiSwipe, Params: "right", - }) + } + for _, option := range options { + option(&action) + } + s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } @@ -664,22 +688,22 @@ func (ud *uiDriver) doAction(action MobileAction) error { if len(location) != 2 { return fmt.Errorf("invalid tap location params: %v", location) } - return ud.TapXY(location[0], location[1]) + return ud.TapXY(location[0], location[1], action.Identifier) } return fmt.Errorf("invalid %s params: %v", uiTapXY, action.Params) case uiTap: if param, ok := action.Params.(string); ok { - return ud.Tap(param, action.IgnoreNotFoundError) + return ud.Tap(param, action.Identifier, action.IgnoreNotFoundError) } return fmt.Errorf("invalid %s params: %v", uiTap, action.Params) case uiTapByOCR: if ocrText, ok := action.Params.(string); ok { - return ud.TapByOCR(ocrText, action.IgnoreNotFoundError) + return ud.TapByOCR(ocrText, action.Identifier, action.IgnoreNotFoundError) } return fmt.Errorf("invalid %s params: %v", uiTapByOCR, action.Params) case uiTapByCV: if imagePath, ok := action.Params.(string); ok { - return ud.TapByCV(imagePath, action.IgnoreNotFoundError) + return ud.TapByCV(imagePath, action.Identifier, action.IgnoreNotFoundError) } return fmt.Errorf("invalid %s params: %v", uiTapByCV, action.Params) case uiDoubleTapXY: