mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-11 15:31:37 +08:00
feat: validate UI exists
This commit is contained in:
@@ -172,11 +172,11 @@ type StepAndroidValidation struct {
|
|||||||
step *TStep
|
step *TStep
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepAndroidValidation) AssertTextExists(expectedText string, msg string) *StepAndroidValidation {
|
func (s *StepAndroidValidation) AssertXpathExists(expectedXpath string, msg string) *StepAndroidValidation {
|
||||||
v := Validator{
|
v := Validator{
|
||||||
Check: "android_ui",
|
Check: "UI",
|
||||||
Assert: "text_exists",
|
Assert: "xpath_exists",
|
||||||
Expect: expectedText,
|
Expect: expectedXpath,
|
||||||
Message: msg,
|
Message: msg,
|
||||||
}
|
}
|
||||||
s.step.Validators = append(s.step.Validators, v)
|
s.step.Validators = append(s.step.Validators, v)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package hrp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/electricbubble/gwda"
|
"github.com/electricbubble/gwda"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@@ -140,11 +141,44 @@ type StepIOSValidation struct {
|
|||||||
step *TStep
|
step *TStep
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StepIOSValidation) AssertTextExists(expectedText string, msg string) *StepIOSValidation {
|
func (s *StepIOSValidation) AssertNameExists(expectedName string, msg string) *StepIOSValidation {
|
||||||
v := Validator{
|
v := Validator{
|
||||||
Check: "ios_ui",
|
Check: "UI",
|
||||||
Assert: "text_exists",
|
Assert: "name_exists",
|
||||||
Expect: expectedText,
|
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,
|
Message: msg,
|
||||||
}
|
}
|
||||||
s.step.Validators = append(s.step.Validators, v)
|
s.step.Validators = append(s.step.Validators, v)
|
||||||
@@ -282,9 +316,14 @@ func runStepIOS(r *SessionRunner, step *TStep) (stepResult *StepResult, err erro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// do validation
|
// validate
|
||||||
// step.Validators
|
validateResults, err := wdaClient.doValidation(step.Validators)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sessionData := newSessionData()
|
||||||
|
sessionData.Validators = validateResults
|
||||||
|
stepResult.Data = sessionData
|
||||||
stepResult.Success = true
|
stepResult.Success = true
|
||||||
return stepResult, nil
|
return stepResult, nil
|
||||||
}
|
}
|
||||||
@@ -314,11 +353,21 @@ func (w *wdaClient) doAction(action MobileAction) error {
|
|||||||
}
|
}
|
||||||
return w.Driver.Tap(location[0], location[1])
|
return w.Driver.Tap(location[0], location[1])
|
||||||
}
|
}
|
||||||
// click on xpath
|
// click on name or xpath
|
||||||
if xpath, ok := action.Params.(string); ok {
|
if param, ok := action.Params.(string); ok {
|
||||||
selector := gwda.BySelector{
|
var selector gwda.BySelector
|
||||||
XPath: xpath,
|
if strings.HasPrefix(param, "/") {
|
||||||
|
// xpath
|
||||||
|
selector = gwda.BySelector{
|
||||||
|
XPath: param,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// name
|
||||||
|
selector = gwda.BySelector{
|
||||||
|
Name: param,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ele, err := w.Driver.FindElement(selector)
|
ele, err := w.Driver.FindElement(selector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "failed to find element")
|
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 {
|
if direction, ok := action.Params.(string); ok {
|
||||||
switch direction {
|
switch direction {
|
||||||
case "up":
|
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
|
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":
|
case "left":
|
||||||
fromX, fromY, toX, toY = width*3/4, height/2, width*1/4, height/2
|
fromX, fromY, toX, toY = width*3/4, height/2, width*1/4, height/2
|
||||||
case "right":
|
case "right":
|
||||||
@@ -367,7 +416,74 @@ func (w *wdaClient) doAction(action MobileAction) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *wdaClient) doValidation() error {
|
func (w *wdaClient) doValidation(iValidators []interface{}) (validateResults []*ValidationResult, err error) {
|
||||||
// TODO
|
for _, iValidator := range iValidators {
|
||||||
return errActionNotImplemented
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ func TestAndroidAction(t *testing.T) {
|
|||||||
NewStep("launch douyin").
|
NewStep("launch douyin").
|
||||||
Android().Serial("xxx").Click("抖音").
|
Android().Serial("xxx").Click("抖音").
|
||||||
Validate().
|
Validate().
|
||||||
AssertTextExists("首页", "首页 tab 不存在").
|
AssertXpathExists("首页", "首页 tab 不存在").
|
||||||
AssertTextExists("消息", "消息 tab 不存在"),
|
AssertXpathExists("消息", "消息 tab 不存在"),
|
||||||
NewStep("swipe up and down").
|
NewStep("swipe up and down").
|
||||||
Android().Serial("xxx").SwipeUp().SwipeUp().SwipeDown(),
|
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{
|
testCase := &TestCase{
|
||||||
Config: NewConfig("ios ui action"),
|
Config: NewConfig("ios ui action on Settings"),
|
||||||
TestSteps: []IStep{
|
TestSteps: []IStep{
|
||||||
NewStep("launch douyin").
|
NewStep("launch Settings").
|
||||||
IOS().Click("//*[@label='抖音']").
|
IOS().Click("//*[@label='设置']").
|
||||||
Validate().
|
Validate().
|
||||||
AssertTextExists("首页", "首页 tab 不存在").
|
AssertNameExists("飞行模式", "「飞行模式」不存在").
|
||||||
AssertTextExists("消息", "消息 tab 不存在"),
|
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").
|
NewStep("swipe up and down").
|
||||||
IOS().SwipeUp().SwipeUp().SwipeDown(),
|
IOS().SwipeUp().SwipeUp().SwipeDown(),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user