diff --git a/hrp/step.go b/hrp/step.go index 63390399..507182a4 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -33,9 +33,10 @@ const ( // UI handling uiHome MobileMethod = "home" - uiClick MobileMethod = "click" - uiDoubleClick MobileMethod = "double_click" - uiLongClick MobileMethod = "long_click" + uiTapXY MobileMethod = "tap_xy" + uiTap MobileMethod = "tap" + uiDoubleTapXY MobileMethod = "double_tap_xy" + uiDoubleTap MobileMethod = "double_tap" uiSwipe MobileMethod = "swipe" uiInput MobileMethod = "input" diff --git a/hrp/step_android_ui.go b/hrp/step_android_ui.go index 14eb76d8..8dac1687 100644 --- a/hrp/step_android_ui.go +++ b/hrp/step_android_ui.go @@ -66,25 +66,17 @@ func (s *StepAndroid) StopRecording() *StepAndroid { return &StepAndroid{step: s.step} } -func (s *StepAndroid) Click(params interface{}) *StepAndroid { +func (s *StepAndroid) Tap(params interface{}) *StepAndroid { s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ - Method: uiClick, + Method: uiTap, Params: params, }) return &StepAndroid{step: s.step} } -func (s *StepAndroid) DoubleClick(params interface{}) *StepAndroid { +func (s *StepAndroid) DoubleTap(params interface{}) *StepAndroid { s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ - Method: uiDoubleClick, - Params: params, - }) - return &StepAndroid{step: s.step} -} - -func (s *StepAndroid) LongClick(params interface{}) *StepAndroid { - s.step.Android.Actions = append(s.step.Android.Actions, MobileAction{ - Method: uiLongClick, + Method: uiDoubleTap, Params: params, }) return &StepAndroid{step: s.step} diff --git a/hrp/step_ios_ui.go b/hrp/step_ios_ui.go index e7c30dd5..6b9358b8 100644 --- a/hrp/step_ios_ui.go +++ b/hrp/step_ios_ui.go @@ -2,12 +2,11 @@ package hrp import ( "fmt" - "net/http" "strings" "time" - gwdaExt "github.com/debugtalk/gwda-ext" "github.com/electricbubble/gwda" + "github.com/httprunner/uixt" "github.com/pkg/errors" "github.com/rs/zerolog/log" ) @@ -93,25 +92,36 @@ func (s *StepIOS) Home() *StepIOS { return &StepIOS{step: s.step} } -func (s *StepIOS) Click(params interface{}) *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{ - Method: uiClick, + Method: uiTapXY, + Params: []float64{x, y}, + }) + return &StepIOS{step: s.step} +} + +// Tap taps on the target element +func (s *StepIOS) Tap(params string) *StepIOS { + s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ + Method: uiTap, Params: params, }) return &StepIOS{step: s.step} } -func (s *StepIOS) DoubleClick(params interface{}) *StepIOS { +// DoubleTapXY double taps the point {X,Y}, X & Y is percentage of coordinates +func (s *StepIOS) DoubleTapXY(x, y float64) *StepIOS { s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ - Method: uiDoubleClick, - Params: params, + Method: uiDoubleTapXY, + Params: []float64{x, y}, }) return &StepIOS{step: s.step} } -func (s *StepIOS) LongClick(params interface{}) *StepIOS { +func (s *StepIOS) DoubleTap(params string) *StepIOS { s.step.IOS.Actions = append(s.step.IOS.Actions, MobileAction{ - Method: uiLongClick, + Method: uiDoubleTap, Params: params, }) return &StepIOS{step: s.step} @@ -385,7 +395,7 @@ func (r *HRPRunner) InitWDAClient(device WDADevice) (client *wdaClient, err erro if err != nil { return nil, errors.Wrap(err, "failed to init WDA driver") } - driverExt, err := gwdaExt.Extend(driver, 0.95) + driverExt, err := uixt.Extend(driver, 0.95) if err != nil { return nil, errors.Wrap(err, "failed to extend gwda.WebDriver") } @@ -407,13 +417,9 @@ func (r *HRPRunner) InitWDAClient(device WDADevice) (client *wdaClient, err erro // cache wda client r.wdaClients = make(map[string]*wdaClient) client = &wdaClient{ - ID: time.Now().Unix(), Device: targetDevice, DriverExt: driverExt, WindowSize: windowSize, - httpClient: &http.Client{ - Timeout: 10 * time.Second, - }, } r.wdaClients[targetDevice.SerialNumber()] = client @@ -476,11 +482,9 @@ func runStepIOS(s *SessionRunner, step *TStep) (stepResult *StepResult, err erro var errActionNotImplemented = errors.New("UI action not implemented") type wdaClient struct { - ID int64 Device *gwda.Device - DriverExt *gwdaExt.DriverExt + DriverExt *uixt.DriverExt WindowSize gwda.Size - httpClient *http.Client } func (w *wdaClient) doAction(action MobileAction) error { @@ -514,78 +518,39 @@ func (w *wdaClient) doAction(action MobileAction) error { return fmt.Errorf("app_terminate params should be bundleId(string), got %v", action.Params) case uiHome: return w.DriverExt.Homescreen() - case uiClick: - // click on coordinate - if location, ok := action.Params.([]int); ok { - // absolute x,y - if len(location) != 2 { - return fmt.Errorf("invalid click location params: %v", location) - } - return w.DriverExt.WebDriver.Tap(location[0], location[1]) - } + case uiTapXY: if location, ok := action.Params.([]float64); ok { - // relative x,y of window size + // relative x,y of window size: [0.5, 0.5] if len(location) != 2 { - return fmt.Errorf("invalid click location params: %v", location) + return fmt.Errorf("invalid tap location params: %v", location) } - x := location[0] * float64(w.WindowSize.Width) - y := location[1] * float64(w.WindowSize.Height) - return w.DriverExt.TapFloat(x, y) + return w.DriverExt.TapXY(location[0], location[1]) } - // click on name or xpath + return fmt.Errorf("invalid %s params: %v", uiTapXY, action.Params) + case uiTap: if param, ok := action.Params.(string); ok { - ele, err := w.findElement(param) - if err != nil { - return errors.Wrap(err, "failed to find element") - } - return ele.Click() + return w.DriverExt.Tap(param) } - return fmt.Errorf("invalid click params: %v", action.Params) - case uiDoubleClick: - // double click on name or xpath + return fmt.Errorf("invalid %s params: %v", uiTap, action.Params) + case uiDoubleTapXY: + if location, ok := action.Params.([]float64); ok { + // relative x,y of window size: [0.5, 0.5] + if len(location) != 2 { + return fmt.Errorf("invalid tap location params: %v", location) + } + return w.DriverExt.DoubleTapXY(location[0], location[1]) + } + return fmt.Errorf("invalid %s params: %v", uiDoubleTapXY, action.Params) + case uiDoubleTap: if param, ok := action.Params.(string); ok { - ele, err := w.findElement(param) - if err != nil { - return errors.Wrap(err, "failed to find element") - } - return ele.DoubleTap() + return w.DriverExt.DoubleTap(param) } - return fmt.Errorf("invalid click params: %v", action.Params) - case uiLongClick: - // long click 2s on name or xpath - if param, ok := action.Params.(string); ok { - ele, err := w.findElement(param) - if err != nil { - return errors.Wrap(err, "failed to find element") - } - return ele.TouchAndHold(2) - } - return fmt.Errorf("invalid click params: %v", action.Params) + return fmt.Errorf("invalid %s params: %v", uiDoubleTap, action.Params) case uiSwipe: - width := w.WindowSize.Width - height := w.WindowSize.Height - - var fromX, fromY, toX, toY int - if direction, ok := action.Params.(string); ok { - switch direction { - case "up": - fromX, fromY, toX, toY = width/2, height*3/4, width/2, height*1/4 - case "down": - fromX, fromY, toX, toY = width/2, height*1/4, width/2, height*3/4 - case "left": - fromX, fromY, toX, toY = width*3/4, height/2, width*1/4, height/2 - case "right": - fromX, fromY, toX, toY = width*1/4, height/2, width*3/4, height/2 - } - } else if params, ok := action.Params.([]int); ok { - if len(params) != 4 { - return fmt.Errorf("invalid swipe params: %v", params) - } - fromX, fromY, toX, toY = params[0], params[1], params[2], params[3] - } else { - return fmt.Errorf("invalid swipe params: %v", action.Params) + if param, ok := action.Params.(string); ok { + return w.DriverExt.SwipeTo(param) } - return w.DriverExt.WebDriver.Swipe(fromX, fromY, toX, toY) + return fmt.Errorf("invalid %s params: %v", uiSwipe, action.Params) case uiInput: // input text on current active element // append \n to send text with enter diff --git a/hrp/step_ios_ui_test.go b/hrp/step_ios_ui_test.go index 9ce227bb..246ede90 100644 --- a/hrp/step_ios_ui_test.go +++ b/hrp/step_ios_ui_test.go @@ -10,7 +10,7 @@ func TestIOSSettingsAction(t *testing.T) { Config: NewConfig("ios ui action on Settings"), TestSteps: []IStep{ NewStep("launch Settings"). - IOS().Home().Click("//*[@label='设置']"). + IOS().Home().Tap("//*[@label='设置']"). Validate(). AssertNameExists("飞行模式"). AssertNameNotExists("飞行模式2"), @@ -31,7 +31,7 @@ func TestIOSSearchApp(t *testing.T) { Config: NewConfig("ios ui action on Search App 资源库"), TestSteps: []IStep{ NewStep("进入 App 资源库 搜索框"). - IOS().Home().SwipeLeft().Times(2).Click("dewey-search-field"). + IOS().Home().SwipeLeft().Times(2).Tap("dewey-search-field"). Validate(). AssertNameExists("取消"), NewStep("搜索抖音"). @@ -78,13 +78,13 @@ func TestIOSWeixinLive(t *testing.T) { IOS(). Home(). AppTerminate("com.tencent.xin"). // 关闭已运行的微信,确保启动微信后在「微信」首页 - Click("微信"). + Tap("微信"). Validate(). AssertNameExists("通讯录", "微信启动失败,「通讯录」不存在"), NewStep("进入直播页"). IOS(). - Click("发现").Sleep(5). // 进入「发现页」;等待 5 秒确保加载完成 - Click([]float64{0.5, 0.3}). // 基于坐标位置点击「直播」;TODO:通过 OCR 识别「直播」 + Tap("发现").Sleep(5). // 进入「发现页」;等待 5 秒确保加载完成 + TapXY(0.5, 0.3). // 基于坐标位置点击「直播」;TODO:通过 OCR 识别「直播」 Validate(). AssertNameExists("直播"), NewStep("向上滑动 5 次"). @@ -112,7 +112,7 @@ func TestIOSCameraPhotoCapture(t *testing.T) { Validate(). AssertNameExists("PhotoCapture", "拍照按钮不存在"), NewStep("start recording"). - IOS().Click("PhotoCapture"), + IOS().Tap("PhotoCapture"), }, } fmt.Println(testCase) @@ -140,9 +140,9 @@ func TestIOSCameraVideoCapture(t *testing.T) { AssertNameExists("VideoCapture", "拍摄按钮不存在"), NewStep("start recording"). IOS(). - Click("VideoCapture"). // 开始录像 + Tap("VideoCapture"). // 开始录像 Sleep(5). - Click("VideoCapture"), // 停止录像 + Tap("VideoCapture"), // 停止录像 }, } fmt.Println(testCase) @@ -158,7 +158,7 @@ func TestIOSDouyinAction(t *testing.T) { Config: NewConfig("ios ui action on 抖音"), TestSteps: []IStep{ NewStep("launch douyin"). - IOS().Home().Click("//*[@label='抖音']"). + IOS().Home().Tap("//*[@label='抖音']"). Validate(). AssertNameExists("首页", "首页 tab 不存在"). AssertNameExists("消息", "消息 tab 不存在"),