mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-11 18:11:21 +08:00
change: TapByHierarchy
This commit is contained in:
@@ -548,6 +548,14 @@ func (ad *ADBDriver) ScreenShot(opts ...option.ActionOption) (raw *bytes.Buffer,
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) TapByHierarchy(text string, opts ...option.ActionOption) error {
|
||||
sourceTree, err := ad.sourceTree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ad.tapByTextUsingHierarchy(sourceTree, text, opts...)
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) Source(srcOpt ...option.SourceOption) (source string, err error) {
|
||||
_, err = ad.runShellCommand("rm", "-rf", "/sdcard/window_dump.xml")
|
||||
if err != nil {
|
||||
@@ -566,7 +574,7 @@ func (ad *ADBDriver) Source(srcOpt ...option.SourceOption) (source string, err e
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) sourceTree(srcOpt ...option.SourceOption) (sourceTree *Hierarchy, err error) {
|
||||
source, err := ad.Source()
|
||||
source, err := ad.Source(srcOpt...)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -578,14 +586,6 @@ func (ad *ADBDriver) sourceTree(srcOpt ...option.SourceOption) (sourceTree *Hier
|
||||
return
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) TapByText(text string, opts ...option.ActionOption) error {
|
||||
sourceTree, err := ad.sourceTree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ad.tapByTextUsingHierarchy(sourceTree, text, opts...)
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string, opts ...option.ActionOption) error {
|
||||
bounds := ad.searchNodes(hierarchy.Layout, text, opts...)
|
||||
actionOptions := option.NewActionOptions(opts...)
|
||||
@@ -606,21 +606,6 @@ func (ad *ADBDriver) tapByTextUsingHierarchy(hierarchy *Hierarchy, text string,
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) TapByTexts(actions ...TapTextAction) error {
|
||||
sourceTree, err := ad.sourceTree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, action := range actions {
|
||||
err := ad.tapByTextUsingHierarchy(sourceTree, action.Text, action.Options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) searchNodes(nodes []Layout, text string, opts ...option.ActionOption) []Bounds {
|
||||
actionOptions := option.NewActionOptions(opts...)
|
||||
var results []Bounds
|
||||
|
||||
@@ -2,18 +2,10 @@ package uixt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
builtinJSON "encoding/json"
|
||||
"fmt"
|
||||
_ "image/gif"
|
||||
_ "image/png"
|
||||
"regexp"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/internal/json"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
|
||||
@@ -109,172 +101,3 @@ type XTDriver struct {
|
||||
CVService ai.ICVService // OCR/CV
|
||||
LLMService ai.ILLMService // LLM
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) Setup() error {
|
||||
// unlock device screen
|
||||
err := dExt.Unlock()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("unlock device screen failed")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) assertOCR(text, assert string) error {
|
||||
var opts []option.ActionOption
|
||||
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text)))
|
||||
|
||||
switch assert {
|
||||
case AssertionEqual:
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "assert ocr equal failed")
|
||||
}
|
||||
case AssertionNotEqual:
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err == nil {
|
||||
return errors.New("assert ocr not equal failed")
|
||||
}
|
||||
case AssertionExists:
|
||||
opts = append(opts, option.WithRegex(true))
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "assert ocr exists failed")
|
||||
}
|
||||
case AssertionNotExists:
|
||||
opts = append(opts, option.WithRegex(true))
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err == nil {
|
||||
return errors.New("assert ocr not exists failed")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected assert method %s", assert)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) assertForegroundApp(appName, assert string) error {
|
||||
app, err := dExt.ForegroundInfo()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("get foreground app failed, skip app assertion")
|
||||
return nil // Notice: ignore error when get foreground app failed
|
||||
}
|
||||
|
||||
switch assert {
|
||||
case AssertionEqual:
|
||||
if app.PackageName != appName {
|
||||
return errors.Wrap(err, "assert foreground app equal failed")
|
||||
}
|
||||
case AssertionNotEqual:
|
||||
if app.PackageName == appName {
|
||||
return errors.New("assert foreground app not equal failed")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected assert method %s", assert)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) DoValidation(check, assert, expected string, message ...string) (err error) {
|
||||
switch check {
|
||||
case SelectorOCR:
|
||||
err = dExt.assertOCR(expected, assert)
|
||||
case SelectorForegroundApp:
|
||||
err = dExt.assertForegroundApp(expected, assert)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if message == nil {
|
||||
message = []string{""}
|
||||
}
|
||||
log.Error().Err(err).Str("assert", assert).Str("expect", expected).
|
||||
Str("msg", message[0]).Msg("validate failed")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Str("assert", assert).Str("expect", expected).Msg("validate success")
|
||||
return nil
|
||||
}
|
||||
|
||||
type DriverRawResponse []byte
|
||||
|
||||
func (r DriverRawResponse) CheckErr() (err error) {
|
||||
reply := new(struct {
|
||||
Value struct {
|
||||
Err string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Traceback string `json:"traceback"` // wda
|
||||
Stacktrace string `json:"stacktrace"` // uia
|
||||
}
|
||||
})
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return err
|
||||
}
|
||||
if reply.Value.Err != "" {
|
||||
errText := reply.Value.Message
|
||||
re := regexp.MustCompile(`{.+?=(.+?)}`)
|
||||
if re.MatchString(reply.Value.Message) {
|
||||
subMatch := re.FindStringSubmatch(reply.Value.Message)
|
||||
errText = subMatch[len(subMatch)-1]
|
||||
}
|
||||
return fmt.Errorf("%s: %s", reply.Value.Err, errText)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToString() (s string, err error) {
|
||||
reply := new(struct{ Value string })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return "", errors.Wrapf(err, "json.Unmarshal failed, rawResponse: %s", string(r))
|
||||
}
|
||||
s = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToBool() (b bool, err error) {
|
||||
reply := new(struct{ Value bool })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return false, err
|
||||
}
|
||||
b = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToSessionInfo() (sessionInfo Session, err error) {
|
||||
reply := new(struct{ Value struct{ Session } })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return Session{}, err
|
||||
}
|
||||
sessionInfo = reply.Value.Session
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) {
|
||||
reply := new(struct{ Value builtinJSON.RawMessage })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToJsonObject() (obj map[string]interface{}, err error) {
|
||||
if err = json.Unmarshal(r, &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueDecodeAsBase64() (raw *bytes.Buffer, err error) {
|
||||
str, err := r.ValueConvertToString()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert value to string")
|
||||
}
|
||||
decodeString, err := base64.StdEncoding.DecodeString(str)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode base64 string")
|
||||
}
|
||||
raw = bytes.NewBuffer(decodeString)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,11 +91,6 @@ func (ma MobileAction) GetOptions() []option.ActionOption {
|
||||
return actionOptionList
|
||||
}
|
||||
|
||||
type TapTextAction struct {
|
||||
Text string
|
||||
Options []option.ActionOption
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) ParseActionOptions(opts ...option.ActionOption) []option.ActionOption {
|
||||
actionOptions := option.NewActionOptions(opts...)
|
||||
|
||||
|
||||
@@ -3,19 +3,24 @@ package uixt
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
builtinJSON "encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/internal/json"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/internal/json"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
@@ -208,3 +213,85 @@ func NewHTTPClientWithConnection(conn net.Conn, timeout time.Duration) *http.Cli
|
||||
Timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
type DriverRawResponse []byte
|
||||
|
||||
func (r DriverRawResponse) CheckErr() (err error) {
|
||||
reply := new(struct {
|
||||
Value struct {
|
||||
Err string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
Traceback string `json:"traceback"` // wda
|
||||
Stacktrace string `json:"stacktrace"` // uia
|
||||
}
|
||||
})
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return err
|
||||
}
|
||||
if reply.Value.Err != "" {
|
||||
errText := reply.Value.Message
|
||||
re := regexp.MustCompile(`{.+?=(.+?)}`)
|
||||
if re.MatchString(reply.Value.Message) {
|
||||
subMatch := re.FindStringSubmatch(reply.Value.Message)
|
||||
errText = subMatch[len(subMatch)-1]
|
||||
}
|
||||
return fmt.Errorf("%s: %s", reply.Value.Err, errText)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToString() (s string, err error) {
|
||||
reply := new(struct{ Value string })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return "", errors.Wrapf(err, "json.Unmarshal failed, rawResponse: %s", string(r))
|
||||
}
|
||||
s = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToBool() (b bool, err error) {
|
||||
reply := new(struct{ Value bool })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return false, err
|
||||
}
|
||||
b = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToSessionInfo() (sessionInfo Session, err error) {
|
||||
reply := new(struct{ Value struct{ Session } })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return Session{}, err
|
||||
}
|
||||
sessionInfo = reply.Value.Session
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) {
|
||||
reply := new(struct{ Value builtinJSON.RawMessage })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueConvertToJsonObject() (obj map[string]interface{}, err error) {
|
||||
if err = json.Unmarshal(r, &obj); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r DriverRawResponse) ValueDecodeAsBase64() (raw *bytes.Buffer, err error) {
|
||||
str, err := r.ValueConvertToString()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to convert value to string")
|
||||
}
|
||||
decodeString, err := base64.StdEncoding.DecodeString(str)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode base64 string")
|
||||
}
|
||||
raw = bytes.NewBuffer(decodeString)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,8 +4,10 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/code"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
func convertToAbsolutePoint(driver IDriver, x, y float64) (absX, absY float64, err error) {
|
||||
@@ -56,3 +58,90 @@ func convertToAbsoluteCoordinates(driver IDriver, fromX, fromY, toX, toY float64
|
||||
func assertRelative(p float64) bool {
|
||||
return p >= 0 && p <= 1
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) Setup() error {
|
||||
// unlock device screen
|
||||
err := dExt.Unlock()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("unlock device screen failed")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) assertOCR(text, assert string) error {
|
||||
var opts []option.ActionOption
|
||||
opts = append(opts, option.WithScreenShotFileName(fmt.Sprintf("assert_ocr_%s", text)))
|
||||
|
||||
switch assert {
|
||||
case AssertionEqual:
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "assert ocr equal failed")
|
||||
}
|
||||
case AssertionNotEqual:
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err == nil {
|
||||
return errors.New("assert ocr not equal failed")
|
||||
}
|
||||
case AssertionExists:
|
||||
opts = append(opts, option.WithRegex(true))
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "assert ocr exists failed")
|
||||
}
|
||||
case AssertionNotExists:
|
||||
opts = append(opts, option.WithRegex(true))
|
||||
_, err := dExt.FindScreenText(text, opts...)
|
||||
if err == nil {
|
||||
return errors.New("assert ocr not exists failed")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected assert method %s", assert)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) assertForegroundApp(appName, assert string) error {
|
||||
app, err := dExt.ForegroundInfo()
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Msg("get foreground app failed, skip app assertion")
|
||||
return nil // Notice: ignore error when get foreground app failed
|
||||
}
|
||||
|
||||
switch assert {
|
||||
case AssertionEqual:
|
||||
if app.PackageName != appName {
|
||||
return errors.Wrap(err, "assert foreground app equal failed")
|
||||
}
|
||||
case AssertionNotEqual:
|
||||
if app.PackageName == appName {
|
||||
return errors.New("assert foreground app not equal failed")
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unexpected assert method %s", assert)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *XTDriver) DoValidation(check, assert, expected string, message ...string) (err error) {
|
||||
switch check {
|
||||
case SelectorOCR:
|
||||
err = dExt.assertOCR(expected, assert)
|
||||
case SelectorForegroundApp:
|
||||
err = dExt.assertForegroundApp(expected, assert)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if message == nil {
|
||||
message = []string{""}
|
||||
}
|
||||
log.Error().Err(err).Str("assert", assert).Str("expect", expected).
|
||||
Str("msg", message[0]).Msg("validate failed")
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info().Str("assert", assert).Str("expect", expected).Msg("validate success")
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user