mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
479 lines
16 KiB
Go
479 lines
16 KiB
Go
package uixt
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
|
)
|
|
|
|
// All elements returned by search endpoints have assigned element_id.
|
|
// Given element_id you can query properties like:
|
|
// enabled, rect, size, location, text, displayed, accessible, name
|
|
type wdaElement struct {
|
|
parent *wdaDriver
|
|
id string // element_id
|
|
}
|
|
|
|
func (we wdaElement) Click() (err error) {
|
|
// [[FBRoute POST:@"/element/:uuid/click"] respondWithTarget:self action:@selector(handleClick:)]
|
|
_, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/element", we.id, "/click")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) SendKeys(text string, options ...DataOption) (err error) {
|
|
// [[FBRoute POST:@"/element/:uuid/value"] respondWithTarget:self action:@selector(handleSetValue:)]
|
|
data := map[string]interface{}{
|
|
"value": strings.Split(text, ""),
|
|
}
|
|
// new data options in post data for extra uiautomator configurations
|
|
d := NewData(data, options...)
|
|
|
|
_, err = we.parent.httpPOST(d.Data, "/session", we.parent.sessionId, "/element", we.id, "/value")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Clear() (err error) {
|
|
// [[FBRoute POST:@"/element/:uuid/clear"] respondWithTarget:self action:@selector(handleClear:)]
|
|
_, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/element", we.id, "/clear")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Tap(x, y int) error {
|
|
return we.TapFloat(float64(x), float64(y))
|
|
}
|
|
|
|
func (we wdaElement) TapFloat(x, y float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/tap/:uuid"] respondWithTarget:self action:@selector(handleTap:)]
|
|
data := map[string]interface{}{
|
|
"x": x,
|
|
"y": y,
|
|
}
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/tap/", we.id)
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) DoubleTap() (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/doubleTap"] respondWithTarget:self action:@selector(handleDoubleTap:)]
|
|
_, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/wda/element", we.id, "/doubleTap")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) TouchAndHold(second ...float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/touchAndHold"] respondWithTarget:self action:@selector(handleTouchAndHold:)]
|
|
data := make(map[string]interface{})
|
|
if len(second) == 0 || second[0] <= 0 {
|
|
second = []float64{1.0}
|
|
}
|
|
data["duration"] = second[0]
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/touchAndHold")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) TwoFingerTap() (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/twoFingerTap"] respondWithTarget:self action:@selector(handleTwoFingerTap:)]
|
|
_, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/wda/element", we.id, "/twoFingerTap")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) TapWithNumberOfTaps(numberOfTaps, numberOfTouches int) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/tapWithNumberOfTaps"] respondWithTarget:self action:@selector(handleTapWithNumberOfTaps:)]
|
|
if numberOfTouches <= 0 {
|
|
return errors.New("'numberOfTouches' must be greater than zero")
|
|
}
|
|
if numberOfTouches > 5 {
|
|
return errors.New("'numberOfTouches' cannot be greater than 5")
|
|
}
|
|
if numberOfTaps <= 0 {
|
|
return errors.New("'numberOfTaps' must be greater than zero")
|
|
}
|
|
if numberOfTaps > 10 {
|
|
return errors.New("'numberOfTaps' cannot be greater than 10")
|
|
}
|
|
data := map[string]interface{}{
|
|
"numberOfTaps": numberOfTaps,
|
|
"numberOfTouches": numberOfTouches,
|
|
}
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/tapWithNumberOfTaps")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) ForceTouch(pressure float64, second ...float64) (err error) {
|
|
return we.ForceTouchFloat(-1, -1, pressure, second...)
|
|
}
|
|
|
|
func (we wdaElement) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/forceTouch"] respondWithTarget:self action:@selector(handleForceTouch:)]
|
|
data := make(map[string]interface{})
|
|
if x != -1 && y != -1 {
|
|
data["x"] = x
|
|
data["y"] = y
|
|
}
|
|
if len(second) == 0 || second[0] <= 0 {
|
|
second = []float64{1.0}
|
|
}
|
|
data["pressure"] = pressure
|
|
data["duration"] = second[0]
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/forceTouch")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Drag(fromX, fromY, toX, toY int, pressForDuration ...float64) error {
|
|
return we.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), pressForDuration...)
|
|
}
|
|
|
|
func (we wdaElement) DragFloat(fromX, fromY, toX, toY float64, pressForDuration ...float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/dragfromtoforduration"] respondWithTarget:self action:@selector(handleDrag:)]
|
|
data := map[string]interface{}{
|
|
"fromX": fromX,
|
|
"fromY": fromY,
|
|
"toX": toX,
|
|
"toY": toY,
|
|
}
|
|
if len(pressForDuration) == 0 || pressForDuration[0] < 0 {
|
|
pressForDuration = []float64{1.0}
|
|
}
|
|
data["duration"] = pressForDuration[0]
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/dragfromtoforduration")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Swipe(fromX, fromY, toX, toY int) error {
|
|
return we.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY))
|
|
}
|
|
|
|
func (we wdaElement) SwipeFloat(fromX, fromY, toX, toY float64) error {
|
|
return we.DragFloat(fromX, fromY, toX, toY, 0)
|
|
}
|
|
|
|
func (we wdaElement) SwipeDirection(direction Direction, velocity ...float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/swipe"] respondWithTarget:self action:@selector(handleSwipe:)]
|
|
data := map[string]interface{}{"direction": direction}
|
|
if len(velocity) != 0 && velocity[0] > 0 {
|
|
data["velocity"] = velocity[0]
|
|
}
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/swipe")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Pinch(scale, velocity float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/pinch"] respondWithTarget:self action:@selector(handlePinch:)]
|
|
if scale <= 0 {
|
|
return errors.New("'scale' must be greater than zero")
|
|
}
|
|
if scale == 1 {
|
|
return errors.New("'scale' must be greater or less than 1")
|
|
}
|
|
if scale < 1 && velocity > 0 {
|
|
return errors.New("'velocity' must be less than zero when 'scale' is less than 1")
|
|
}
|
|
if scale > 1 && velocity <= 0 {
|
|
return errors.New("'velocity' must be greater than zero when 'scale' is greater than 1")
|
|
}
|
|
data := map[string]interface{}{
|
|
"scale": scale,
|
|
"velocity": velocity,
|
|
}
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/pinch")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) PinchToZoomOutByW3CAction(scale ...float64) (err error) {
|
|
if len(scale) == 0 {
|
|
scale = []float64{1.0}
|
|
} else if scale[0] > 23 {
|
|
scale[0] = 23
|
|
}
|
|
var size Size
|
|
if size, err = we.Size(); err != nil {
|
|
return err
|
|
}
|
|
r := scale[0] * 2 / 100.0
|
|
offsetX, offsetY := float64(size.Width)*r, float64(size.Height)*r
|
|
|
|
actions := NewW3CActions().SwipeFloat(0-offsetX, 0-offsetY, 0, 0, we).SwipeFloat(offsetX, offsetY, 0, 0, we)
|
|
return we.parent.PerformW3CActions(actions)
|
|
}
|
|
|
|
func (we wdaElement) Rotate(rotation float64, velocity ...float64) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/rotate"] respondWithTarget:self action:@selector(handleRotate:)]
|
|
if rotation > math.Pi*2 || rotation < math.Pi*-2 {
|
|
return errors.New("'rotation' must not be more than 2π or less than -2π")
|
|
}
|
|
if len(velocity) == 0 || velocity[0] == 0 {
|
|
velocity = []float64{rotation}
|
|
}
|
|
if rotation > 0 && velocity[0] < 0 || rotation < 0 && velocity[0] > 0 {
|
|
return errors.New("'rotation' and 'velocity' must have the same sign")
|
|
}
|
|
data := map[string]interface{}{
|
|
"rotation": rotation,
|
|
"velocity": velocity[0],
|
|
}
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/rotate")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) PickerWheelSelect(order PickerWheelOrder, offset ...int) (err error) {
|
|
// [[FBRoute POST:@"/wda/pickerwheel/:uuid/select"] respondWithTarget:self action:@selector(handleWheelSelect:)]
|
|
if len(offset) == 0 {
|
|
offset = []int{2}
|
|
} else if offset[0] <= 0 || offset[0] > 5 {
|
|
return fmt.Errorf("'offset' value is expected to be in range (0, 5]. '%d' was given instead", offset[0])
|
|
}
|
|
data := map[string]interface{}{
|
|
"order": order,
|
|
"offset": float64(offset[0]) * 0.1,
|
|
}
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/pickerwheel", we.id, "/select")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) scroll(data interface{}) (err error) {
|
|
// [[FBRoute POST:@"/wda/element/:uuid/scroll"] respondWithTarget:self action:@selector(handleScroll:)]
|
|
_, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/scroll")
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) ScrollElementByName(name string) error {
|
|
data := map[string]interface{}{"name": name}
|
|
return we.scroll(data)
|
|
}
|
|
|
|
func (we wdaElement) ScrollElementByPredicate(predicate string) error {
|
|
data := map[string]interface{}{"predicateString": predicate}
|
|
return we.scroll(data)
|
|
}
|
|
|
|
func (we wdaElement) ScrollToVisible() error {
|
|
data := map[string]interface{}{"toVisible": true}
|
|
return we.scroll(data)
|
|
}
|
|
|
|
func (we wdaElement) ScrollDirection(direction Direction, distance ...float64) error {
|
|
if len(distance) == 0 || distance[0] <= 0 {
|
|
distance = []float64{0.5}
|
|
}
|
|
data := map[string]interface{}{
|
|
"direction": direction,
|
|
"distance": distance[0],
|
|
}
|
|
return we.scroll(data)
|
|
}
|
|
|
|
func (we wdaElement) FindElement(by BySelector) (element WebElement, err error) {
|
|
// [[FBRoute POST:@"/element/:uuid/element"] respondWithTarget:self action:@selector(handleFindSubElement:)]
|
|
using, value := by.getUsingAndValue()
|
|
data := map[string]interface{}{
|
|
"using": using,
|
|
"value": value,
|
|
}
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/element", we.id, "/element"); err != nil {
|
|
return nil, err
|
|
}
|
|
var elementID string
|
|
if elementID, err = rawResp.valueConvertToElementID(); err != nil {
|
|
if errors.Is(err, errNoSuchElement) {
|
|
return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
|
|
}
|
|
return nil, err
|
|
}
|
|
element = &wdaElement{parent: we.parent, id: elementID}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) FindElements(by BySelector) (elements []WebElement, err error) {
|
|
// [[FBRoute POST:@"/element/:uuid/elements"] respondWithTarget:self action:@selector(handleFindSubElements:)]
|
|
using, value := by.getUsingAndValue()
|
|
data := map[string]interface{}{
|
|
"using": using,
|
|
"value": value,
|
|
}
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/element", we.id, "/elements"); err != nil {
|
|
return nil, err
|
|
}
|
|
var elementIDs []string
|
|
if elementIDs, err = rawResp.valueConvertToElementIDs(); err != nil {
|
|
if errors.Is(err, errNoSuchElement) {
|
|
return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
|
|
}
|
|
return nil, err
|
|
}
|
|
elements = make([]WebElement, len(elementIDs))
|
|
for i := range elementIDs {
|
|
elements[i] = &wdaElement{parent: we.parent, id: elementIDs[i]}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) FindVisibleCells() (elements []WebElement, err error) {
|
|
// [[FBRoute GET:@"/wda/element/:uuid/getVisibleCells"] respondWithTarget:self action:@selector(handleFindVisibleCells:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/wda/element", we.id, "/getVisibleCells"); err != nil {
|
|
return nil, err
|
|
}
|
|
var elementIDs []string
|
|
if elementIDs, err = rawResp.valueConvertToElementIDs(); err != nil {
|
|
if errors.Is(err, errNoSuchElement) {
|
|
return nil, fmt.Errorf("%w: unable to find a cell element in this element", err)
|
|
}
|
|
return nil, err
|
|
}
|
|
elements = make([]WebElement, len(elementIDs))
|
|
for i := range elementIDs {
|
|
elements[i] = &wdaElement{parent: we.parent, id: elementIDs[i]}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Rect() (rect Rect, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/rect"] respondWithTarget:self action:@selector(handleGetRect:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/rect"); err != nil {
|
|
return Rect{}, err
|
|
}
|
|
reply := new(struct{ Value struct{ Rect } })
|
|
if err = json.Unmarshal(rawResp, reply); err != nil {
|
|
return Rect{}, err
|
|
}
|
|
rect = reply.Value.Rect
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Location() (Point, error) {
|
|
rect, err := we.Rect()
|
|
if err != nil {
|
|
return Point{}, err
|
|
}
|
|
return rect.Point, nil
|
|
}
|
|
|
|
func (we wdaElement) Size() (Size, error) {
|
|
rect, err := we.Rect()
|
|
if err != nil {
|
|
return Size{}, err
|
|
}
|
|
return rect.Size, nil
|
|
}
|
|
|
|
func (we wdaElement) Text() (text string, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/text"] respondWithTarget:self action:@selector(handleGetText:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/text"); err != nil {
|
|
return "", err
|
|
}
|
|
if text, err = rawResp.valueConvertToString(); err != nil {
|
|
return "", err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) Type() (elemType string, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/name"] respondWithTarget:self action:@selector(handleGetName:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/name"); err != nil {
|
|
return "", err
|
|
}
|
|
if elemType, err = rawResp.valueConvertToString(); err != nil {
|
|
return "", err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) IsEnabled() (enabled bool, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/enabled"] respondWithTarget:self action:@selector(handleGetEnabled:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/enabled"); err != nil {
|
|
return false, err
|
|
}
|
|
if enabled, err = rawResp.valueConvertToBool(); err != nil {
|
|
return false, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) IsDisplayed() (displayed bool, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/displayed"] respondWithTarget:self action:@selector(handleGetDisplayed:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/displayed"); err != nil {
|
|
return false, err
|
|
}
|
|
if displayed, err = rawResp.valueConvertToBool(); err != nil {
|
|
return false, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) IsSelected() (selected bool, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/selected"] respondWithTarget:self action:@selector(handleGetSelected:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/selected"); err != nil {
|
|
return false, err
|
|
}
|
|
if selected, err = rawResp.valueConvertToBool(); err != nil {
|
|
return false, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) IsAccessible() (accessible bool, err error) {
|
|
// [[FBRoute GET:@"/wda/element/:uuid/accessible"] respondWithTarget:self action:@selector(handleGetAccessible:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/wda/element", we.id, "/accessible"); err != nil {
|
|
return false, err
|
|
}
|
|
if accessible, err = rawResp.valueConvertToBool(); err != nil {
|
|
return false, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) IsAccessibilityContainer() (isAccessibilityContainer bool, err error) {
|
|
// [[FBRoute GET:@"/wda/element/:uuid/accessibilityContainer"] respondWithTarget:self action:@selector(handleGetIsAccessibilityContainer:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/wda/element", we.id, "/accessibilityContainer"); err != nil {
|
|
return false, err
|
|
}
|
|
if isAccessibilityContainer, err = rawResp.valueConvertToBool(); err != nil {
|
|
return false, err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) GetAttribute(attr ElementAttribute) (value string, err error) {
|
|
// [[FBRoute GET:@"/element/:uuid/attribute/:name"] respondWithTarget:self action:@selector(handleGetAttribute:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/attribute", attr.getAttributeName()); err != nil {
|
|
return "", err
|
|
}
|
|
if value, err = rawResp.valueConvertToString(); err != nil {
|
|
return "", err
|
|
}
|
|
return
|
|
}
|
|
|
|
func (we wdaElement) UID() (uid string) {
|
|
return we.id
|
|
}
|
|
|
|
func (we wdaElement) Screenshot() (raw *bytes.Buffer, err error) {
|
|
// W3C element screenshot
|
|
// [[FBRoute GET:@"/element/:uuid/screenshot"] respondWithTarget:self action:@selector(handleElementScreenshot:)]
|
|
// JSONWP element screenshot
|
|
// [[FBRoute GET:@"/screenshot/:uuid"] respondWithTarget:self action:@selector(handleElementScreenshot:)]
|
|
var rawResp rawResponse
|
|
if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/screenshot"); err != nil {
|
|
return nil, err
|
|
}
|
|
if raw, err = rawResp.valueDecodeAsBase64(); err != nil {
|
|
return nil, err
|
|
}
|
|
return
|
|
}
|