diff --git a/hrp/step_android_ui.go b/hrp/step_android_ui.go index f49adb6d..106c5a76 100644 --- a/hrp/step_android_ui.go +++ b/hrp/step_android_ui.go @@ -172,11 +172,11 @@ type StepAndroidValidation struct { step *TStep } -func (s *StepAndroidValidation) AssertTextExists(expectedText string, msg string) *StepAndroidValidation { +func (s *StepAndroidValidation) AssertXpathExists(expectedXpath string, msg string) *StepAndroidValidation { v := Validator{ - Check: "android_ui", - Assert: "text_exists", - Expect: expectedText, + Check: "UI", + Assert: "xpath_exists", + Expect: expectedXpath, Message: msg, } s.step.Validators = append(s.step.Validators, v) diff --git a/hrp/step_ios_ui.go b/hrp/step_ios_ui.go index aca120e4..e466da98 100644 --- a/hrp/step_ios_ui.go +++ b/hrp/step_ios_ui.go @@ -2,6 +2,7 @@ package hrp import ( "fmt" + "strings" "github.com/electricbubble/gwda" "github.com/pkg/errors" @@ -140,11 +141,44 @@ type StepIOSValidation struct { step *TStep } -func (s *StepIOSValidation) AssertTextExists(expectedText string, msg string) *StepIOSValidation { +func (s *StepIOSValidation) AssertNameExists(expectedName string, msg string) *StepIOSValidation { v := Validator{ - Check: "ios_ui", - Assert: "text_exists", - Expect: expectedText, + Check: "UI", + Assert: "name_exists", + Expect: expectedName, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} + +func (s *StepIOSValidation) AssertNameNotExists(expectedName string, msg string) *StepIOSValidation { + v := Validator{ + Check: "UI", + Assert: "name_not_exists", + Expect: expectedName, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} + +func (s *StepIOSValidation) AssertXpathExists(expectedXpath string, msg string) *StepIOSValidation { + v := Validator{ + Check: "UI", + Assert: "xpath_exists", + Expect: expectedXpath, + Message: msg, + } + s.step.Validators = append(s.step.Validators, v) + return s +} + +func (s *StepIOSValidation) AssertXpathNotExists(expectedXpath string, msg string) *StepIOSValidation { + v := Validator{ + Check: "UI", + Assert: "xpath_not_exists", + Expect: expectedXpath, Message: msg, } s.step.Validators = append(s.step.Validators, v) @@ -282,9 +316,14 @@ func runStepIOS(r *SessionRunner, step *TStep) (stepResult *StepResult, err erro } } - // do validation - // step.Validators - + // validate + validateResults, err := wdaClient.doValidation(step.Validators) + if err != nil { + return + } + sessionData := newSessionData() + sessionData.Validators = validateResults + stepResult.Data = sessionData stepResult.Success = true return stepResult, nil } @@ -314,11 +353,21 @@ func (w *wdaClient) doAction(action MobileAction) error { } return w.Driver.Tap(location[0], location[1]) } - // click on xpath - if xpath, ok := action.Params.(string); ok { - selector := gwda.BySelector{ - XPath: xpath, + // click on name or xpath + if param, ok := action.Params.(string); ok { + var selector gwda.BySelector + if strings.HasPrefix(param, "/") { + // xpath + selector = gwda.BySelector{ + XPath: param, + } + } else { + // name + selector = gwda.BySelector{ + Name: param, + } } + ele, err := w.Driver.FindElement(selector) if err != nil { return errors.Wrap(err, "failed to find element") @@ -340,9 +389,9 @@ func (w *wdaClient) doAction(action MobileAction) error { if direction, ok := action.Params.(string); ok { switch direction { case "up": - fromX, fromY, toX, toY = width/2, height*1/4, width/2, height*3/4 - case "down": 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": @@ -367,7 +416,74 @@ func (w *wdaClient) doAction(action MobileAction) error { return nil } -func (w *wdaClient) doValidation() error { - // TODO - return errActionNotImplemented +func (w *wdaClient) doValidation(iValidators []interface{}) (validateResults []*ValidationResult, err error) { + for _, iValidator := range iValidators { + validator, ok := iValidator.(Validator) + if !ok { + return nil, errors.New("validator type error") + } + + validataResult := &ValidationResult{ + Validator: validator, + CheckResult: "fail", + } + + // parse check value + if validator.Check != "UI" { + validataResult.CheckResult = "skip" + log.Warn().Interface("validator", validator).Msg("skip validator") + validateResults = append(validateResults, validataResult) + continue + } + + expected, ok := validator.Expect.(string) + if !ok { + return nil, errors.New("validator expect should be string") + } + + var result bool + switch validator.Assert { + case "xpath_exists": + result = w.assertXpath(expected, true) + case "xpath_not_exists": + result = w.assertXpath(expected, false) + case "name_exists": + result = w.assertName(expected, true) + case "name_not_exists": + result = w.assertName(expected, false) + } + if result { + log.Info(). + Str("assert", validator.Assert). + Str("expect", expected). + Msg("validate UI success") + validataResult.CheckResult = "pass" + validateResults = append(validateResults, validataResult) + } else { + log.Error(). + Str("assert", validator.Assert). + Str("expect", expected). + Str("msg", validator.Message). + Msg("validate UI failed") + validateResults = append(validateResults, validataResult) + err = errors.New("step validation failed") + } + } + return +} + +func (w *wdaClient) assertName(name string, exists bool) bool { + selector := gwda.BySelector{ + Name: name, + } + _, err := w.Driver.FindElement(selector) + return exists == (err == nil) +} + +func (w *wdaClient) assertXpath(xpath string, exists bool) bool { + selector := gwda.BySelector{ + XPath: xpath, + } + _, err := w.Driver.FindElement(selector) + return exists == (err == nil) } diff --git a/hrp/step_ui_test.go b/hrp/step_ui_test.go index c49c479d..2dc01076 100644 --- a/hrp/step_ui_test.go +++ b/hrp/step_ui_test.go @@ -12,8 +12,8 @@ func TestAndroidAction(t *testing.T) { NewStep("launch douyin"). Android().Serial("xxx").Click("抖音"). Validate(). - AssertTextExists("首页", "首页 tab 不存在"). - AssertTextExists("消息", "消息 tab 不存在"), + AssertXpathExists("首页", "首页 tab 不存在"). + AssertXpathExists("消息", "消息 tab 不存在"), NewStep("swipe up and down"). Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(), }, @@ -27,15 +27,35 @@ func TestAndroidAction(t *testing.T) { } } -func TestIOSAction(t *testing.T) { +func TestIOSSettingsAction(t *testing.T) { testCase := &TestCase{ - Config: NewConfig("ios ui action"), + Config: NewConfig("ios ui action on Settings"), TestSteps: []IStep{ - NewStep("launch douyin"). - IOS().Click("//*[@label='抖音']"). + NewStep("launch Settings"). + IOS().Click("//*[@label='设置']"). Validate(). - AssertTextExists("首页", "首页 tab 不存在"). - AssertTextExists("消息", "消息 tab 不存在"), + AssertNameExists("飞行模式", "「飞行模式」不存在"). + AssertNameNotExists("飞行模式2", "「飞行模式2」不存在"), + NewStep("swipe up and down"). + IOS().SwipeUp().SwipeUp().SwipeDown(), + }, + } + + err := NewRunner(t).Run(testCase) + if err != nil { + t.Fatal(err) + } +} + +func TestIOSDouyinAction(t *testing.T) { + testCase := &TestCase{ + Config: NewConfig("ios ui action on 抖音"), + TestSteps: []IStep{ + NewStep("launch douyin"). + IOS().Click("//*[@label='抖音']"). + Validate(). + AssertNameExists("首页", "首页 tab 不存在"). + AssertNameExists("消息", "消息 tab 不存在"), NewStep("swipe up and down"). IOS().SwipeUp().SwipeUp().SwipeDown(), },