From c4d23f3bdd4d93c79777a912e112fd5940f0d2e5 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Thu, 1 Sep 2022 23:54:27 +0800 Subject: [PATCH] fix: hrp run ios UI tests --- examples/uitest/demo_weixin_live.json | 102 ++++++++++++++++++++++++++ examples/uitest/demo_weixin_test.go | 2 +- hrp/step.go | 14 ++-- hrp/step_ios_ui.go | 26 +++---- hrp/testcase.go | 8 ++ 5 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 examples/uitest/demo_weixin_live.json diff --git a/examples/uitest/demo_weixin_live.json b/examples/uitest/demo_weixin_live.json new file mode 100644 index 00000000..a789f0ee --- /dev/null +++ b/examples/uitest/demo_weixin_live.json @@ -0,0 +1,102 @@ +{ + "config": { + "name": "通过 feed 卡片进入微信直播间" + }, + "teststeps": [ + { + "name": "启动微信", + "ios": { + "actions": [ + { + "method": "home" + }, + { + "method": "app_terminate", + "params": "com.tencent.xin" + }, + { + "method": "swipe_to_tap_app", + "params": "微信", + "max_retry_times": 5 + } + ] + }, + "validate": [ + { + "check": "ui_label", + "assert": "exists", + "expect": "通讯录", + "msg": "微信启动失败,「通讯录」不存在" + } + ] + }, + { + "name": "进入直播页", + "ios": { + "actions": [ + { + "method": "tap", + "params": "发现" + }, + { + "method": "tap_ocr", + "params": "视频号" + } + ] + } + }, + { + "name": "处理青少年弹窗", + "ios": { + "actions": [ + { + "method": "tap_ocr", + "params": "我知道了", + "ignore_NotFoundError": true + } + ] + } + }, + { + "name": "在推荐页上划,直到出现「轻触进入直播间」", + "ios": { + "actions": [ + { + "method": "swipe_to_tap_text", + "params": "轻触进入直播间", + "max_retry_times": 10 + } + ] + } + }, + { + "name": "向上滑动,等待 10s", + "ios": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep", + "params": 10 + }, + { + "method": "screenshot" + }, + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep", + "params": 10 + }, + { + "method": "screenshot" + } + ] + } + } + ] +} diff --git a/examples/uitest/demo_weixin_test.go b/examples/uitest/demo_weixin_test.go index cd5cbda3..05200abe 100644 --- a/examples/uitest/demo_weixin_test.go +++ b/examples/uitest/demo_weixin_test.go @@ -34,7 +34,7 @@ func TestIOSWeixinLive(t *testing.T) { SwipeUp().Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s,截图保存 }, } - fmt.Println(testCase) + if err := testCase.Dump2JSON("demo_weixin_live.json"); err != nil { t.Fatal(err) } diff --git a/hrp/step.go b/hrp/step.go index 8636d0c3..2a219bb7 100644 --- a/hrp/step.go +++ b/hrp/step.go @@ -56,31 +56,31 @@ const ( ) type MobileAction struct { - Method MobileMethod `json:"method" yaml:"method"` + Method MobileMethod `json:"method,omitempty" yaml:"method,omitempty"` Params interface{} `json:"params,omitempty" yaml:"params,omitempty"` - maxRetryTimes int // max retry times - timeout int // TODO: wait timeout in seconds for mobile action - ignoreNotFoundError bool // ignore error if target element not found + 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 WithMaxRetryTimes(maxRetryTimes int) ActionOption { return func(o *MobileAction) { - o.maxRetryTimes = maxRetryTimes + o.MaxRetryTimes = maxRetryTimes } } func WithTimeout(timeout int) ActionOption { return func(o *MobileAction) { - o.timeout = timeout + o.Timeout = timeout } } func WithIgnoreNotFoundError(ignoreError bool) ActionOption { return func(o *MobileAction) { - o.ignoreNotFoundError = ignoreError + o.IgnoreNotFoundError = ignoreError } } diff --git a/hrp/step_ios_ui.go b/hrp/step_ios_ui.go index 6cce8217..d8cf5382 100644 --- a/hrp/step_ios_ui.go +++ b/hrp/step_ios_ui.go @@ -195,10 +195,6 @@ func (s *StepIOS) SwipeToTapApp(appName string, options ...ActionOption) *StepIO for _, option := range options { option(&action) } - // default to retry 5 times - if action.maxRetryTimes == 0 { - action.maxRetryTimes = 5 - } s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } @@ -211,10 +207,6 @@ func (s *StepIOS) SwipeToTapText(text string, options ...ActionOption) *StepIOS for _, option := range options { option(&action) } - // default to retry 10 times - if action.maxRetryTimes == 0 { - action.maxRetryTimes = 10 - } s.step.IOS.Actions = append(s.step.IOS.Actions, action) return &StepIOS{step: s.step} } @@ -620,8 +612,12 @@ func (ud *uiDriver) doAction(action MobileAction) error { ud.SwipeTo("right") } + // default to retry 5 times + if action.MaxRetryTimes == 0 { + action.MaxRetryTimes = 5 + } // swipe next screen until app found - return ud.SwipeUntil("left", findApp, foundAppAction, action.maxRetryTimes) + return ud.SwipeUntil("left", findApp, foundAppAction, action.MaxRetryTimes) } return fmt.Errorf("invalid %s params, should be app name(string), got %v", swipeToTapApp, action.Params) @@ -638,8 +634,12 @@ func (ud *uiDriver) doAction(action MobileAction) error { return d.TapFloat(x+width*0.5, y+height*0.5) } + // default to retry 10 times + if action.MaxRetryTimes == 0 { + action.MaxRetryTimes = 10 + } // swipe until live room found - return ud.SwipeUntil("up", findText, foundTextAction, 20) + return ud.SwipeUntil("up", findText, foundTextAction, action.MaxRetryTimes) } return fmt.Errorf("invalid %s params, should be app text(string), got %v", swipeToTapText, action.Params) @@ -668,17 +668,17 @@ func (ud *uiDriver) doAction(action MobileAction) error { 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.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.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.IgnoreNotFoundError) } return fmt.Errorf("invalid %s params: %v", uiTapByCV, action.Params) case uiDoubleTapXY: diff --git a/hrp/testcase.go b/hrp/testcase.go index 4bf3315b..6f4bd46b 100644 --- a/hrp/testcase.go +++ b/hrp/testcase.go @@ -260,6 +260,14 @@ func (tc *TCase) toTestCase() (*TestCase, error) { testCase.TestSteps = append(testCase.TestSteps, &StepWebSocket{ step: step, }) + } else if step.IOS != nil { + testCase.TestSteps = append(testCase.TestSteps, &StepIOS{ + step: step, + }) + } else if step.Android != nil { + testCase.TestSteps = append(testCase.TestSteps, &StepAndroid{ + step: step, + }) } else { log.Warn().Interface("step", step).Msg("[convertTestCase] unexpected step") }