mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-10 17:43:00 +08:00
change: remove unused UI APIs
This commit is contained in:
@@ -12,7 +12,7 @@ import (
|
||||
func TestAndroidDouYinLive(t *testing.T) {
|
||||
testCase := &hrp.TestCase{
|
||||
Config: hrp.NewConfig("通过 feed 头像进入抖音直播间").
|
||||
SetAndroid(uixt.WithAdbLogOn(true), uixt.WithSerialNumber("2d06bf70")),
|
||||
SetAndroid(uixt.WithAdbLogOn(true)),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("启动抖音").
|
||||
Android().
|
||||
@@ -36,13 +36,6 @@ func TestAndroidDouYinLive(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
if err := testCase.Dump2JSON("demo_android_douyin_live.json"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := testCase.Dump2YAML("demo_android_douyin_live.yaml"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
runner := hrp.NewRunner(t).SetSaveTests(true)
|
||||
err := runner.Run(testCase)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,9 +6,16 @@
|
||||
},
|
||||
"ios": [
|
||||
{
|
||||
"perf_options": {
|
||||
"sys_cpu": true,
|
||||
"sys_mem": true,
|
||||
"fps": true,
|
||||
"network": true
|
||||
},
|
||||
"port": 8700,
|
||||
"mjpeg_port": 8800,
|
||||
"log_on": true
|
||||
"log_on": true,
|
||||
"xctest_bundle_id": "com.gtf.wda.runner.xctrunner"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -63,8 +70,8 @@
|
||||
"actions": [
|
||||
{
|
||||
"method": "tap_ocr",
|
||||
"params": "购物",
|
||||
"identifier": "点击购物"
|
||||
"params": "商城",
|
||||
"identifier": "点击商城"
|
||||
},
|
||||
{
|
||||
"method": "sleep",
|
||||
|
||||
@@ -24,6 +24,7 @@ func TestWDALog(t *testing.T) {
|
||||
uixt.WithIOSPerfNetwork(true),
|
||||
uixt.WithIOSPerfFPS(true),
|
||||
),
|
||||
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
|
||||
),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("启动抖音").
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
package uixt
|
||||
|
||||
import "strings"
|
||||
|
||||
type touchGesture struct {
|
||||
Touch PointF `json:"touch"`
|
||||
Time float64 `json:"time"`
|
||||
}
|
||||
|
||||
type TouchAction []touchGesture
|
||||
|
||||
func NewTouchAction(cap ...int) *TouchAction {
|
||||
if len(cap) == 0 || cap[0] <= 0 {
|
||||
cap = []int{8}
|
||||
}
|
||||
tmp := make(TouchAction, 0, cap[0])
|
||||
return &tmp
|
||||
}
|
||||
|
||||
func (ta *TouchAction) Add(x, y int, startTime ...float64) *TouchAction {
|
||||
return ta.AddFloat(float64(x), float64(y), startTime...)
|
||||
}
|
||||
|
||||
func (ta *TouchAction) AddFloat(x, y float64, startTime ...float64) *TouchAction {
|
||||
if len(startTime) == 0 {
|
||||
var tmp float64 = 0
|
||||
if len(*ta) != 0 {
|
||||
g := (*ta)[len(*ta)-1]
|
||||
tmp = g.Time + 0.05
|
||||
}
|
||||
startTime = []float64{tmp}
|
||||
}
|
||||
*ta = append(*ta, touchGesture{Touch: PointF{x, y}, Time: startTime[0]})
|
||||
return ta
|
||||
}
|
||||
|
||||
func (ta *TouchAction) AddPoint(point Point, startTime ...float64) *TouchAction {
|
||||
return ta.AddFloat(float64(point.X), float64(point.Y), startTime...)
|
||||
}
|
||||
|
||||
func (ta *TouchAction) AddPointF(point PointF, startTime ...float64) *TouchAction {
|
||||
return ta.AddFloat(point.X, point.Y, startTime...)
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) MultiPointerGesture(gesture1 *TouchAction, gesture2 *TouchAction, tas ...*TouchAction) (err error) {
|
||||
// Must provide coordinates for at least 2 pointers
|
||||
actions := make([]*TouchAction, 0)
|
||||
actions = append(actions, gesture1, gesture2)
|
||||
if len(tas) != 0 {
|
||||
actions = append(actions, tas...)
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"actions": actions,
|
||||
}
|
||||
// register(postHandler, new MultiPointerGesture("/wd/hub/session/:sessionId/touch/multi/perform"))
|
||||
_, err = ud.httpPOST(data, "/session", ud.sessionId, "/touch/multi/perform")
|
||||
return
|
||||
}
|
||||
|
||||
type w3cGesture map[string]interface{}
|
||||
|
||||
func _newW3CGesture() w3cGesture {
|
||||
return make(w3cGesture)
|
||||
}
|
||||
|
||||
func (g w3cGesture) _set(key string, value interface{}) w3cGesture {
|
||||
g[key] = value
|
||||
return g
|
||||
}
|
||||
|
||||
func (g w3cGesture) pause(duration float64) w3cGesture {
|
||||
return g._set("type", "pause").
|
||||
_set("duration", duration)
|
||||
}
|
||||
|
||||
func (g w3cGesture) keyDown(value string) w3cGesture {
|
||||
return g._set("type", "keyDown").
|
||||
_set("value", value)
|
||||
}
|
||||
|
||||
func (g w3cGesture) keyUp(value string) w3cGesture {
|
||||
return g._set("type", "keyUp").
|
||||
_set("value", value)
|
||||
}
|
||||
|
||||
func (g w3cGesture) pointerDown(button int) w3cGesture {
|
||||
return g._set("type", "pointerDown")._set("button", button)
|
||||
}
|
||||
|
||||
func (g w3cGesture) pointerUp(button int) w3cGesture {
|
||||
return g._set("type", "pointerUp")._set("button", button)
|
||||
}
|
||||
|
||||
func (g w3cGesture) pointerMove(x, y float64, origin string, duration float64, pressureAndSize ...float64) w3cGesture {
|
||||
switch len(pressureAndSize) {
|
||||
case 1:
|
||||
g._set("pressure", pressureAndSize[0])
|
||||
case 2:
|
||||
g._set("pressure", pressureAndSize[0])
|
||||
g._set("size", pressureAndSize[1])
|
||||
}
|
||||
return g._set("type", "pointerMove").
|
||||
_set("duration", duration).
|
||||
_set("origin", origin).
|
||||
_set("x", x).
|
||||
_set("y", y)
|
||||
}
|
||||
|
||||
func (g w3cGesture) size(size ...float64) w3cGesture {
|
||||
if len(size) == 0 {
|
||||
size = []float64{1.0}
|
||||
}
|
||||
return g._set("size", size[0])
|
||||
}
|
||||
|
||||
func (g w3cGesture) pressure(pressure ...float64) w3cGesture {
|
||||
if len(pressure) == 0 {
|
||||
pressure = []float64{1.0}
|
||||
}
|
||||
return g._set("pressure", pressure[0])
|
||||
}
|
||||
|
||||
type W3CGestures []w3cGesture
|
||||
|
||||
func NewW3CGestures(cap ...int) *W3CGestures {
|
||||
if len(cap) == 0 || cap[0] <= 0 {
|
||||
cap = []int{8}
|
||||
}
|
||||
tmp := make(W3CGestures, 0, cap[0])
|
||||
return &tmp
|
||||
}
|
||||
|
||||
func (g *W3CGestures) Pause(duration ...float64) *W3CGestures {
|
||||
if len(duration) == 0 || duration[0] < 0 {
|
||||
duration = []float64{0.5}
|
||||
}
|
||||
*g = append(*g, _newW3CGesture().pause(duration[0]*1000))
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *W3CGestures) KeyDown(value string) *W3CGestures {
|
||||
*g = append(*g, _newW3CGesture().keyDown(value))
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *W3CGestures) KeyUp(value string) *W3CGestures {
|
||||
*g = append(*g, _newW3CGesture().keyUp(value))
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *W3CGestures) SendKeys(text string) *W3CGestures {
|
||||
ss := strings.Split(text, "")
|
||||
for i := range ss {
|
||||
g.KeyDown(ss[i])
|
||||
g.KeyUp(ss[i])
|
||||
}
|
||||
return g
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
)
|
||||
|
||||
func TestConvertPoints(t *testing.T) {
|
||||
data := "10-09 20:16:48.216 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317808206,\"ext\":\"输入\",\"from\":{\"x\":0.0,\"y\":0.0},\"operation\":\"Gtf-SendKeys\",\"run_time\":627,\"start\":1665317807579,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":0.0,\"y\":0.0}}\n10-09 20:18:22.899 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317902898,\"ext\":\"进入直播间\",\"from\":{\"x\":717.0,\"y\":2117.5},\"operation\":\"Gtf-Tap\",\"run_time\":121,\"start\":1665317902777,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":717.0,\"y\":2117.5}}\n10-09 20:18:32.063 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317912062,\"ext\":\"第一次上划\",\"from\":{\"x\":1437.0,\"y\":2409.9},\"operation\":\"Gtf-Swipe\",\"run_time\":32,\"start\":1665317912030,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":1437.0,\"y\":2409.9}}"
|
||||
eps := ConvertPoints(data)
|
||||
if len(eps) != 3 {
|
||||
t.Fatal()
|
||||
}
|
||||
jsons, _ := json.Marshal(eps)
|
||||
println(fmt.Sprintf("%v", string(jsons)))
|
||||
}
|
||||
@@ -309,45 +309,12 @@ func (ud *uiaDriver) StopCamera() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) ActiveAppInfo() (info AppInfo, err error) {
|
||||
// TODO
|
||||
return info, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
|
||||
// TODO
|
||||
return appsList, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppState(bundleId string) (runState AppState, err error) {
|
||||
// TODO
|
||||
return runState, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) IsLocked() (locked bool, err error) {
|
||||
// TODO
|
||||
return locked, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Unlock() (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Lock() (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Homescreen() (err error) {
|
||||
return ud.PressKeyCode(KCHome, KMEmpty)
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) {
|
||||
if len(flags) == 0 {
|
||||
flags = []KeyFlag{KFFromSystem}
|
||||
}
|
||||
return ud._pressKeyCode(keyCode, metaState, KFFromSystem)
|
||||
return ud._pressKeyCode(keyCode, metaState, flags...)
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) _pressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) {
|
||||
@@ -365,138 +332,28 @@ func (ud *uiaDriver) _pressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AlertText() (text string, err error) {
|
||||
// register(getHandler, new GetAlertText("/wd/hub/session/:sessionId/alert/text"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ud.httpGET("/session", ud.sessionId, "alert/text"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
reply := new(struct{ Value string })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
text = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AlertButtons() (btnLabels []string, err error) {
|
||||
// TODO
|
||||
return btnLabels, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AlertAccept(label ...string) (err error) {
|
||||
data := map[string]interface{}{
|
||||
"buttonLabel": nil,
|
||||
}
|
||||
if len(label) != 0 {
|
||||
data["buttonLabel"] = label[0]
|
||||
}
|
||||
// register(postHandler, new AcceptAlert("/wd/hub/session/:sessionId/alert/accept"))
|
||||
_, err = ud.httpPOST(data, "/session", ud.sessionId, "alert/accept")
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AlertDismiss(label ...string) (err error) {
|
||||
data := map[string]interface{}{
|
||||
"buttonLabel": nil,
|
||||
}
|
||||
if len(label) != 0 {
|
||||
data["buttonLabel"] = label[0]
|
||||
}
|
||||
// register(postHandler, new DismissAlert("/wd/hub/session/:sessionId/alert/dismiss"))
|
||||
_, err = ud.httpPOST(data, "/session", ud.sessionId, "alert/dismiss")
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AlertSendKeys(text string) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) check() error {
|
||||
if ud.adbDevice.Serial() == "" {
|
||||
return errors.New("adb daemon: the device is not ready")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppLaunch(bundleId string, launchOpt ...AppLaunchOption) (err error) {
|
||||
if err = ud.check(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var sOutput string
|
||||
if sOutput, err = ud.adbDevice.RunShellCommand("monkey -p", bundleId, "-c android.intent.category.LAUNCHER 1"); err != nil {
|
||||
func (ud *uiaDriver) AppLaunch(bundleId string) (err error) {
|
||||
// 不指定 Activity 名称启动(启动主 Activity)
|
||||
// adb shell monkey -p <packagename> -c android.intent.category.LAUNCHER 1
|
||||
sOutput, err := ud.adbDevice.RunShellCommand(
|
||||
"monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1",
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Contains(sOutput, "monkey aborted") {
|
||||
return fmt.Errorf("app launch: %s", strings.TrimSpace(sOutput))
|
||||
}
|
||||
|
||||
if len(launchOpt) != 0 {
|
||||
var ce error
|
||||
exists := func(ud WebDriver) (bool, error) {
|
||||
for _, opt := range launchOpt {
|
||||
if bySelector, ok := opt["bySelector"]; ok {
|
||||
for _, e := range bySelector.([]BySelector) {
|
||||
_, ce = ud.FindElement(e)
|
||||
if ce == nil {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
if err = ud.WaitWithTimeoutAndInterval(exists, 45, 1); err != nil {
|
||||
return fmt.Errorf("app launch: %s: %w", err.Error(), ce)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppLaunchUnattached(bundleId string) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
// Dispose corresponds to the command:
|
||||
// adb -s $serial forward --remove $localPort
|
||||
func (ud *uiaDriver) Dispose() (err error) {
|
||||
if err = ud.check(); err != nil {
|
||||
return err
|
||||
}
|
||||
if ud.localPort == 0 {
|
||||
return nil
|
||||
}
|
||||
return ud.adbDevice.ForwardKill(ud.localPort)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppTerminate(bundleId string) (successful bool, err error) {
|
||||
if err = ud.check(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// 强制停止应用,停止 <packagename> 相关的进程
|
||||
// adb shell am force-stop <packagename>
|
||||
_, err = ud.adbDevice.RunShellCommand("am", "force-stop", bundleId)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppActivate(bundleId string) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppDeactivate(second float64) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) AppAuthReset(resource ProtectedResource) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Tap(x, y int, options ...DataOption) error {
|
||||
return ud.TapFloat(float64(x), float64(y), options...)
|
||||
}
|
||||
@@ -608,20 +465,6 @@ func (ud *uiaDriver) ForceTouchFloat(x, y, pressure float64, second ...float64)
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) PerformW3CActions(actions *W3CActions) (err error) {
|
||||
data := map[string]interface{}{
|
||||
"actions": actions,
|
||||
}
|
||||
// register(postHandler, new W3CActions("/wd/hub/session/:sessionId/actions"))
|
||||
_, err = ud.httpPOST(data, "/session", ud.sessionId, "/actions")
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) PerformAppiumTouchActions(touchActs *TouchActions) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
|
||||
lbl := content
|
||||
|
||||
@@ -679,31 +522,7 @@ func (ud *uiaDriver) SendKeys(text string, options ...DataOption) (err error) {
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Input(text string, options ...DataOption) (err error) {
|
||||
data := map[string]interface{}{
|
||||
"view": text,
|
||||
}
|
||||
// new data options in post data for extra uiautomator configurations
|
||||
newData := NewData(data, options...)
|
||||
|
||||
var element WebElement
|
||||
if valuetext, ok := newData["textview"]; ok {
|
||||
element, err = ud.FindElement(BySelector{UiAutomator: NewUiSelectorHelper().TextContains(fmt.Sprintf("%v", valuetext)).String()})
|
||||
} else if valueid, ok := newData["id"]; ok {
|
||||
element, err = ud.FindElement(BySelector{ResourceIdID: fmt.Sprintf("%v", valueid)})
|
||||
} else if valuedesc, ok := newData["description"]; ok {
|
||||
element, err = ud.FindElement(BySelector{UiAutomator: NewUiSelectorHelper().Description(fmt.Sprintf("%v", valuedesc)).String()})
|
||||
} else {
|
||||
element, err = ud.FindElement(BySelector{ClassName: ElementType{EditText: true}})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return element.SendKeys(text, options...)
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) KeyboardDismiss(keyNames ...string) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
return ud.SendKeys(text, options...)
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) PressButton(devBtn DeviceButton) (err error) {
|
||||
@@ -711,47 +530,6 @@ func (ud *uiaDriver) PressButton(devBtn DeviceButton) (err error) {
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) ExpectNotification(notifyName string, notifyType NotificationType, second ...int) (err error) {
|
||||
// register(postHandler, new OpenNotification("/wd/hub/session/:sessionId/appium/device/open_notifications"))
|
||||
_, err = ud.httpPOST(nil, "/session", ud.sessionId, "appium/device/open_notifications")
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) SiriActivate(text string) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) SiriOpenUrl(url string) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Orientation() (orientation Orientation, err error) {
|
||||
// register(getHandler, new GetOrientation("/wd/hub/session/:sessionId/orientation"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ud.httpGET("/session", ud.sessionId, "orientation"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
reply := new(struct{ Value Orientation })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
orientation = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) SetOrientation(orientation Orientation) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Rotation() (rotation Rotation, err error) {
|
||||
// register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation"))
|
||||
var rawResp rawResponse
|
||||
@@ -772,105 +550,6 @@ func (ud *uiaDriver) SetRotation(rotation Rotation) (err error) {
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) MatchTouchID(isMatch bool) (err error) {
|
||||
// TODO
|
||||
return errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) _findElements(method, selector string, elementID ...string) (elements []WebElement, err error) {
|
||||
// register(postHandler, new FindElements("/wd/hub/session/:sessionId/elements"))
|
||||
data := map[string]interface{}{
|
||||
"strategy": method,
|
||||
"selector": selector,
|
||||
}
|
||||
if len(elementID) != 0 {
|
||||
data["context"] = elementID[0]
|
||||
}
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ud.httpPOST(data, "/session", ud.sessionId, "/elements"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reply := new(struct{ Value []map[string]string })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(reply.Value) == 0 {
|
||||
return nil, fmt.Errorf("no such element: unable to find an element using '%s', value '%s'", method, selector)
|
||||
}
|
||||
elements = make([]WebElement, len(reply.Value))
|
||||
for i, elem := range reply.Value {
|
||||
var id string
|
||||
if id = elementIDFromValue(elem); id == "" {
|
||||
return nil, fmt.Errorf("invalid element returned: %+v", reply)
|
||||
}
|
||||
uie := WebElement(uiaElement{parent: ud, id: id})
|
||||
elements[i] = uie
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) _findElement(method, selector string, elementID ...string) (elem *uiaElement, err error) {
|
||||
// register(postHandler, new FindElement("/wd/hub/session/:sessionId/element"))
|
||||
data := map[string]interface{}{
|
||||
"strategy": method,
|
||||
"selector": selector,
|
||||
}
|
||||
if len(elementID) != 0 {
|
||||
data["context"] = elementID[0]
|
||||
}
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ud.httpPOST(data, "/session", ud.sessionId, "/element"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reply := new(struct{ Value map[string]string })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(reply.Value) == 0 {
|
||||
return nil, fmt.Errorf("no such element: unable to find an element using '%s', value '%s'", method, selector)
|
||||
}
|
||||
var id string
|
||||
if id = elementIDFromValue(reply.Value); id == "" {
|
||||
return nil, fmt.Errorf("invalid element returned: %+v", reply)
|
||||
}
|
||||
elem = &uiaElement{parent: ud, id: id}
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) ActiveElement() (element WebElement, err error) {
|
||||
// TODO
|
||||
return element, errDriverNotImplemented
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) FindElement(by BySelector) (element WebElement, err error) {
|
||||
return ud._findElement(by.getUsingAndValue())
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) FindElements(by BySelector) (elements []WebElement, err error) {
|
||||
// [[FBRoute POST:@"/elements"] respondWithTarget:self action:@selector(handleFindElements:)]
|
||||
using, value := by.getUsingAndValue()
|
||||
data := map[string]interface{}{
|
||||
"using": using,
|
||||
"value": value,
|
||||
}
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ud.httpPOST(data, "/session", ud.sessionId, "/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] = WebElement(uiaElement{parent: ud, id: elementIDs[i]})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (ud *uiaDriver) Screenshot() (raw *bytes.Buffer, err error) {
|
||||
// register(getHandler, new CaptureScreenshot("/wd/hub/session/:sessionId/screenshot"))
|
||||
var rawResp rawResponse
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var errElementNotImplemented = errors.New("element method not implemented")
|
||||
|
||||
type uiaElement struct {
|
||||
parent *uiaDriver
|
||||
id string
|
||||
}
|
||||
|
||||
func (ue uiaElement) Click() (err error) {
|
||||
// register(postHandler, new Click("/wd/hub/session/:sessionId/element/:id/click"))
|
||||
_, err = ue.parent.httpPOST(nil, "/session", ue.parent.sessionId, "/element", ue.id, "/click")
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) SendKeys(text string, options ...DataOption) (err error) {
|
||||
// register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/element/:id/value"))
|
||||
// https://github.com/appium/appium-uiutomator2-server/blob/master/app/src/main/java/io/appium/uiutomator2/handler/SendKeysToElement.java#L76-L85
|
||||
data := map[string]interface{}{
|
||||
"text": text,
|
||||
}
|
||||
|
||||
// new data options in post data for extra uiautomator configurations
|
||||
newData := NewData(data, options...)
|
||||
|
||||
_, err = ue.parent.httpPOST(newData, "/session", ue.parent.sessionId, "/element", ue.id, "/value")
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) Clear() (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) Tap(x, y int) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) TapFloat(x, y float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) DoubleTap() (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) TouchAndHold(second ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) TwoFingerTap() (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) TapWithNumberOfTaps(numberOfTaps, numberOfTouches int) (err error) {
|
||||
// Todo: implement
|
||||
log.Fatal().Msg("not support")
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) ForceTouch(pressure float64, second ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) Drag(fromX, fromY, toX, toY int, steps ...float64) (err error) {
|
||||
return ue.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), steps...)
|
||||
}
|
||||
|
||||
func (ue uiaElement) DragFloat(fromX, fromY, toX, toY float64, steps ...float64) (err error) {
|
||||
if len(steps) == 0 {
|
||||
steps = []float64{12 * 10}
|
||||
} else {
|
||||
steps[0] = 12 * 10
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"elementId": ue.id,
|
||||
"endX": toX,
|
||||
"endY": toY,
|
||||
"steps": steps[0],
|
||||
}
|
||||
return ue.parent._drag(data)
|
||||
}
|
||||
|
||||
func (ue uiaElement) Swipe(fromX, fromY, toX, toY int) error {
|
||||
return ue.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY))
|
||||
}
|
||||
|
||||
func (ue uiaElement) SwipeFloat(fromX, fromY, toX, toY float64) error {
|
||||
options := []DataOption{
|
||||
WithDataSteps(12),
|
||||
WithCustomOption("elementId", ue.id),
|
||||
}
|
||||
return ue.parent._swipe(fromX, fromY, toX, toY, options...)
|
||||
}
|
||||
|
||||
func (ue uiaElement) SwipeDirection(direction Direction, velocity ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) Pinch(scale, velocity float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) PinchToZoomOutByW3CAction(scale ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) Rotate(rotation float64, velocity ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) PickerWheelSelect(order PickerWheelOrder, offset ...int) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) scroll(data interface{}) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) ScrollElementByName(name string) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) ScrollElementByPredicate(predicate string) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) ScrollToVisible() (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) ScrollDirection(direction Direction, distance ...float64) (err error) {
|
||||
// TODO
|
||||
return errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) FindElement(by BySelector) (element WebElement, err error) {
|
||||
method, selector := by.getMethodAndSelector()
|
||||
return ue.parent._findElement(method, selector, ue.id)
|
||||
}
|
||||
|
||||
func (ue uiaElement) FindElements(by BySelector) (elements []WebElement, err error) {
|
||||
method, selector := by.getMethodAndSelector()
|
||||
return ue.parent._findElements(method, selector, ue.id)
|
||||
}
|
||||
|
||||
func (ue uiaElement) FindVisibleCells() (elements []WebElement, err error) {
|
||||
// TODO
|
||||
return elements, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) Rect() (rect Rect, err error) {
|
||||
// register(getHandler, new GetRect("/wd/hub/session/:sessionId/element/:id/rect"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/rect"); err != nil {
|
||||
return Rect{}, err
|
||||
}
|
||||
reply := new(struct{ Value Rect })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return Rect{}, err
|
||||
}
|
||||
rect = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) Location() (point Point, err error) {
|
||||
// register(getHandler, new Location("/wd/hub/session/:sessionId/element/:id/location"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/location"); err != nil {
|
||||
return Point{-1, -1}, err
|
||||
}
|
||||
reply := new(struct{ Value Point })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return Point{-1, -1}, err
|
||||
}
|
||||
point = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) Size() (size Size, err error) {
|
||||
// register(getHandler, new GetSize("/wd/hub/session/:sessionId/element/:id/size"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/size"); err != nil {
|
||||
return Size{-1, -1}, err
|
||||
}
|
||||
reply := new(struct{ Value Size })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return Size{-1, -1}, err
|
||||
}
|
||||
size = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) Text() (text string, err error) {
|
||||
// register(getHandler, new GetText("/wd/hub/session/:sessionId/element/:id/text"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/text"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
reply := new(struct{ Value string })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return "", err
|
||||
}
|
||||
text = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) Type() (elemType string, err error) {
|
||||
// TODO
|
||||
return elemType, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) IsEnabled() (enabled bool, err error) {
|
||||
// TODO
|
||||
return enabled, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) IsDisplayed() (displayed bool, err error) {
|
||||
// TODO
|
||||
return displayed, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) IsSelected() (selected bool, err error) {
|
||||
// TODO
|
||||
return selected, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) IsAccessible() (accessible bool, err error) {
|
||||
// TODO
|
||||
return accessible, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) IsAccessibilityContainer() (isAccessibilityContainer bool, err error) {
|
||||
// TODO
|
||||
return isAccessibilityContainer, errElementNotImplemented
|
||||
}
|
||||
|
||||
func (ue uiaElement) GetAttribute(attr ElementAttribute) (value string, err error) {
|
||||
// register(getHandler, new GetElementAttribute("/wd/hub/session/:sessionId/element/:id/attribute/:name"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/attribute", attr.getAttributeName()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
reply := new(struct{ Value string })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return "", err
|
||||
}
|
||||
value = reply.Value
|
||||
return
|
||||
}
|
||||
|
||||
func (ue uiaElement) UID() (uid string) {
|
||||
return ue.id
|
||||
}
|
||||
|
||||
func (ue uiaElement) Screenshot() (raw *bytes.Buffer, err error) {
|
||||
// W3C endpoint
|
||||
// register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/element/:id/screenshot"))
|
||||
// JSONWP endpoint
|
||||
// register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/screenshot/:id"))
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/screenshot"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reply := new(struct{ Value string })
|
||||
if err = json.Unmarshal(rawResp, reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var decodeStr []byte
|
||||
if decodeStr, err = base64.StdEncoding.DecodeString(reply.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
raw = bytes.NewBuffer(decodeStr)
|
||||
return
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -115,20 +117,6 @@ func TestDriver_Screenshot(t *testing.T) {
|
||||
t.Log(ioutil.WriteFile("/Users/hero/Desktop/s1.png", screenshot.Bytes(), 0o600))
|
||||
}
|
||||
|
||||
func TestDriver_Orientation(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
orientation, err := driver.Orientation()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(orientation)
|
||||
}
|
||||
|
||||
func TestDriver_Rotation(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
@@ -232,20 +220,6 @@ func TestDriver_DeviceInfo(t *testing.T) {
|
||||
t.Logf("bluetooth state: %s", devInfo.Bluetooth.State)
|
||||
}
|
||||
|
||||
func TestDriver_AlertText(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
alertText, err := driver.AlertText()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(alertText)
|
||||
}
|
||||
|
||||
func TestDriver_Tap(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
@@ -326,333 +300,13 @@ func TestDriver_SendKeys(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//func TestDriver_PressBack(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.PressBack()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestDriver_PressKeyCode(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.PressKeyCodeAsync(KCx)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// err = driver.PressKeyCodeAsync(KCx, KMCapLocked)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// // err = driver.PressKeyCodeAsync(KCExplorer)
|
||||
// // if err != nil {
|
||||
// // t.Fatal(err)
|
||||
// // }
|
||||
//
|
||||
// err = driver.PressKeyCode(KCExplorer, KMEmpty)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestDriver_LongPressKeyCode(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.LongPressKeyCode(KCAt, KMEmpty)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestDriver_TouchDown(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// doTouchUp := func() {
|
||||
// err = driver.TouchUp(400, 260)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// err = driver.TouchDown(400, 260)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // _ = driver.TapPoint(Point{400, 500})
|
||||
// doTouchUp()
|
||||
//
|
||||
// err = driver.TouchDownPoint(Point{400, 260})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// doTouchUp()
|
||||
//}
|
||||
//
|
||||
//func TestDriver_TouchUp(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.TouchDown(400, 260)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // err = driver.TouchUp(400, 260)
|
||||
// err = driver.TouchUpPoint(Point{400, 260})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestDriver_TouchMove(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// doTouchDown := func(x, y int) {
|
||||
// err = driver.TouchDown(x, y)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// doTouchUp := func(x, y int) {
|
||||
// err = driver.TouchUp(x, y)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// doTouchDown(400, 260)
|
||||
//
|
||||
// err = driver.TouchMove(400, 500)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// doTouchUp(400, 500)
|
||||
//
|
||||
// doTouchDown(400, 500)
|
||||
//
|
||||
// err = driver.TouchMove(400, 260)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// doTouchUp(400, 260)
|
||||
//}
|
||||
//
|
||||
//func TestDriver_OpenNotification(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.OpenNotification()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestDriver_Flick(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.Flick(50, -100)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestDriver_ScrollTo(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.ScrollTo(BySelector{ClassName: "android.widget.SeekBar"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestDriver_MultiPointerGesture(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// gesture1 := NewTouchAction().Add(150, 340, 0.35).AddFloat(50, 300)
|
||||
// gesture2 := NewTouchAction().Add(200, 340).AddFloat(300, 300)
|
||||
// gesture3 := NewTouchAction().Add(300, 500).AddFloat(350, 500).AddPoint(Point{300, 550}).AddPointF(PointF{350, 550})
|
||||
// _ = gesture3
|
||||
//
|
||||
// // err = driver.MultiPointerGesture(gesture1, gesture2)
|
||||
// err = driver.MultiPointerGesture(gesture1, gesture2, gesture3)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestDriver_PerformW3CActions(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // actionKey := NewW3CAction(ATKey, NewW3CGestures().KeyDown("g").KeyUp("g").Pause().KeyDown("o").KeyUp("o"))
|
||||
// // actionKey := NewW3CAction(ATKey, NewW3CGestures().SendKeys("golang"))
|
||||
// // err = driver.PerformW3CActions(actionKey)
|
||||
// // if err != nil {
|
||||
// // t.Fatal(err)
|
||||
// // }
|
||||
//
|
||||
// // var queryField map[string]string
|
||||
// // queryField = make(map[string]string)
|
||||
// // {
|
||||
// // queryField = map[string]string{
|
||||
// // "a": "",
|
||||
// // }
|
||||
// // }
|
||||
//
|
||||
// elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/search"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// // actionPointer := NewW3CAction(ATPointer, NewW3CGestures().PointerMove(0, 0, elem.id).PointerDown().Pause(3).PointerUp())
|
||||
// // actionPointer := NewW3CAction(ATPointer,
|
||||
// // NewW3CGestures().PointerMove(400, 500, "viewport").PointerDown().Pause(2).
|
||||
// // PointerMove(0, 0, elem.id).Pause(2).
|
||||
// // PointerMove(20, 0, "pointer").Pause(2).
|
||||
// // PointerUp(),
|
||||
// // )
|
||||
// actionPointer := NewW3CAction(ATPointer,
|
||||
// NewW3CGestures().PointerMoveTo(400, 500).PointerDown().
|
||||
// PointerMouseOver(0, 0, elem).
|
||||
// PointerMoveRelative(20, 0).PointerUp())
|
||||
// err = driver.PerformW3CActions(actionPointer)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestDriver_GetClipboard(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// text, err := driver.GetClipboard()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// t.Log(text)
|
||||
//}
|
||||
//
|
||||
//func TestDriver_SetClipboard(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// content := "test123"
|
||||
// err = driver.SetClipboard(ClipDataTypePlaintext, content)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// text, err := driver.GetClipboard()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if text != content {
|
||||
// t.Fatal("should be the same")
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestDriver_AlertAccept(t *testing.T) {
|
||||
func TestDriver_PressBack(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = driver.AlertAccept()
|
||||
// err = driver.AlertAccept("是")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriver_AlertDismiss(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// err = driver.AlertDismiss()
|
||||
err = driver.AlertDismiss("否")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//func TestDriver_SetAppiumSettings(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// appiumSettings, err := driver.GetAppiumSettings()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// sdopd := appiumSettings["shutdownOnPowerDisconnect"]
|
||||
// t.Log("shutdownOnPowerDisconnect:", sdopd)
|
||||
//
|
||||
// err = driver.SetAppiumSettings(map[string]interface{}{"shutdownOnPowerDisconnect": !sdopd.(bool)})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// appiumSettings, err = driver.GetAppiumSettings()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if appiumSettings["shutdownOnPowerDisconnect"] == sdopd.(bool) {
|
||||
// t.Fatal("should not be equal")
|
||||
// }
|
||||
// t.Log("shutdownOnPowerDisconnect:", appiumSettings["shutdownOnPowerDisconnect"])
|
||||
//}
|
||||
|
||||
func TestDriver_SetOrientation(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = driver.SetOrientation(OrientationLandscapeLeft)
|
||||
// err = driver.SetOrientation(OrientationPortrait)
|
||||
err = driver.PressBack()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -671,118 +325,6 @@ func TestDriver_SetRotation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//func TestDriver_NetworkConnection(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = driver.NetworkConnection(NetworkTypeWifi)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestDriver_FindElement(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "android:id/content"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
e := ElementAttribute{}.WithLabel("class")
|
||||
t.Log(elem.GetAttribute(e))
|
||||
}
|
||||
|
||||
func TestDriver_FindElements(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// elements, err := driver.FindElements(BySelector{ResourceIdID: "com.android.settings:id/title"})
|
||||
elements, err := driver.FindElements(BySelector{UiAutomator: "new UiSelector().textStartsWith(\"应\");"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(len(elements))
|
||||
}
|
||||
|
||||
func TestDriver_WaitWithTimeoutAndInterval(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
element, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().className(\"android.view.ViewGroup\");"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := element.FindElement(BySelector{UiAutomator: "new UiSelector().className(\"android.widget.LinearLayout\").index(6);"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rect, err := elem.Rect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
x := rect.X + int(float64(rect.Width)*2)
|
||||
y := rect.Y + rect.Height/2
|
||||
err = driver.Tap(x, y)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
by := BySelector{UiAutomator: "new UiSelector().text(\"科技\");"}
|
||||
exists := func(d WebDriver) (bool, error) {
|
||||
element, err = d.FindElement(by)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err = driver.WaitWithTimeoutAndInterval(exists, 1, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// element, err = driver.FindElement(by)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
err = element.Click()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//func TestDriver_ActiveElement(t *testing.T) {
|
||||
// device, _ := NewAndroidDevice()
|
||||
// driver, err := device.NewUSBDriver(nil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer func() {
|
||||
// _ = driver.Dispose()
|
||||
// }()
|
||||
//
|
||||
// element, err := driver.ActiveElement()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// if err = element.SendKeys("test"); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestUiSelectorHelper_NewUiSelectorHelper(t *testing.T) {
|
||||
uiSelector := NewUiSelectorHelper().Text("a").String()
|
||||
if uiSelector != `new UiSelector().text("a");` {
|
||||
@@ -828,39 +370,6 @@ func TestDeviceList(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//func TestAndroidNewUSBDriver(t *testing.T) {
|
||||
// device, _ := NewAndroidDevice()
|
||||
// driver, err := device.NewUSBDriver(nil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer driver.Dispose()
|
||||
//
|
||||
// ready, err := driver.Status()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if !ready {
|
||||
// t.Fatal("should be 'true'")
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestDriver_ActiveAppPackageName(t *testing.T) {
|
||||
// device, _ := NewAndroidDevice()
|
||||
// driver, err := device.NewUSBDriver(nil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer driver.Dispose()
|
||||
//
|
||||
// appPackageName, err := driver.ActiveAppPackageName()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// t.Log(appPackageName)
|
||||
//}
|
||||
|
||||
func TestDriver_AppLaunch(t *testing.T) {
|
||||
device, _ := NewAndroidDevice()
|
||||
driver, err := device.NewUSBDriver(nil)
|
||||
@@ -912,7 +421,6 @@ func TestDriver_AppTerminate(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer driver.Dispose()
|
||||
|
||||
_, err = driver.AppTerminate("tv.danmaku.bili")
|
||||
if err != nil {
|
||||
@@ -920,413 +428,12 @@ func TestDriver_AppTerminate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
//func TestNewWiFiDriver(t *testing.T) {
|
||||
// device, _ := NewAndroidDevice(WithAdbIP("192.168.1.28"))
|
||||
// driver, err := device.NewHTTPDriver(nil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // SetDebug(false, true)
|
||||
// _, err = driver.ActiveAppActivity()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestDriver_AppInstall(t *testing.T) {
|
||||
// device, _ := NewAndroidDevice()
|
||||
// driver, err := device.NewUSBDriver(nil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer driver.Dispose()
|
||||
//
|
||||
// err = driver.AppInstall("/Users/hero/Desktop/xuexi_android_10002068.apk")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestDriver_AppUninstall(t *testing.T) {
|
||||
// device, _ := NewAndroidDevice()
|
||||
// driver, err := device.NewUSBDriver(nil)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer driver.Dispose()
|
||||
//
|
||||
// err = driver.AppUninstall("cn.xuexi.android")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
func TestBySelector_getMethodAndSelector(t *testing.T) {
|
||||
testVal := "test id"
|
||||
bySelector := BySelector{ResourceIdID: testVal}
|
||||
method, selector := bySelector.getMethodAndSelector()
|
||||
if method != "id" || selector != testVal {
|
||||
t.Fatal(method, "=", selector)
|
||||
}
|
||||
|
||||
bySelector = BySelector{ContentDescription: testVal}
|
||||
method, selector = bySelector.getMethodAndSelector()
|
||||
if method != "accessibility id" || selector != testVal {
|
||||
t.Fatal(method, "=", selector)
|
||||
func TestConvertPoints(t *testing.T) {
|
||||
data := "10-09 20:16:48.216 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317808206,\"ext\":\"输入\",\"from\":{\"x\":0.0,\"y\":0.0},\"operation\":\"Gtf-SendKeys\",\"run_time\":627,\"start\":1665317807579,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":0.0,\"y\":0.0}}\n10-09 20:18:22.899 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317902898,\"ext\":\"进入直播间\",\"from\":{\"x\":717.0,\"y\":2117.5},\"operation\":\"Gtf-Tap\",\"run_time\":121,\"start\":1665317902777,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":717.0,\"y\":2117.5}}\n10-09 20:18:32.063 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317912062,\"ext\":\"第一次上划\",\"from\":{\"x\":1437.0,\"y\":2409.9},\"operation\":\"Gtf-Swipe\",\"run_time\":32,\"start\":1665317912030,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":1437.0,\"y\":2409.9}}"
|
||||
eps := ConvertPoints(data)
|
||||
if len(eps) != 3 {
|
||||
t.Fatal()
|
||||
}
|
||||
jsons, _ := json.Marshal(eps)
|
||||
println(fmt.Sprintf("%v", string(jsons)))
|
||||
}
|
||||
|
||||
func TestElement_Text(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
text, err := elem.Text()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(text)
|
||||
}
|
||||
|
||||
func TestElement_GetAttribute(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
e := ElementAttribute{}.WithName("class")
|
||||
attribute, err := elem.GetAttribute(e)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(attribute)
|
||||
}
|
||||
|
||||
//func TestElement_ContentDescription(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/search"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// name, err := elem.ContentDescription()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// t.Log(name)
|
||||
//}
|
||||
|
||||
func TestElement_Size(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/search"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
size, err := elem.Size()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(size)
|
||||
}
|
||||
|
||||
func TestElement_Rect(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rect, err := elem.Rect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(rect)
|
||||
}
|
||||
|
||||
func TestElement_Screenshot(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
screenshot, err := elem.Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(ioutil.WriteFile("/Users/hero/Desktop/e1.png", screenshot.Bytes(), 0o600))
|
||||
}
|
||||
|
||||
func TestElement_Location(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
location, err := elem.Location()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(location)
|
||||
}
|
||||
|
||||
func TestElement_Click(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = elem.Click()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestElement_Clear(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "android:id/search_src_text"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = elem.Clear()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestElement_SendKeys(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "android:id/search_src_text"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// return
|
||||
|
||||
// err = elem.SendKeys("abc")
|
||||
err = elem.SendKeys("456")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestElement_FindElements(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/main_content"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elements, err := parentElem.FindElements(BySelector{ResourceIdID: "com.android.settings:id/category"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(len(elements))
|
||||
}
|
||||
|
||||
func TestElement_FindElement(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/main_content"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := parentElem.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(elem.Text())
|
||||
}
|
||||
|
||||
func TestElement_Swipe(t *testing.T) {
|
||||
driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
rect, err := elem.Rect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(rect)
|
||||
|
||||
var startX, startY, endX, endY int
|
||||
startX = rect.X + rect.Width/20
|
||||
startY = rect.Y + rect.Height/2
|
||||
endX = startX
|
||||
endY = startY - startY/2
|
||||
err = elem.Swipe(startX, startY, endX, endY)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
//func TestElement_Drag(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// elements, err := driver.FindElements(BySelector{ClassName: "android.widget.TextView"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// for i, elem := range elements {
|
||||
// text, _ := elem.Text()
|
||||
// t.Log(i, text)
|
||||
// }
|
||||
//
|
||||
// rect, err := elements[0].Rect()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // err = elements[0].Drag(300, 450, 256)
|
||||
// err = elements[0].Drag(300, 450, 256)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = elements[0].DragTo(elements[1], 256)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// endPoint := PointF{X: float64(rect.X + rect.Width/3*2), Y: float64(rect.Y + rect.Height/2)}
|
||||
// err = elements[0].DragPointF(endPoint, 256)
|
||||
// if err != nil {
|
||||
// t.Fatal()
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestElement_Flick(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// elem, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().text(\"提示音和通知\");"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = elem.Flick(36, 20, 100)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestElement_ScrollTo(t *testing.T) {
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // how to make it work?
|
||||
// // parentElem, err := driver.FindElement(BySelector{ClassName: "android.widget.ScrollView"})
|
||||
// // parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.cyanogenmod.filemanager:id/navigation_view_layout"})
|
||||
// parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/dashboard"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = parentElem.ScrollTo(BySelector{ContentDescription: "电池"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
//func TestElement_ScrollToElement(t *testing.T) {
|
||||
// // android.widget.HorizontalScrollView
|
||||
// driver, err := NewUIADriver(nil, uiaServerURL)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// // how to make it work?
|
||||
// parentElem, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().resourceId(\"com.android.settings:id/dashboard\");"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// element, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().text(\"电池\");"})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//
|
||||
// err = parentElem.ScrollToElement(element)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
//}
|
||||
|
||||
@@ -7,10 +7,12 @@ import (
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
|
||||
@@ -22,19 +24,18 @@ import (
|
||||
type MobileMethod string
|
||||
|
||||
const (
|
||||
AppInstall MobileMethod = "install"
|
||||
AppUninstall MobileMethod = "uninstall"
|
||||
AppStart MobileMethod = "app_start"
|
||||
AppLaunch MobileMethod = "app_launch" // 等待 app 打开并堵塞到 app 首屏加载完成,可以传入 app 的启动参数、环境变量
|
||||
AppLaunchUnattached MobileMethod = "app_launch_unattached" // 只负责通知打开 app,不堵塞等待,不可传入启动参数
|
||||
AppTerminate MobileMethod = "app_terminate"
|
||||
AppStop MobileMethod = "app_stop"
|
||||
CtlScreenShot MobileMethod = "screenshot"
|
||||
CtlSleep MobileMethod = "sleep"
|
||||
CtlStartCamera MobileMethod = "camera_start" // alias for app_launch camera
|
||||
CtlStopCamera MobileMethod = "camera_stop" // alias for app_terminate camera
|
||||
RecordStart MobileMethod = "record_start"
|
||||
RecordStop MobileMethod = "record_stop"
|
||||
AppInstall MobileMethod = "install"
|
||||
AppUninstall MobileMethod = "uninstall"
|
||||
AppStart MobileMethod = "app_start"
|
||||
AppLaunch MobileMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
|
||||
AppTerminate MobileMethod = "app_terminate"
|
||||
AppStop MobileMethod = "app_stop"
|
||||
CtlScreenShot MobileMethod = "screenshot"
|
||||
CtlSleep MobileMethod = "sleep"
|
||||
CtlStartCamera MobileMethod = "camera_start" // alias for app_launch camera
|
||||
CtlStopCamera MobileMethod = "camera_stop" // alias for app_terminate camera
|
||||
RecordStart MobileMethod = "record_start"
|
||||
RecordStop MobileMethod = "record_stop"
|
||||
|
||||
// UI validation
|
||||
SelectorName string = "ui_name"
|
||||
@@ -314,28 +315,6 @@ func isPathExists(path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) FindUIElement(param string) (ele WebElement, err error) {
|
||||
var selector BySelector
|
||||
if strings.HasPrefix(param, "/") {
|
||||
// xpath
|
||||
selector = BySelector{
|
||||
XPath: param,
|
||||
}
|
||||
} else if strings.HasPrefix(param, "com.") {
|
||||
// name
|
||||
selector = BySelector{
|
||||
ResourceIdID: param,
|
||||
}
|
||||
} else {
|
||||
// name
|
||||
selector = BySelector{
|
||||
LinkText: NewElementAttribute().WithName(param),
|
||||
}
|
||||
}
|
||||
|
||||
return dExt.Driver.FindElement(selector)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) FindUIRectInUIKit(search string, options ...DataOption) (x, y, width, height float64, err error) {
|
||||
// click on text, using OCR
|
||||
if !isPathExists(search) {
|
||||
@@ -351,30 +330,6 @@ func (dExt *DriverExt) MappingToRectInUIKit(rect image.Rectangle) (x, y, width,
|
||||
return
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) PerformTouchActions(touchActions *TouchActions) error {
|
||||
return dExt.Driver.PerformAppiumTouchActions(touchActions)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) PerformActions(actions *W3CActions) error {
|
||||
return dExt.Driver.PerformW3CActions(actions)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) IsNameExist(name string) bool {
|
||||
selector := BySelector{
|
||||
LinkText: NewElementAttribute().WithName(name),
|
||||
}
|
||||
_, err := dExt.Driver.FindElement(selector)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) IsLabelExist(label string) bool {
|
||||
selector := BySelector{
|
||||
LinkText: NewElementAttribute().WithLabel(label),
|
||||
}
|
||||
_, err := dExt.Driver.FindElement(selector)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) IsOCRExist(text string) bool {
|
||||
_, _, _, _, err := dExt.FindTextByOCR(text)
|
||||
return err == nil
|
||||
@@ -400,12 +355,6 @@ func (dExt *DriverExt) DoAction(action MobileAction) error {
|
||||
}
|
||||
return fmt.Errorf("invalid %s params, should be bundleId(string), got %v",
|
||||
AppLaunch, action.Params)
|
||||
case AppLaunchUnattached:
|
||||
if bundleId, ok := action.Params.(string); ok {
|
||||
return dExt.Driver.AppLaunchUnattached(bundleId)
|
||||
}
|
||||
return fmt.Errorf("invalid %s params, should be bundleId(string), got %v",
|
||||
AppLaunchUnattached, action.Params)
|
||||
case ACTION_SwipeToTapApp:
|
||||
if appName, ok := action.Params.(string); ok {
|
||||
return dExt.swipeToTapApp(appName, action)
|
||||
@@ -688,10 +637,6 @@ func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...s
|
||||
}
|
||||
var result bool
|
||||
switch check {
|
||||
case SelectorName:
|
||||
result = (dExt.IsNameExist(expected) == exists)
|
||||
case SelectorLabel:
|
||||
result = (dExt.IsLabelExist(expected) == exists)
|
||||
case SelectorOCR:
|
||||
result = (dExt.IsOCRExist(expected) == exists)
|
||||
case SelectorImage:
|
||||
@@ -717,12 +662,57 @@ func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...s
|
||||
return true
|
||||
}
|
||||
|
||||
func checkErr(t *testing.T, err error, msg ...string) {
|
||||
if err != nil {
|
||||
if len(msg) == 0 {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
t.Fatal(msg, err)
|
||||
}
|
||||
func (dExt *DriverExt) ConnectMjpegStream(httpClient *http.Client) (err error) {
|
||||
if httpClient == nil {
|
||||
return errors.New(`'httpClient' can't be nil`)
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
if req, err = http.NewRequest(http.MethodGet, "http://*", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
if resp, err = httpClient.Do(req); err != nil {
|
||||
return err
|
||||
}
|
||||
// defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var boundary string
|
||||
if _, param, err := mime.ParseMediaType(resp.Header.Get("Content-Type")); err != nil {
|
||||
return err
|
||||
} else {
|
||||
boundary = strings.Trim(param["boundary"], "-")
|
||||
}
|
||||
|
||||
mjpegReader := multipart.NewReader(resp.Body, boundary)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-dExt.doneMjpegStream:
|
||||
_ = resp.Body.Close()
|
||||
return
|
||||
default:
|
||||
var part *multipart.Part
|
||||
if part, err = mjpegReader.NextPart(); err != nil {
|
||||
dExt.frame = nil
|
||||
continue
|
||||
}
|
||||
|
||||
raw := new(bytes.Buffer)
|
||||
if _, err = raw.ReadFrom(part); err != nil {
|
||||
dExt.frame = nil
|
||||
continue
|
||||
}
|
||||
dExt.frame = raw
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) CloseMjpegStream() {
|
||||
dExt.doneMjpegStream <- true
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
//go:build opencv
|
||||
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sort"
|
||||
)
|
||||
|
||||
func (dExt *DriverExt) GesturePassword(pathname string, password ...int) (err error) {
|
||||
var rects []image.Rectangle
|
||||
if rects, err = dExt.FindAllImageRect(pathname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sort.Slice(rects, func(i, j int) bool {
|
||||
if rects[i].Min.Y < rects[j].Min.Y {
|
||||
return true
|
||||
} else if rects[i].Min.Y == rects[j].Min.Y {
|
||||
if rects[i].Min.X < rects[j].Min.X {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
touchActions := NewTouchActions(len(password)*2 + 1)
|
||||
for i := range password {
|
||||
x, y, width, height := dExt.MappingToRectInUIKit(rects[password[i]])
|
||||
x = x + width*0.5
|
||||
y = y + height*0.5
|
||||
|
||||
if i == 0 {
|
||||
touchActions.Press(NewTouchActionPress().WithXYFloat(x, y)).
|
||||
Wait(0.2)
|
||||
} else {
|
||||
touchActions.MoveTo(NewTouchActionMoveTo().WithXYFloat(x, y)).
|
||||
Wait(0.2)
|
||||
}
|
||||
}
|
||||
touchActions.Release()
|
||||
|
||||
return dExt.PerformTouchActions(touchActions)
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//go:build opencv
|
||||
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDriverExt_GesturePassword(t *testing.T) {
|
||||
split := strings.Split("6304258", "")
|
||||
password := make([]int, len(split))
|
||||
for i := range split {
|
||||
password[i], _ = strconv.Atoi(split[i])
|
||||
}
|
||||
|
||||
driverExt, err := iosDevice.NewDriver(nil)
|
||||
checkErr(t, err)
|
||||
|
||||
pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/IMG_5.png"
|
||||
|
||||
err = driverExt.GesturePassword(pathSearch, password...)
|
||||
checkErr(t, err)
|
||||
}
|
||||
@@ -4,8 +4,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -30,13 +28,6 @@ func NewCapabilities() Capabilities {
|
||||
return make(Capabilities)
|
||||
}
|
||||
|
||||
func (caps Capabilities) WithAppLaunchOption(launchOpt AppLaunchOption) Capabilities {
|
||||
for k, v := range launchOpt {
|
||||
caps[k] = v
|
||||
}
|
||||
return caps
|
||||
}
|
||||
|
||||
// WithDefaultAlertAction
|
||||
func (caps Capabilities) WithDefaultAlertAction(alertAction AlertAction) Capabilities {
|
||||
caps["defaultAlertAction"] = alertAction
|
||||
@@ -286,44 +277,6 @@ func (v AppState) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// AppLaunchOption Configure app launch parameters
|
||||
type AppLaunchOption map[string]interface{}
|
||||
|
||||
func NewAppLaunchOption() AppLaunchOption {
|
||||
return make(AppLaunchOption)
|
||||
}
|
||||
|
||||
func (opt AppLaunchOption) WithBundleId(bundleId string) AppLaunchOption {
|
||||
opt["bundleId"] = bundleId
|
||||
return opt
|
||||
}
|
||||
|
||||
// WithShouldWaitForQuiescence whether to wait for quiescence on application startup
|
||||
// Defaults to `true`
|
||||
func (opt AppLaunchOption) WithShouldWaitForQuiescence(b bool) AppLaunchOption {
|
||||
opt["shouldWaitForQuiescence"] = b
|
||||
return opt
|
||||
}
|
||||
|
||||
// WithArguments The optional array of application command line arguments.
|
||||
// The arguments are going to be applied if the application was not running before.
|
||||
func (opt AppLaunchOption) WithArguments(args []string) AppLaunchOption {
|
||||
opt["arguments"] = args
|
||||
return opt
|
||||
}
|
||||
|
||||
// WithEnvironment The optional dictionary of environment variables for the application, which is going to be executed.
|
||||
// The environment variables are going to be applied if the application was not running before.
|
||||
func (opt AppLaunchOption) WithEnvironment(env map[string]string) AppLaunchOption {
|
||||
opt["environment"] = env
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt AppLaunchOption) WithBySelector(bySelector ...BySelector) AppLaunchOption {
|
||||
opt["bySelector"] = bySelector
|
||||
return opt
|
||||
}
|
||||
|
||||
// PasteboardType The type of the item on the pasteboard.
|
||||
type PasteboardType string
|
||||
|
||||
@@ -443,306 +396,6 @@ func (opt SourceOption) WithExcludedAttributes(attributes []string) SourceOption
|
||||
return opt
|
||||
}
|
||||
|
||||
const (
|
||||
// legacyWebElementIdentifier is the string constant used in the old
|
||||
// WebDriver JSON protocol that is the key for the map that contains an
|
||||
// unique element identifier.
|
||||
legacyWebElementIdentifier = "ELEMENT"
|
||||
|
||||
// webElementIdentifier is the string constant defined by the W3C
|
||||
// specification that is the key for the map that contains a unique element identifier.
|
||||
webElementIdentifier = "element-6066-11e4-a52e-4f735466cecf"
|
||||
)
|
||||
|
||||
func elementIDFromValue(val map[string]string) string {
|
||||
for _, key := range []string{webElementIdentifier, legacyWebElementIdentifier} {
|
||||
if v, ok := val[key]; ok && v != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// performance ranking: class name > accessibility id > link text > predicate > class chain > xpath
|
||||
type BySelector struct {
|
||||
ClassName ElementType `json:"class name"`
|
||||
|
||||
// isSearchByIdentifier
|
||||
Name string `json:"name"`
|
||||
Id string `json:"id"`
|
||||
AccessibilityId string `json:"accessibility id"`
|
||||
|
||||
// partialSearch
|
||||
LinkText ElementAttribute `json:"link text"`
|
||||
PartialLinkText ElementAttribute `json:"partial link text"`
|
||||
// partialSearch
|
||||
|
||||
Predicate string `json:"predicate string"`
|
||||
|
||||
ClassChain string `json:"class chain"`
|
||||
|
||||
XPath string `json:"xpath"` // not recommended, it's slow because it is not supported by XCTest natively
|
||||
|
||||
// Set the search criteria to match the given resource ResourceIdID.
|
||||
ResourceIdID string `json:"id"`
|
||||
// Set the search criteria to match the content-description property for a widget.
|
||||
ContentDescription string `json:"accessibility id"`
|
||||
|
||||
UiAutomator string `json:"-android uiautomator"`
|
||||
}
|
||||
|
||||
func (wl BySelector) getUsingAndValue() (using, value string) {
|
||||
vBy := reflect.ValueOf(wl)
|
||||
tBy := reflect.TypeOf(wl)
|
||||
for i := 0; i < vBy.NumField(); i++ {
|
||||
vi := vBy.Field(i).Interface()
|
||||
switch vi := vi.(type) {
|
||||
case ElementType:
|
||||
value = vi.String()
|
||||
case string:
|
||||
value = vi
|
||||
case ElementAttribute:
|
||||
value = vi.String()
|
||||
}
|
||||
if value != "" && value != "UNKNOWN" {
|
||||
using = tBy.Field(i).Tag.Get("json")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (by BySelector) getMethodAndSelector() (method, selector string) {
|
||||
vBy := reflect.ValueOf(by)
|
||||
tBy := reflect.TypeOf(by)
|
||||
for i := 0; i < vBy.NumField(); i++ {
|
||||
vi := vBy.Field(i).Interface()
|
||||
// switch vi := vi.(type) {
|
||||
// case string:
|
||||
// selector = vi
|
||||
// }
|
||||
selector = vi.(string)
|
||||
if selector != "" && selector != "UNKNOWN" {
|
||||
method = tBy.Field(i).Tag.Get("json")
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ElementAttribute map[string]interface{}
|
||||
|
||||
func (ea ElementAttribute) String() string {
|
||||
for k, v := range ea {
|
||||
switch v := v.(type) {
|
||||
case bool:
|
||||
return k + "=" + strconv.FormatBool(v)
|
||||
case string:
|
||||
return k + "=" + v
|
||||
default:
|
||||
return k + "=" + fmt.Sprintf("%v", v)
|
||||
}
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
func (ea ElementAttribute) getAttributeName() string {
|
||||
for k := range ea {
|
||||
return k
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
func NewElementAttribute() ElementAttribute {
|
||||
return make(ElementAttribute)
|
||||
}
|
||||
|
||||
// WithUID Element's unique identifier
|
||||
func (ea ElementAttribute) WithUID(uid string) ElementAttribute {
|
||||
ea["UID"] = uid
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithAccessibilityContainer Whether element is an accessibility container
|
||||
// (contains children of any depth that are accessible)
|
||||
func (ea ElementAttribute) WithAccessibilityContainer(b bool) ElementAttribute {
|
||||
ea["accessibilityContainer"] = b
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithAccessible Whether element is accessible
|
||||
func (ea ElementAttribute) WithAccessible(b bool) ElementAttribute {
|
||||
ea["accessible"] = b
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithEnabled Whether element is enabled
|
||||
func (ea ElementAttribute) WithEnabled(b bool) ElementAttribute {
|
||||
ea["enabled"] = b
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithLabel Element's label
|
||||
func (ea ElementAttribute) WithLabel(s string) ElementAttribute {
|
||||
ea["label"] = s
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithName Element's name
|
||||
func (ea ElementAttribute) WithName(s string) ElementAttribute {
|
||||
ea["name"] = s
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithSelected Element's selected state
|
||||
func (ea ElementAttribute) WithSelected(b bool) ElementAttribute {
|
||||
ea["selected"] = b
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithType Element's type
|
||||
func (ea ElementAttribute) WithType(elemType ElementType) ElementAttribute {
|
||||
ea["type"] = elemType
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithValue Element's value
|
||||
func (ea ElementAttribute) WithValue(s string) ElementAttribute {
|
||||
ea["value"] = s
|
||||
return ea
|
||||
}
|
||||
|
||||
// WithVisible
|
||||
//
|
||||
// Whether element is visible
|
||||
func (ea ElementAttribute) WithVisible(b bool) ElementAttribute {
|
||||
ea["visible"] = b
|
||||
return ea
|
||||
}
|
||||
|
||||
func (et ElementType) String() string {
|
||||
vBy := reflect.ValueOf(et)
|
||||
tBy := reflect.TypeOf(et)
|
||||
for i := 0; i < vBy.NumField(); i++ {
|
||||
if vBy.Field(i).Bool() {
|
||||
return tBy.Field(i).Tag.Get("json")
|
||||
}
|
||||
}
|
||||
return "UNKNOWN"
|
||||
}
|
||||
|
||||
// ElementType
|
||||
// !!! This mapping should be updated if there are changes after each new XCTest release"`
|
||||
type ElementType struct {
|
||||
Any bool `json:"XCUIElementTypeAny"`
|
||||
Other bool `json:"XCUIElementTypeOther"`
|
||||
Application bool `json:"XCUIElementTypeApplication"`
|
||||
Group bool `json:"XCUIElementTypeGroup"`
|
||||
Window bool `json:"XCUIElementTypeWindow"`
|
||||
Sheet bool `json:"XCUIElementTypeSheet"`
|
||||
Drawer bool `json:"XCUIElementTypeDrawer"`
|
||||
Alert bool `json:"XCUIElementTypeAlert"`
|
||||
Dialog bool `json:"XCUIElementTypeDialog"`
|
||||
Button bool `json:"XCUIElementTypeButton"`
|
||||
RadioButton bool `json:"XCUIElementTypeRadioButton"`
|
||||
RadioGroup bool `json:"XCUIElementTypeRadioGroup"`
|
||||
CheckBox bool `json:"XCUIElementTypeCheckBox"`
|
||||
DisclosureTriangle bool `json:"XCUIElementTypeDisclosureTriangle"`
|
||||
PopUpButton bool `json:"XCUIElementTypePopUpButton"`
|
||||
ComboBox bool `json:"XCUIElementTypeComboBox"`
|
||||
MenuButton bool `json:"XCUIElementTypeMenuButton"`
|
||||
ToolbarButton bool `json:"XCUIElementTypeToolbarButton"`
|
||||
Popover bool `json:"XCUIElementTypePopover"`
|
||||
Keyboard bool `json:"XCUIElementTypeKeyboard"`
|
||||
Key bool `json:"XCUIElementTypeKey"`
|
||||
NavigationBar bool `json:"XCUIElementTypeNavigationBar"`
|
||||
TabBar bool `json:"XCUIElementTypeTabBar"`
|
||||
TabGroup bool `json:"XCUIElementTypeTabGroup"`
|
||||
Toolbar bool `json:"XCUIElementTypeToolbar"`
|
||||
StatusBar bool `json:"XCUIElementTypeStatusBar"`
|
||||
Table bool `json:"XCUIElementTypeTable"`
|
||||
TableRow bool `json:"XCUIElementTypeTableRow"`
|
||||
TableColumn bool `json:"XCUIElementTypeTableColumn"`
|
||||
Outline bool `json:"XCUIElementTypeOutline"`
|
||||
OutlineRow bool `json:"XCUIElementTypeOutlineRow"`
|
||||
Browser bool `json:"XCUIElementTypeBrowser"`
|
||||
CollectionView bool `json:"XCUIElementTypeCollectionView"`
|
||||
Slider bool `json:"XCUIElementTypeSlider"`
|
||||
PageIndicator bool `json:"XCUIElementTypePageIndicator"`
|
||||
ProgressIndicator bool `json:"XCUIElementTypeProgressIndicator"`
|
||||
ActivityIndicator bool `json:"XCUIElementTypeActivityIndicator"`
|
||||
SegmentedControl bool `json:"XCUIElementTypeSegmentedControl"`
|
||||
Picker bool `json:"XCUIElementTypePicker"`
|
||||
PickerWheel bool `json:"XCUIElementTypePickerWheel"`
|
||||
Switch bool `json:"XCUIElementTypeSwitch"`
|
||||
Toggle bool `json:"XCUIElementTypeToggle"`
|
||||
Link bool `json:"XCUIElementTypeLink"`
|
||||
Image bool `json:"XCUIElementTypeImage"`
|
||||
Icon bool `json:"XCUIElementTypeIcon"`
|
||||
SearchField bool `json:"XCUIElementTypeSearchField"`
|
||||
ScrollView bool `json:"XCUIElementTypeScrollView"`
|
||||
ScrollBar bool `json:"XCUIElementTypeScrollBar"`
|
||||
StaticText bool `json:"XCUIElementTypeStaticText"`
|
||||
TextField bool `json:"XCUIElementTypeTextField"`
|
||||
SecureTextField bool `json:"XCUIElementTypeSecureTextField"`
|
||||
DatePicker bool `json:"XCUIElementTypeDatePicker"`
|
||||
TextView bool `json:"XCUIElementTypeTextView"`
|
||||
Menu bool `json:"XCUIElementTypeMenu"`
|
||||
MenuItem bool `json:"XCUIElementTypeMenuItem"`
|
||||
MenuBar bool `json:"XCUIElementTypeMenuBar"`
|
||||
MenuBarItem bool `json:"XCUIElementTypeMenuBarItem"`
|
||||
Map bool `json:"XCUIElementTypeMap"`
|
||||
WebView bool `json:"XCUIElementTypeWebView"`
|
||||
IncrementArrow bool `json:"XCUIElementTypeIncrementArrow"`
|
||||
DecrementArrow bool `json:"XCUIElementTypeDecrementArrow"`
|
||||
Timeline bool `json:"XCUIElementTypeTimeline"`
|
||||
RatingIndicator bool `json:"XCUIElementTypeRatingIndicator"`
|
||||
ValueIndicator bool `json:"XCUIElementTypeValueIndicator"`
|
||||
SplitGroup bool `json:"XCUIElementTypeSplitGroup"`
|
||||
Splitter bool `json:"XCUIElementTypeSplitter"`
|
||||
RelevanceIndicator bool `json:"XCUIElementTypeRelevanceIndicator"`
|
||||
ColorWell bool `json:"XCUIElementTypeColorWell"`
|
||||
HelpTag bool `json:"XCUIElementTypeHelpTag"`
|
||||
Matte bool `json:"XCUIElementTypeMatte"`
|
||||
DockItem bool `json:"XCUIElementTypeDockItem"`
|
||||
Ruler bool `json:"XCUIElementTypeRuler"`
|
||||
RulerMarker bool `json:"XCUIElementTypeRulerMarker"`
|
||||
Grid bool `json:"XCUIElementTypeGrid"`
|
||||
LevelIndicator bool `json:"XCUIElementTypeLevelIndicator"`
|
||||
Cell bool `json:"XCUIElementTypeCell"`
|
||||
LayoutArea bool `json:"XCUIElementTypeLayoutArea"`
|
||||
LayoutItem bool `json:"XCUIElementTypeLayoutItem"`
|
||||
Handle bool `json:"XCUIElementTypeHandle"`
|
||||
Stepper bool `json:"XCUIElementTypeStepper"`
|
||||
Tab bool `json:"XCUIElementTypeTab"`
|
||||
TouchBar bool `json:"XCUIElementTypeTouchBar"`
|
||||
StatusItem bool `json:"XCUIElementTypeStatusItem"`
|
||||
EditText bool `json:"android.widget.EditText"`
|
||||
}
|
||||
|
||||
// ProtectedResource A system resource that requires user authorization to access.
|
||||
type ProtectedResource int
|
||||
|
||||
// https://developer.apple.com/documentation/xctest/xcuiprotectedresource?language=objc
|
||||
const (
|
||||
ProtectedResourceContacts ProtectedResource = 1
|
||||
ProtectedResourceCalendar ProtectedResource = 2
|
||||
ProtectedResourceReminders ProtectedResource = 3
|
||||
ProtectedResourcePhotos ProtectedResource = 4
|
||||
ProtectedResourceMicrophone ProtectedResource = 5
|
||||
ProtectedResourceCamera ProtectedResource = 6
|
||||
ProtectedResourceMediaLibrary ProtectedResource = 7
|
||||
ProtectedResourceHomeKit ProtectedResource = 8
|
||||
ProtectedResourceSystemRootDirectory ProtectedResource = 0x40000000
|
||||
ProtectedResourceUserDesktopDirectory ProtectedResource = 0x40000001
|
||||
ProtectedResourceUserDownloadsDirectory ProtectedResource = 0x40000002
|
||||
ProtectedResourceUserDocumentsDirectory ProtectedResource = 0x40000003
|
||||
ProtectedResourceBluetooth ProtectedResource = -0x40000000
|
||||
ProtectedResourceKeyboardNetwork ProtectedResource = -0x40000001
|
||||
ProtectedResourceLocation ProtectedResource = -0x40000002
|
||||
ProtectedResourceHealth ProtectedResource = -0x40000003
|
||||
)
|
||||
|
||||
type Condition func(wd WebDriver) (bool, error)
|
||||
|
||||
type Direction string
|
||||
@@ -968,55 +621,16 @@ type WebDriver interface {
|
||||
WindowSize() (Size, error)
|
||||
Screen() (Screen, error)
|
||||
Scale() (float64, error)
|
||||
ActiveAppInfo() (AppInfo, error)
|
||||
// ActiveAppsList Retrieves the information about the currently active apps
|
||||
ActiveAppsList() ([]AppBaseInfo, error)
|
||||
// AppState Get the state of the particular application in scope of the current session.
|
||||
// !This method is only returning reliable results since Xcode9 SDK
|
||||
AppState(bundleId string) (AppState, error)
|
||||
|
||||
// IsLocked Checks if the screen is locked or not.
|
||||
IsLocked() (bool, error)
|
||||
// Unlock Forces the device under test to unlock.
|
||||
// An immediate return will happen if the device is already unlocked
|
||||
// and an error is going to be thrown if the screen has not been unlocked after the timeout.
|
||||
Unlock() error
|
||||
// Lock Forces the device under test to switch to the lock screen.
|
||||
// An immediate return will happen if the device is already locked
|
||||
// and an error is going to be thrown if the screen has not been locked after the timeout.
|
||||
Lock() error
|
||||
|
||||
// Homescreen Forces the device under test to switch to the home screen
|
||||
Homescreen() error
|
||||
|
||||
// AlertText Returns alert's title and description separated by new lines
|
||||
AlertText() (string, error)
|
||||
// AlertButtons Gets the labels of the buttons visible in the alert
|
||||
AlertButtons() ([]string, error)
|
||||
// AlertAccept Accepts alert, if present
|
||||
AlertAccept(label ...string) error
|
||||
// AlertDismiss Dismisses alert, if present
|
||||
AlertDismiss(label ...string) error
|
||||
// AlertSendKeys Types a text into an input inside the alert container, if it is present
|
||||
AlertSendKeys(text string) error
|
||||
|
||||
// AppLaunch Launch an application with given bundle identifier in scope of current session.
|
||||
// !This method is only available since Xcode9 SDK
|
||||
AppLaunch(bundleId string, launchOpt ...AppLaunchOption) error
|
||||
// AppLaunchUnattached Launch the app with the specified bundle ID.
|
||||
AppLaunchUnattached(bundleId string) error
|
||||
AppLaunch(bundleId string) error
|
||||
// AppTerminate Terminate an application with the given bundle id.
|
||||
// Either `true` if the app has been successfully terminated or `false` if it was not running
|
||||
AppTerminate(bundleId string) (bool, error)
|
||||
// AppActivate Activate an application with given bundle identifier in scope of current session.
|
||||
// !This method is only available since Xcode9 SDK
|
||||
AppActivate(bundleId string) error
|
||||
// AppDeactivate Deactivates application for given time and then activate it again
|
||||
// The minimum application switch wait is 3 seconds
|
||||
AppDeactivate(second float64) error
|
||||
|
||||
// AppAuthReset Resets the authorization status for a protected resource. Available since Xcode 11.4
|
||||
AppAuthReset(ProtectedResource) error
|
||||
|
||||
// StartCamera Starts a new camera for recording
|
||||
StartCamera() error
|
||||
@@ -1045,13 +659,6 @@ type WebDriver interface {
|
||||
Swipe(fromX, fromY, toX, toY int, options ...DataOption) error
|
||||
SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error
|
||||
|
||||
ForceTouch(x, y int, pressure float64, second ...float64) error
|
||||
ForceTouchFloat(x, y, pressure float64, second ...float64) error
|
||||
|
||||
// PerformW3CActions Perform complex touch action in scope of the current application.
|
||||
PerformW3CActions(actions *W3CActions) error
|
||||
PerformAppiumTouchActions(touchActs *TouchActions) error
|
||||
|
||||
// SetPasteboard Sets data to the general pasteboard
|
||||
SetPasteboard(contentType PasteboardType, content string) error
|
||||
// GetPasteboard Gets the data contained in the general pasteboard.
|
||||
@@ -1066,45 +673,12 @@ type WebDriver interface {
|
||||
// Input works like SendKeys
|
||||
Input(text string, options ...DataOption) error
|
||||
|
||||
// KeyboardDismiss Tries to dismiss the on-screen keyboard
|
||||
KeyboardDismiss(keyNames ...string) error
|
||||
|
||||
// PressButton Presses the corresponding hardware button on the device
|
||||
PressButton(devBtn DeviceButton) error
|
||||
|
||||
// PressBack Presses the back button
|
||||
PressBack(options ...DataOption) error
|
||||
|
||||
// IOHIDEvent Emulated triggering of the given low-level IOHID device event.
|
||||
// duration: The event duration in float seconds (XCTest uses 0.005 for a single press event)
|
||||
IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) error
|
||||
|
||||
// ExpectNotification Creates an expectation that is fulfilled when an expected Notification is received
|
||||
ExpectNotification(notifyName string, notifyType NotificationType, second ...int) error
|
||||
|
||||
// SiriActivate Activates Siri service voice recognition with the given text to parse
|
||||
SiriActivate(text string) error
|
||||
// SiriOpenUrl Opens the particular url scheme using Siri voice recognition helpers.
|
||||
// !This will only work since XCode 8.3/iOS 10.3
|
||||
// It doesn't actually work, right?
|
||||
SiriOpenUrl(url string) error
|
||||
|
||||
Orientation() (Orientation, error)
|
||||
// SetOrientation Sets requested device interface orientation.
|
||||
SetOrientation(Orientation) error
|
||||
|
||||
Rotation() (Rotation, error)
|
||||
// SetRotation Sets the devices orientation to the rotation passed.
|
||||
SetRotation(Rotation) error
|
||||
|
||||
// MatchTouchID Matches or mismatches TouchID request
|
||||
MatchTouchID(isMatch bool) error
|
||||
|
||||
// ActiveElement Returns the element, which currently holds the keyboard input focus or nil if there are no such elements.
|
||||
ActiveElement() (WebElement, error)
|
||||
FindElement(by BySelector) (WebElement, error)
|
||||
FindElements(by BySelector) ([]WebElement, error)
|
||||
|
||||
Screenshot() (*bytes.Buffer, error)
|
||||
|
||||
// Source Return application elements tree
|
||||
@@ -1122,13 +696,6 @@ type WebDriver interface {
|
||||
|
||||
IsHealthy() (bool, error)
|
||||
|
||||
// WaitWithTimeoutAndInterval waits for the condition to evaluate to true.
|
||||
WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error
|
||||
// WaitWithTimeout works like WaitWithTimeoutAndInterval, but with default polling interval.
|
||||
WaitWithTimeout(condition Condition, timeout time.Duration) error
|
||||
// Wait works like WaitWithTimeoutAndInterval, but using the default timeout and polling interval.
|
||||
Wait(condition Condition) error
|
||||
|
||||
// Close inner connections properly
|
||||
Close() error
|
||||
|
||||
@@ -1136,94 +703,3 @@ type WebDriver interface {
|
||||
StartCaptureLog(identifier ...string) (err error)
|
||||
StopCaptureLog() (result interface{}, err error)
|
||||
}
|
||||
|
||||
// WebElement defines method supported by web elements.
|
||||
type WebElement interface {
|
||||
// Click Waits for element to become stable (not move) and performs sync tap on element.
|
||||
Click() error
|
||||
// SendKeys Types a text into element. It will try to activate keyboard on element,
|
||||
// if element has no keyboard focus.
|
||||
// frequency: Frequency of typing (letters per sec). The default value is 60
|
||||
SendKeys(text string, options ...DataOption) error
|
||||
// Clear Clears text on element. It will try to activate keyboard on element,
|
||||
// if element has no keyboard focus.
|
||||
Clear() error
|
||||
|
||||
// Tap Waits for element to become stable (not move) and performs sync tap on element,
|
||||
// relative to the current element position
|
||||
Tap(x, y int) error
|
||||
TapFloat(x, y float64) error
|
||||
|
||||
// DoubleTap Sends a double tap event to a hittable point computed for the element.
|
||||
DoubleTap() error
|
||||
|
||||
// TouchAndHold Sends a long-press gesture to a hittable point computed for the element,
|
||||
// holding for the specified duration.
|
||||
// second: The default value is 1
|
||||
TouchAndHold(second ...float64) error
|
||||
// TwoFingerTap Sends a two finger tap event to a hittable point computed for the element.
|
||||
TwoFingerTap() error
|
||||
// TapWithNumberOfTaps Sends one or more taps with one or more touch points.
|
||||
TapWithNumberOfTaps(numberOfTaps, numberOfTouches int) error
|
||||
// ForceTouch Waits for element to become stable (not move) and performs sync force touch on element.
|
||||
// second: The default value is 1
|
||||
ForceTouch(pressure float64, second ...float64) error
|
||||
// ForceTouchFloat works like ForceTouch, but relative to the current element position
|
||||
ForceTouchFloat(x, y, pressure float64, second ...float64) error
|
||||
|
||||
// Drag Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
|
||||
// relative to the current element position
|
||||
// pressForDuration: The default value is 1 second.
|
||||
Drag(fromX, fromY, toX, toY int, pressForDuration ...float64) error
|
||||
DragFloat(fromX, fromY, toX, toY float64, pressForDuration ...float64) error
|
||||
|
||||
// Swipe works like Drag, but `pressForDuration` value is 0.
|
||||
// relative to the current element position
|
||||
Swipe(fromX, fromY, toX, toY int) error
|
||||
SwipeFloat(fromX, fromY, toX, toY float64) error
|
||||
// SwipeDirection Performs swipe gesture on the element.
|
||||
// velocity: swipe speed in pixels per second. Custom velocity values are only supported since Xcode SDK 11.4.
|
||||
SwipeDirection(direction Direction, velocity ...float64) error
|
||||
|
||||
// Pinch Sends a pinching gesture with two touches.
|
||||
// scale: The scale of the pinch gesture. Use a scale between 0 and 1 to "pinch close" or zoom out
|
||||
// and a scale greater than 1 to "pinch open" or zoom in.
|
||||
// velocity: The velocity of the pinch in scale factor per second.
|
||||
Pinch(scale, velocity float64) error
|
||||
PinchToZoomOutByW3CAction(scale ...float64) error
|
||||
|
||||
// Rotate Sends a rotation gesture with two touches.
|
||||
// rotation: The rotation of the gesture in radians.
|
||||
// velocity: The velocity of the rotation gesture in radians per second.
|
||||
Rotate(rotation float64, velocity ...float64) error
|
||||
|
||||
// PickerWheelSelect
|
||||
// offset: The default value is 2
|
||||
PickerWheelSelect(order PickerWheelOrder, offset ...int) error
|
||||
|
||||
ScrollElementByName(name string) error
|
||||
ScrollElementByPredicate(predicate string) error
|
||||
ScrollToVisible() error
|
||||
// ScrollDirection
|
||||
// distance: The default value is 0.5
|
||||
ScrollDirection(direction Direction, distance ...float64) error
|
||||
|
||||
FindElement(by BySelector) (element WebElement, err error)
|
||||
FindElements(by BySelector) (elements []WebElement, err error)
|
||||
FindVisibleCells() (elements []WebElement, err error)
|
||||
|
||||
Rect() (rect Rect, err error)
|
||||
Location() (Point, error)
|
||||
Size() (Size, error)
|
||||
Text() (text string, err error)
|
||||
Type() (elemType string, err error)
|
||||
IsEnabled() (enabled bool, err error)
|
||||
IsDisplayed() (displayed bool, err error)
|
||||
IsSelected() (selected bool, err error)
|
||||
IsAccessible() (accessible bool, err error)
|
||||
IsAccessibilityContainer() (isAccessibilityContainer bool, err error)
|
||||
GetAttribute(attr ElementAttribute) (value string, err error)
|
||||
UID() (uid string)
|
||||
|
||||
Screenshot() (raw *bytes.Buffer, err error)
|
||||
}
|
||||
|
||||
@@ -1,373 +0,0 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type W3CActions []map[string]interface{}
|
||||
|
||||
func NewW3CActions(capacity ...int) *W3CActions {
|
||||
if len(capacity) == 0 || capacity[0] <= 0 {
|
||||
capacity = []int{8}
|
||||
}
|
||||
tmp := make(W3CActions, 0, capacity[0])
|
||||
return &tmp
|
||||
}
|
||||
|
||||
func (act *W3CActions) SendKeys(text string) *W3CActions {
|
||||
keyboard := make(map[string]interface{})
|
||||
keyboard["type"] = "key"
|
||||
keyboard["id"] = "keyboard" + strconv.FormatInt(int64(len(*act)+1), 10)
|
||||
|
||||
ss := strings.Split(text, "")
|
||||
type KeyEvent struct {
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
actOptKey := make([]KeyEvent, 0, len(ss)+1)
|
||||
for i := range ss {
|
||||
actOptKey = append(
|
||||
actOptKey,
|
||||
KeyEvent{Type: "keyDown", Value: ss[i]},
|
||||
KeyEvent{Type: "keyUp", Value: ss[i]},
|
||||
)
|
||||
}
|
||||
keyboard["actions"] = actOptKey
|
||||
*act = append(*act, keyboard)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *W3CActions) _newFinger() map[string]interface{} {
|
||||
pointer := make(map[string]interface{})
|
||||
pointer["type"] = "pointer"
|
||||
pointer["id"] = "finger" + strconv.FormatInt(int64(len(*act)+1), 10)
|
||||
pointer["parameters"] = map[string]string{"pointerType": "touch"}
|
||||
return pointer
|
||||
}
|
||||
|
||||
func (act *W3CActions) FingerAction(fingerAct *FingerAction, fActs ...*FingerAction) *W3CActions {
|
||||
fActs = append([]*FingerAction{fingerAct}, fActs...)
|
||||
for i := range fActs {
|
||||
pointer := act._newFinger()
|
||||
pointer["actions"] = *fActs[i]
|
||||
*act = append(*act, pointer)
|
||||
}
|
||||
return act
|
||||
}
|
||||
|
||||
type FingerAction []map[string]interface{}
|
||||
|
||||
func NewFingerAction(capacity ...int) *FingerAction {
|
||||
if len(capacity) == 0 || capacity[0] <= 0 {
|
||||
capacity = []int{8}
|
||||
}
|
||||
tmp := make(FingerAction, 0, capacity[0])
|
||||
return &tmp
|
||||
}
|
||||
|
||||
type FingerMove map[string]interface{}
|
||||
|
||||
func NewFingerMove() FingerMove {
|
||||
return map[string]interface{}{"type": "pointerMove"}
|
||||
}
|
||||
|
||||
func (fm FingerMove) WithXY(x, y int) FingerMove {
|
||||
fm["x"] = x
|
||||
fm["y"] = y
|
||||
return fm
|
||||
}
|
||||
|
||||
func (fm FingerMove) WithXYFloat(x, y float64) FingerMove {
|
||||
fm["x"] = x
|
||||
fm["y"] = y
|
||||
return fm
|
||||
}
|
||||
|
||||
func (fm FingerMove) WithOrigin(element WebElement) FingerMove {
|
||||
fm["origin"] = element.UID()
|
||||
return fm
|
||||
}
|
||||
|
||||
func (fm FingerMove) WithDuration(second float64) FingerMove {
|
||||
fm["duration"] = second
|
||||
return fm
|
||||
}
|
||||
|
||||
func (fa *FingerAction) Move(fm FingerMove) *FingerAction {
|
||||
*fa = append(*fa, fm)
|
||||
return fa
|
||||
}
|
||||
|
||||
func (fa *FingerAction) Down() *FingerAction {
|
||||
*fa = append(*fa, map[string]interface{}{"type": "pointerDown"})
|
||||
return fa
|
||||
}
|
||||
|
||||
func (fa *FingerAction) Up() *FingerAction {
|
||||
*fa = append(*fa, map[string]interface{}{"type": "pointerUp"})
|
||||
return fa
|
||||
}
|
||||
|
||||
func (fa *FingerAction) Pause(second ...float64) *FingerAction {
|
||||
if len(second) == 0 || second[0] < 0 {
|
||||
second = []float64{0.5}
|
||||
}
|
||||
tmp := map[string]interface{}{
|
||||
"type": "pause",
|
||||
"duration": second[0] * 1000,
|
||||
}
|
||||
*fa = append(*fa, tmp)
|
||||
return fa
|
||||
}
|
||||
|
||||
func (act *W3CActions) Tap(x, y int, element ...WebElement) *W3CActions {
|
||||
fm := NewFingerMove().WithXY(x, y)
|
||||
if len(element) != 0 {
|
||||
fm.WithOrigin(element[0])
|
||||
}
|
||||
fingerAction := NewFingerAction().
|
||||
Move(fm).
|
||||
Down().
|
||||
Pause(0.1).
|
||||
Up()
|
||||
return act.FingerAction(fingerAction)
|
||||
}
|
||||
|
||||
func (act *W3CActions) DoubleTap(x, y int, element ...WebElement) *W3CActions {
|
||||
fm := NewFingerMove().WithXY(x, y)
|
||||
if len(element) != 0 {
|
||||
fm.WithOrigin(element[0])
|
||||
}
|
||||
fingerAction := NewFingerAction().
|
||||
Move(fm).
|
||||
Down().
|
||||
Pause(0.1).
|
||||
Up().
|
||||
Pause(0.04).
|
||||
Down().
|
||||
Pause(0.1).
|
||||
Up()
|
||||
return act.FingerAction(fingerAction)
|
||||
}
|
||||
|
||||
func (act *W3CActions) Press(x, y int, second float64, element ...WebElement) *W3CActions {
|
||||
fm := NewFingerMove().WithXY(x, y)
|
||||
if len(element) != 0 {
|
||||
fm.WithOrigin(element[0])
|
||||
}
|
||||
fingerAction := NewFingerAction().
|
||||
Move(fm).
|
||||
Down().
|
||||
Pause(second).
|
||||
Up()
|
||||
return act.FingerAction(fingerAction)
|
||||
}
|
||||
|
||||
func (act *W3CActions) Swipe(fromX, fromY, toX, toY int, element ...WebElement) *W3CActions {
|
||||
fmFrom := NewFingerMove().WithXY(fromX, fromY)
|
||||
fmTo := NewFingerMove().WithXY(toX, toY)
|
||||
if len(element) != 0 {
|
||||
fmFrom.WithOrigin(element[0])
|
||||
fmTo.WithOrigin(element[0])
|
||||
}
|
||||
fingerAction := NewFingerAction().
|
||||
Move(fmFrom).
|
||||
Down().
|
||||
Pause(0.25).
|
||||
Move(fmTo).
|
||||
Pause(0.25).
|
||||
Up()
|
||||
return act.FingerAction(fingerAction)
|
||||
}
|
||||
|
||||
func (act *W3CActions) SwipeFloat(fromX, fromY, toX, toY float64, element ...WebElement) *W3CActions {
|
||||
fmFrom := NewFingerMove().WithXYFloat(fromX, fromY)
|
||||
fmTo := NewFingerMove().WithXYFloat(toX, toY)
|
||||
if len(element) != 0 {
|
||||
fmFrom.WithOrigin(element[0])
|
||||
fmTo.WithOrigin(element[0])
|
||||
}
|
||||
fingerAction := NewFingerAction().
|
||||
Move(fmFrom).
|
||||
Down().
|
||||
Pause(0.25).
|
||||
Move(fmTo).
|
||||
Pause(0.25).
|
||||
Up()
|
||||
return act.FingerAction(fingerAction)
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------------------- */
|
||||
|
||||
type TouchActions []map[string]interface{}
|
||||
|
||||
func NewTouchActions(capacity ...int) *TouchActions {
|
||||
if len(capacity) == 0 || capacity[0] <= 0 {
|
||||
capacity = []int{8}
|
||||
}
|
||||
tmp := make(TouchActions, 0, capacity[0])
|
||||
return &tmp
|
||||
}
|
||||
|
||||
func (act *TouchActions) MoveTo(opt TouchActionMoveTo) *TouchActions {
|
||||
tmp := map[string]interface{}{
|
||||
"action": "moveTo",
|
||||
"options": opt,
|
||||
}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *TouchActions) Tap(opt TouchActionTap) *TouchActions {
|
||||
tmp := map[string]interface{}{
|
||||
"action": "tap",
|
||||
"options": opt,
|
||||
}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *TouchActions) Press(opt TouchActionPress) *TouchActions {
|
||||
tmp := map[string]interface{}{
|
||||
"action": "press",
|
||||
"options": opt,
|
||||
}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *TouchActions) LongPress(opt TouchActionLongPress) *TouchActions {
|
||||
tmp := map[string]interface{}{
|
||||
"action": "longPress",
|
||||
"options": opt,
|
||||
}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *TouchActions) Wait(second ...float64) *TouchActions {
|
||||
if len(second) == 0 || second[0] < 0 {
|
||||
second = []float64{0.5}
|
||||
}
|
||||
tmp := map[string]interface{}{
|
||||
"action": "wait",
|
||||
"options": map[string]interface{}{"ms": second[0] * 1000},
|
||||
}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *TouchActions) Release() *TouchActions {
|
||||
tmp := map[string]interface{}{"action": "release"}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
func (act *TouchActions) Cancel() *TouchActions {
|
||||
tmp := map[string]interface{}{"action": "cancel"}
|
||||
*act = append(*act, tmp)
|
||||
return act
|
||||
}
|
||||
|
||||
type TouchActionMoveTo map[string]interface{}
|
||||
|
||||
func NewTouchActionMoveTo() TouchActionMoveTo {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
func (opt TouchActionMoveTo) WithXY(x, y int) TouchActionMoveTo {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionMoveTo) WithXYFloat(x, y float64) TouchActionMoveTo {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionMoveTo) WithElement(element WebElement) TouchActionMoveTo {
|
||||
opt["element"] = element.UID()
|
||||
return opt
|
||||
}
|
||||
|
||||
type TouchActionTap map[string]interface{}
|
||||
|
||||
func NewTouchActionTap() TouchActionTap {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
func (opt TouchActionTap) WithXY(x, y int) TouchActionTap {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionTap) WithXYFloat(x, y float64) TouchActionTap {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionTap) WithElement(element WebElement) TouchActionTap {
|
||||
opt["element"] = element.UID()
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionTap) WithCount(count int) TouchActionTap {
|
||||
opt["count"] = count
|
||||
return opt
|
||||
}
|
||||
|
||||
type TouchActionPress map[string]interface{}
|
||||
|
||||
func NewTouchActionPress() TouchActionPress {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
func (opt TouchActionPress) WithXY(x, y int) TouchActionPress {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionPress) WithXYFloat(x, y float64) TouchActionPress {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionPress) WithElement(element WebElement) TouchActionPress {
|
||||
opt["element"] = element.UID()
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionPress) WithPressure(pressure float64) TouchActionPress {
|
||||
opt["pressure"] = pressure
|
||||
return opt
|
||||
}
|
||||
|
||||
type TouchActionLongPress map[string]interface{}
|
||||
|
||||
func NewTouchActionLongPress() TouchActionLongPress {
|
||||
return make(map[string]interface{})
|
||||
}
|
||||
|
||||
func (opt TouchActionLongPress) WithXY(x, y int) TouchActionLongPress {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionLongPress) WithXYFloat(x, y float64) TouchActionLongPress {
|
||||
opt["x"] = x
|
||||
opt["y"] = y
|
||||
return opt
|
||||
}
|
||||
|
||||
func (opt TouchActionLongPress) WithElement(element WebElement) TouchActionLongPress {
|
||||
opt["element"] = element.UID()
|
||||
return opt
|
||||
}
|
||||
@@ -1,23 +1,16 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
builtinJSON "encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
builtinLog "log"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -25,7 +18,6 @@ import (
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/code"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/env"
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
|
||||
)
|
||||
|
||||
@@ -687,168 +679,3 @@ func (dev *IOSDevice) RunXCTest(bundleID string) (cancel context.CancelFunc, err
|
||||
|
||||
return cancel, nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) ConnectMjpegStream(httpClient *http.Client) (err error) {
|
||||
if httpClient == nil {
|
||||
return errors.New(`'httpClient' can't be nil`)
|
||||
}
|
||||
|
||||
var req *http.Request
|
||||
if req, err = http.NewRequest(http.MethodGet, "http://*", nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
if resp, err = httpClient.Do(req); err != nil {
|
||||
return err
|
||||
}
|
||||
// defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
var boundary string
|
||||
if _, param, err := mime.ParseMediaType(resp.Header.Get("Content-Type")); err != nil {
|
||||
return err
|
||||
} else {
|
||||
boundary = strings.Trim(param["boundary"], "-")
|
||||
}
|
||||
|
||||
mjpegReader := multipart.NewReader(resp.Body, boundary)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-dExt.doneMjpegStream:
|
||||
_ = resp.Body.Close()
|
||||
return
|
||||
default:
|
||||
var part *multipart.Part
|
||||
if part, err = mjpegReader.NextPart(); err != nil {
|
||||
dExt.frame = nil
|
||||
continue
|
||||
}
|
||||
|
||||
raw := new(bytes.Buffer)
|
||||
if _, err = raw.ReadFrom(part); err != nil {
|
||||
dExt.frame = nil
|
||||
continue
|
||||
}
|
||||
dExt.frame = raw
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) CloseMjpegStream() {
|
||||
dExt.doneMjpegStream <- true
|
||||
}
|
||||
|
||||
type rawResponse []byte
|
||||
|
||||
func (r rawResponse) 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 rawResponse) 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 rawResponse) 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 rawResponse) valueConvertToSessionInfo() (sessionInfo SessionInfo, err error) {
|
||||
reply := new(struct{ Value struct{ SessionInfo } })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return SessionInfo{}, err
|
||||
}
|
||||
sessionInfo = reply.Value.SessionInfo
|
||||
return
|
||||
}
|
||||
|
||||
func (r rawResponse) 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 rawResponse) 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
|
||||
}
|
||||
|
||||
var errNoSuchElement = errors.New("no such element")
|
||||
|
||||
func (r rawResponse) valueConvertToElementID() (id string, err error) {
|
||||
reply := new(struct{ Value map[string]string })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(reply.Value) == 0 {
|
||||
return "", errNoSuchElement
|
||||
}
|
||||
if id = elementIDFromValue(reply.Value); id == "" {
|
||||
return "", fmt.Errorf("invalid element returned: %+v", reply)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r rawResponse) valueConvertToElementIDs() (IDs []string, err error) {
|
||||
reply := new(struct{ Value []map[string]string })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(reply.Value) == 0 {
|
||||
return nil, errNoSuchElement
|
||||
}
|
||||
IDs = make([]string, len(reply.Value))
|
||||
for i, elem := range reply.Value {
|
||||
var id string
|
||||
if id = elementIDFromValue(elem); id == "" {
|
||||
return nil, fmt.Errorf("invalid element returned: %+v", reply)
|
||||
}
|
||||
IDs[i] = id
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
@@ -315,12 +315,9 @@ func (wd *wdaDriver) AlertSendKeys(text string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) AppLaunch(bundleId string, launchOpt ...AppLaunchOption) (err error) {
|
||||
func (wd *wdaDriver) AppLaunch(bundleId string) (err error) {
|
||||
// [[FBRoute POST:@"/wda/apps/launch"] respondWithTarget:self action:@selector(handleSessionAppLaunch:)]
|
||||
data := make(map[string]interface{})
|
||||
if len(launchOpt) != 0 {
|
||||
data = launchOpt[0]
|
||||
}
|
||||
data["bundleId"] = bundleId
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/apps/launch")
|
||||
return
|
||||
@@ -363,13 +360,6 @@ func (wd *wdaDriver) AppDeactivate(second float64) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) AppAuthReset(resource ProtectedResource) (err error) {
|
||||
// [[FBRoute POST:@"/wda/resetAppAuth"] respondWithTarget:self action:@selector(handleResetAppAuth:)]
|
||||
data := map[string]interface{}{"resource": resource}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/resetAppAuth")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) Tap(x, y int, options ...DataOption) error {
|
||||
return wd.TapFloat(float64(x), float64(y), options...)
|
||||
}
|
||||
@@ -447,37 +437,6 @@ func (wd *wdaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataO
|
||||
return wd.DragFloat(fromX, fromY, toX, toY, options...)
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) ForceTouch(x, y int, pressure float64, second ...float64) error {
|
||||
return wd.ForceTouchFloat(float64(x), float64(y), pressure, second...)
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) ForceTouchFloat(x, y, pressure float64, second ...float64) error {
|
||||
if len(second) == 0 || second[0] <= 0 {
|
||||
second = []float64{1.0}
|
||||
}
|
||||
actions := NewTouchActions().
|
||||
Press(
|
||||
NewTouchActionPress().WithXYFloat(x, y).WithPressure(pressure)).
|
||||
Wait(second[0]).
|
||||
Release()
|
||||
return wd.PerformAppiumTouchActions(actions)
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) PerformW3CActions(actions *W3CActions) (err error) {
|
||||
// [[FBRoute POST:@"/actions"] respondWithTarget:self action:@selector(handlePerformW3CTouchActions:)]
|
||||
data := map[string]interface{}{"actions": actions}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/actions")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) PerformAppiumTouchActions(touchActs *TouchActions) (err error) {
|
||||
// [[FBRoute POST:@"/wda/touch/perform"] respondWithTarget:self action:@selector(handlePerformAppiumTouchActions:)]
|
||||
// [[FBRoute POST:@"/wda/touch/multi/perform"]
|
||||
data := map[string]interface{}{"actions": touchActs}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/touch/multi/perform")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
|
||||
// [[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)]
|
||||
data := map[string]interface{}{
|
||||
@@ -516,16 +475,6 @@ func (wd *wdaDriver) Input(text string, options ...DataOption) (err error) {
|
||||
return wd.SendKeys(text, options...)
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) KeyboardDismiss(keyNames ...string) (err error) {
|
||||
// [[FBRoute POST:@"/wda/keyboard/dismiss"] respondWithTarget:self action:@selector(handleDismissKeyboardCommand:)]
|
||||
if len(keyNames) == 0 {
|
||||
keyNames = []string{"return"}
|
||||
}
|
||||
data := map[string]interface{}{"keyNames": keyNames}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/keyboard/dismiss")
|
||||
return
|
||||
}
|
||||
|
||||
// PressBack simulates a short press on the BACK button.
|
||||
func (wd *wdaDriver) PressBack(options ...DataOption) (err error) {
|
||||
windowSize, err := wd.WindowSize()
|
||||
@@ -554,20 +503,6 @@ func (wd *wdaDriver) PressButton(devBtn DeviceButton) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) (err error) {
|
||||
// [[FBRoute POST:@"/wda/performIoHidEvent"] respondWithTarget:self action:@selector(handlePeformIOHIDEvent:)]
|
||||
if len(duration) == 0 || duration[0] <= 0 {
|
||||
duration = []float64{0.005}
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"page": pageID,
|
||||
"usage": usageID,
|
||||
"duration": duration[0],
|
||||
}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/performIoHidEvent")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) StartCamera() (err error) {
|
||||
// start camera, alias for app_launch com.apple.camera
|
||||
return wd.AppLaunch("com.apple.camera")
|
||||
@@ -585,34 +520,6 @@ func (wd *wdaDriver) StopCamera() (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) ExpectNotification(notifyName string, notifyType NotificationType, second ...int) (err error) {
|
||||
// [[FBRoute POST:@"/wda/expectNotification"] respondWithTarget:self action:@selector(handleExpectNotification:)]
|
||||
if len(second) == 0 {
|
||||
second = []int{60}
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"name": notifyName,
|
||||
"type": notifyType,
|
||||
"timeout": second[0],
|
||||
}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/expectNotification")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) SiriActivate(text string) (err error) {
|
||||
// [[FBRoute POST:@"/wda/siri/activate"] respondWithTarget:self action:@selector(handleActivateSiri:)]
|
||||
data := map[string]interface{}{"text": text}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/siri/activate")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) SiriOpenUrl(url string) (err error) {
|
||||
// [[FBRoute POST:@"/url"] respondWithTarget:self action:@selector(handleOpenURL:)]
|
||||
data := map[string]interface{}{"url": url}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/url")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) Orientation() (orientation Orientation, err error) {
|
||||
// [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)]
|
||||
var rawResp rawResponse
|
||||
@@ -654,74 +561,6 @@ func (wd *wdaDriver) SetRotation(rotation Rotation) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) MatchTouchID(isMatch bool) (err error) {
|
||||
// [FBRoute POST:@"/wda/touch_id"]
|
||||
data := map[string]interface{}{"match": isMatch}
|
||||
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/touch_id")
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) ActiveElement() (element WebElement, err error) {
|
||||
// [[FBRoute GET:@"/element/active"] respondWithTarget:self action:@selector(handleGetActiveElement:)]
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = wd.httpGET("/session", wd.sessionId, "/element/active"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var elementID string
|
||||
if elementID, err = rawResp.valueConvertToElementID(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
element = &wdaElement{parent: wd, id: elementID}
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) FindElement(by BySelector) (element WebElement, err error) {
|
||||
// [[FBRoute POST:@"/element"] respondWithTarget:self action:@selector(handleFindElement:)]
|
||||
using, value := by.getUsingAndValue()
|
||||
data := map[string]interface{}{
|
||||
"using": using,
|
||||
"value": value,
|
||||
}
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = wd.httpPOST(data, "/session", wd.sessionId, "/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: wd, id: elementID}
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) FindElements(by BySelector) (elements []WebElement, err error) {
|
||||
// [[FBRoute POST:@"/elements"] respondWithTarget:self action:@selector(handleFindElements:)]
|
||||
using, value := by.getUsingAndValue()
|
||||
data := map[string]interface{}{
|
||||
"using": using,
|
||||
"value": value,
|
||||
}
|
||||
var rawResp rawResponse
|
||||
if rawResp, err = wd.httpPOST(data, "/session", wd.sessionId, "/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: wd, id: elementIDs[i]}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) Screenshot() (raw *bytes.Buffer, err error) {
|
||||
// [[FBRoute GET:@"/screenshot"] respondWithTarget:self action:@selector(handleGetScreenshot:)]
|
||||
// [[FBRoute GET:@"/screenshot"].withoutSession respondWithTarget:self action:@selector(handleGetScreenshot:)]
|
||||
@@ -838,32 +677,6 @@ func (wd *wdaDriver) WdaShutdown() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error {
|
||||
startTime := time.Now()
|
||||
for {
|
||||
done, err := condition(wd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if done {
|
||||
return nil
|
||||
}
|
||||
|
||||
if elapsed := time.Since(startTime); elapsed > timeout {
|
||||
return fmt.Errorf("timeout after %v", elapsed)
|
||||
}
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) WaitWithTimeout(condition Condition, timeout time.Duration) error {
|
||||
return wd.WaitWithTimeoutAndInterval(condition, timeout, DefaultWaitInterval)
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) Wait(condition Condition) error {
|
||||
return wd.WaitWithTimeoutAndInterval(condition, DefaultWaitTimeout, DefaultWaitInterval)
|
||||
}
|
||||
|
||||
func (wd *wdaDriver) triggerWDALog(data map[string]interface{}) (rawResp []byte, err error) {
|
||||
// [[FBRoute POST:@"/gtf/automation/log"].withoutSession respondWithTarget:self action:@selector(handleAutomationLog:)]
|
||||
return wd.httpPOST(data, "/gtf/automation/log")
|
||||
@@ -907,3 +720,78 @@ func (wd *wdaDriver) StopCaptureLog() (result interface{}, err error) {
|
||||
log.Info().Interface("value", reply.Value).Msg("get WDA log response")
|
||||
return reply.Value, nil
|
||||
}
|
||||
|
||||
type rawResponse []byte
|
||||
|
||||
func (r rawResponse) 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 rawResponse) 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 rawResponse) 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 rawResponse) valueConvertToSessionInfo() (sessionInfo SessionInfo, err error) {
|
||||
reply := new(struct{ Value struct{ SessionInfo } })
|
||||
if err = json.Unmarshal(r, reply); err != nil {
|
||||
return SessionInfo{}, err
|
||||
}
|
||||
sessionInfo = reply.Value.SessionInfo
|
||||
return
|
||||
}
|
||||
|
||||
func (r rawResponse) 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 rawResponse) 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
|
||||
}
|
||||
|
||||
@@ -1,478 +0,0 @@
|
||||
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
|
||||
newData := NewData(data, options...)
|
||||
|
||||
_, err = we.parent.httpPOST(newData, "/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
|
||||
}
|
||||
@@ -5,7 +5,6 @@ package uixt
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@@ -69,23 +68,6 @@ func TestNewUSBDriver(t *testing.T) {
|
||||
// t.Log(driver.IsWdaHealthy())
|
||||
}
|
||||
|
||||
func Test_remoteWD_NewSession(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
// sessionInfo, err := driver.NewSession(nil)
|
||||
sessionInfo, err := driver.NewSession(
|
||||
NewCapabilities().WithAppLaunchOption(
|
||||
NewAppLaunchOption().WithBundleId(bundleId).WithArguments([]string{"-AppleLanguages", "(Russian)"}),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(sessionInfo.SessionId) == 0 {
|
||||
t.Fatal(sessionInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_ActiveSession(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
@@ -219,128 +201,6 @@ func Test_remoteWD_Screen(t *testing.T) {
|
||||
t.Log(screen)
|
||||
}
|
||||
|
||||
func Test_remoteWD_ActiveAppInfo(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
appInfo, err := driver.ActiveAppInfo()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(appInfo.BundleId) == 0 {
|
||||
t.Fatal(appInfo)
|
||||
}
|
||||
t.Log(appInfo)
|
||||
}
|
||||
|
||||
func Test_remoteWD_ActiveAppsList(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
appsList, err := driver.ActiveAppsList()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(appsList) == 0 {
|
||||
t.Fatal(appsList)
|
||||
}
|
||||
t.Log(appsList)
|
||||
}
|
||||
|
||||
func Test_remoteWD_AppState(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
runState, err := driver.AppState(bundleId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(runState)
|
||||
}
|
||||
|
||||
func Test_remoteWD_IsLocked(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
locked, err := driver.IsLocked()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(locked)
|
||||
}
|
||||
|
||||
func Test_remoteWD_Unlock(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.Unlock()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_Lock(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.Lock()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AlertText(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
text, err := driver.AlertText()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = text
|
||||
t.Log(text)
|
||||
}
|
||||
|
||||
func Test_remoteWD_AlertButtons(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
btnLabels, err := driver.AlertButtons()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(btnLabels)
|
||||
}
|
||||
|
||||
func Test_remoteWD_AlertAccept(t *testing.T) {
|
||||
// Test_remoteWD_AppAuthReset(t)
|
||||
// return
|
||||
|
||||
setup(t)
|
||||
|
||||
err := driver.AlertAccept()
|
||||
// err := driver.AlertAccept("")
|
||||
// err := driver.AlertAccept("好")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AlertDismiss(t *testing.T) {
|
||||
// Test_remoteWD_AppAuthReset(t)
|
||||
// return
|
||||
|
||||
setup(t)
|
||||
|
||||
err := driver.AlertDismiss()
|
||||
// err := driver.AlertDismiss("")
|
||||
// err := driver.AlertDismiss("不允许")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AlertSendKeys(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.AlertSendKeys("todo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_Homescreen(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
@@ -367,15 +227,6 @@ func Test_remoteWD_AppLaunch(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AppLaunchUnattached(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.AppLaunchUnattached(bundleId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AppTerminate(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
@@ -385,33 +236,6 @@ func Test_remoteWD_AppTerminate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AppActivate(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.AppActivate(bundleId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AppDeactivate(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.AppDeactivate(2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_AppAuthReset(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.AppAuthReset(ProtectedResourceCamera)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_Tap(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
@@ -450,15 +274,6 @@ func Test_remoteWD_Drag(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_ForceTouch(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.ForceTouch(256, 400, 0.8, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_SetPasteboard(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
@@ -524,147 +339,6 @@ func Test_remoteWD_PressButton(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_SiriActivate(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.SiriActivate("What's the weather like today")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_SiriOpenUrl(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.SiriOpenUrl("Prefs:root=Bluetooth")
|
||||
// err := driver.SiriOpenUrl("Prefs:root=WIFI![]()")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_Orientation(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
orientation, err := driver.Orientation()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if orientation == "" {
|
||||
t.Fatal(orientation)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_SetOrientation(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
var err error
|
||||
err = driver.SetOrientation(OrientationLandscapeLeft)
|
||||
err = driver.SetOrientation(OrientationLandscapeRight)
|
||||
err = driver.SetOrientation(OrientationPortrait)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_Rotation(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
rotation, err := driver.Rotation()
|
||||
if err != nil {
|
||||
t.Fatal()
|
||||
}
|
||||
t.Log(rotation)
|
||||
}
|
||||
|
||||
func Test_remoteWD_SetRotation(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.SetRotation(Rotation{X: 0, Y: 0, Z: 270})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_PerformW3CActions(t *testing.T) {
|
||||
// setup(t)
|
||||
// actions := NewW3CActions().SendKeys("App Store")
|
||||
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
actions := NewW3CActions().FingerAction(
|
||||
NewFingerAction().
|
||||
Move(NewFingerMove().WithXY(-15, -85).WithOrigin(element)).
|
||||
Down().
|
||||
Pause(0.25).
|
||||
Move(NewFingerMove().WithOrigin(element)).
|
||||
Pause(0.25).
|
||||
Up(),
|
||||
NewFingerAction().
|
||||
Move(NewFingerMove().WithXY(15, 85).WithOrigin(element)).
|
||||
Down().
|
||||
Pause(0.25).
|
||||
Move(NewFingerMove().WithOrigin(element)).
|
||||
Pause(0.25).
|
||||
Up(),
|
||||
)
|
||||
err := driver.PerformW3CActions(actions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_PerformAppiumTouchActions(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
actions := NewTouchActions().
|
||||
Press(NewTouchActionPress().WithElement(element).WithXY(100, 150).WithPressure(0.2)).
|
||||
Wait(0.2).
|
||||
MoveTo(NewTouchActionMoveTo().WithXY(300, 150)).
|
||||
Wait(0.2).
|
||||
MoveTo(NewTouchActionMoveTo().WithElement(element)).
|
||||
Wait(0.2).
|
||||
MoveTo(NewTouchActionMoveTo().WithElement(element).WithXY(300, 400)).
|
||||
Release()
|
||||
|
||||
err := driver.PerformAppiumTouchActions(actions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_ActiveElement(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
element, err := driver.ActiveElement()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = element
|
||||
// t.Log(element)
|
||||
}
|
||||
|
||||
func Test_remoteWD_FindElement(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
element, err := driver.FindElement(BySelector{Name: "设置"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = element
|
||||
// t.Log(element)
|
||||
}
|
||||
|
||||
func Test_remoteWD_FindElements(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
elements, err := driver.FindElements(BySelector{ClassName: ElementType{Icon: true}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = elements
|
||||
t.Log(elements)
|
||||
}
|
||||
|
||||
func Test_remoteWD_Screenshot(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
@@ -741,434 +415,3 @@ func Test_remoteWD_AccessibleSource(t *testing.T) {
|
||||
_ = source
|
||||
fmt.Println(source)
|
||||
}
|
||||
|
||||
func Test_remoteWD_Wait(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
var element WebElement
|
||||
var err error
|
||||
|
||||
by := BySelector{Name: "通知"}
|
||||
// driver.AppLaunch()
|
||||
exists := func(d WebDriver) (bool, error) {
|
||||
element, err = d.FindElement(by)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
_ = exists
|
||||
_ = element
|
||||
|
||||
err = driver.AppLaunchUnattached(bundleId)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// element, err = driver.FindElement(by)
|
||||
err = driver.WaitWithTimeoutAndInterval(exists, time.Second*10, time.Millisecond*10)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// t.Log(element.Rect())
|
||||
}
|
||||
|
||||
func Test_remoteWD_Location(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
location, err := driver.Location()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(location)
|
||||
}
|
||||
|
||||
func Test_remoteWD_KeyboardDismiss(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.KeyboardDismiss()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWD_ExpectNotification(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
// bundleId = "com.apple.shortcuts"
|
||||
// err := driver.ExpectNotification("shortcuts", NotificationTypePlain, 10)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
}
|
||||
|
||||
func Test_remoteWD_IOHIDEvent(t *testing.T) {
|
||||
setup(t)
|
||||
|
||||
err := driver.IOHIDEvent(EventPageIDConsumer, EventUsageIDCsmrVolumeDown)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setupElement(t *testing.T, by BySelector) WebElement {
|
||||
setup(t)
|
||||
element, err := driver.FindElement(by)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return element
|
||||
}
|
||||
|
||||
func Test_remoteWE_Click(t *testing.T) {
|
||||
element := setupElement(t, BySelector{LinkText: NewElementAttribute().WithLabel("设置")})
|
||||
|
||||
err := element.Click()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_SendKeys(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{SearchField: true}})
|
||||
|
||||
err := element.SendKeys("App Store")
|
||||
// err := element.SendKeys("App Store", 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_Clear(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{SearchField: true}})
|
||||
|
||||
err := element.Clear()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_Tap(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
err := element.Tap(10, 20)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_DoubleTap(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
err := element.DoubleTap()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_TouchAndHold(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
err := element.TouchAndHold(-1)
|
||||
// err := element.TouchAndHold(5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_TwoFingerTap(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
err := element.TwoFingerTap()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_TapWithNumberOfTaps(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
err := element.TapWithNumberOfTaps(3, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_ForceTouch(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
// err := element.ForceTouch(1, -1)
|
||||
err := element.ForceTouchFloat(10, 20, 1, -1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_Drag(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
// err := element.Drag(10, 20, 10, 300, -1)
|
||||
err := element.Swipe(10, 20, 10, 300)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_SwipeDirection(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
// err := element.SwipeDirection(DirectionUp, -1)
|
||||
err := element.SwipeDirection(DirectionDown, 120)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_Pinch(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
// zoom in
|
||||
// err := element.Pinch(2,10)
|
||||
// zoom out
|
||||
err := element.Pinch(0.9, -4.5)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_PinchToZoomOutByW3CAction(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
err := element.PinchToZoomOutByW3CAction(15)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_Rotate(t *testing.T) {
|
||||
element := setupElement(t, BySelector{Name: "touchableView"})
|
||||
|
||||
// 90 CW
|
||||
// err := element.Rotate(math.Pi / 2)
|
||||
// 180 CCW
|
||||
err := element.Rotate(math.Pi * -2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_PickerWheelSelect(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{PickerWheel: true}})
|
||||
|
||||
err := element.PickerWheelSelect(PickerWheelOrderNext, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = element.PickerWheelSelect(PickerWheelOrderPrevious)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_scroll(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
|
||||
|
||||
var err error
|
||||
// err = element.ScrollElementByName("电池")
|
||||
// err = element.ScrollElementByPredicate("type == 'XCUIElementTypeCell' AND name LIKE 'Safari*'")
|
||||
err = element.ScrollDirection(DirectionDown, 0.8)
|
||||
|
||||
// element, err = driver.FindElement(BySelector{PartialLinkText: NewElementAttribute().WithLabel("Safari")})
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// err = element.ScrollToVisible()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_FindElement(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
|
||||
|
||||
var err error
|
||||
element, err = element.FindElement(BySelector{PartialLinkText: NewElementAttribute().WithLabel("Safari")})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = element.Click()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_FindElements(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
|
||||
|
||||
elements, err := element.FindElements(BySelector{ClassName: ElementType{Cell: true}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = elements[0].Click()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_FindVisibleCells(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
|
||||
|
||||
cells, err := element.FindVisibleCells()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = cells[0].Click()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_remoteWE_Rect(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
|
||||
rect, err := element.Rect()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
location, err := element.Location()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
size, err := element.Size()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_, _, _ = rect, location, size
|
||||
t.Log(rect, location, size)
|
||||
}
|
||||
|
||||
func Test_remoteWE_Text(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
|
||||
text, err := element.Text()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = text
|
||||
// t.Log(text)
|
||||
}
|
||||
|
||||
func Test_remoteWE_Type(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
|
||||
elemType, err := element.Type()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = elemType
|
||||
// t.Log(elemType)
|
||||
}
|
||||
|
||||
func Test_remoteWE_IsEnabled(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
|
||||
enabled, err := element.IsEnabled()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = enabled
|
||||
// t.Log(enabled)
|
||||
}
|
||||
|
||||
func Test_remoteWE_IsDisplayed(t *testing.T) {
|
||||
element := setupElement(t, BySelector{PartialLinkText: NewElementAttribute().WithLabel("Safari")})
|
||||
|
||||
displayed, err := element.IsDisplayed()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = displayed
|
||||
// t.Log(displayed)
|
||||
}
|
||||
|
||||
func Test_remoteWE_IsSelected(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
// element := setupElement(t, BySelector{Name: "添加到主屏幕"})
|
||||
// element := setupElement(t, BySelector{Name: "仅App资源库"})
|
||||
|
||||
selected, err := element.IsSelected()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = selected
|
||||
// t.Log(selected)
|
||||
}
|
||||
|
||||
func Test_remoteWE_IsAccessible(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
|
||||
accessible, err := element.IsAccessible()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = accessible
|
||||
// t.Log(accessible)
|
||||
}
|
||||
|
||||
func Test_remoteWE_IsAccessibilityContainer(t *testing.T) {
|
||||
// element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
|
||||
|
||||
isAccessibilityContainer, err := element.IsAccessibilityContainer()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = isAccessibilityContainer
|
||||
// t.Log(isAccessibilityContainer)
|
||||
}
|
||||
|
||||
func Test_remoteWE_GetAttribute(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{StaticText: true}})
|
||||
|
||||
value, err := element.GetAttribute(NewElementAttribute().WithValue(""))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = value
|
||||
// t.Log(value)
|
||||
}
|
||||
|
||||
func Test_remoteWE_Screenshot(t *testing.T) {
|
||||
element := setupElement(t, BySelector{ClassName: ElementType{TextView: true}})
|
||||
|
||||
screenshot, err := element.Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = screenshot
|
||||
|
||||
// img, format, err := image.Decode(screenshot)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// userHomeDir, _ := os.UserHomeDir()
|
||||
// file, err := os.Create(userHomeDir + "/Desktop/e1." + format)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer func() { _ = file.Close() }()
|
||||
// switch format {
|
||||
// case "png":
|
||||
// err = png.Encode(file, img)
|
||||
// case "jpeg":
|
||||
// err = jpeg.Encode(file, img, nil)
|
||||
// }
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// t.Log(file.Name())
|
||||
}
|
||||
|
||||
@@ -97,12 +97,6 @@ func (dExt *DriverExt) Tap(param string, options ...DataOption) error {
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, options ...DataOption) (err error) {
|
||||
// click on element, find by name attribute
|
||||
ele, err := dExt.FindUIElement(param)
|
||||
if err == nil {
|
||||
return ele.Click()
|
||||
}
|
||||
|
||||
dataOptions := NewDataOptions(options...)
|
||||
|
||||
x, y, width, height, err := dExt.FindUIRectInUIKit(param, options...)
|
||||
@@ -132,12 +126,6 @@ func (dExt *DriverExt) DoubleTap(param string) (err error) {
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64) (err error) {
|
||||
// click on element, find by name attribute
|
||||
ele, err := dExt.FindUIElement(param)
|
||||
if err == nil {
|
||||
return ele.DoubleTap()
|
||||
}
|
||||
|
||||
var x, y, width, height float64
|
||||
if x, y, width, height, err = dExt.FindUIRectInUIKit(param); err != nil {
|
||||
return err
|
||||
@@ -145,24 +133,3 @@ func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64) (
|
||||
|
||||
return dExt.Driver.DoubleTapFloat(x+width*xOffset, y+height*yOffset)
|
||||
}
|
||||
|
||||
// TapWithNumber sends one or more taps
|
||||
func (dExt *DriverExt) TapWithNumber(param string, numberOfTaps int) (err error) {
|
||||
return dExt.TapWithNumberOffset(param, numberOfTaps, 0.5, 0.5)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) TapWithNumberOffset(param string, numberOfTaps int, xOffset, yOffset float64) (err error) {
|
||||
if numberOfTaps <= 0 {
|
||||
numberOfTaps = 1
|
||||
}
|
||||
var x, y, width, height float64
|
||||
if x, y, width, height, err = dExt.FindUIRectInUIKit(param); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
x = x + width*xOffset
|
||||
y = y + height*yOffset
|
||||
|
||||
touchActions := NewTouchActions().Tap(NewTouchActionTap().WithXYFloat(x, y).WithCount(numberOfTaps))
|
||||
return dExt.PerformTouchActions(touchActions)
|
||||
}
|
||||
|
||||
@@ -12,17 +12,14 @@ func init() {
|
||||
iosDevice, _ = NewIOSDevice()
|
||||
}
|
||||
|
||||
func TestDriverExt_TapWithNumber(t *testing.T) {
|
||||
driverExt, err := iosDevice.NewDriver(nil)
|
||||
checkErr(t, err)
|
||||
|
||||
pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/flag7.png"
|
||||
|
||||
err = driverExt.TapWithNumber(pathSearch, 3)
|
||||
checkErr(t, err)
|
||||
|
||||
err = driverExt.TapWithNumberOffset(pathSearch, 3, 0.5, 0.75)
|
||||
checkErr(t, err)
|
||||
func checkErr(t *testing.T, err error, msg ...string) {
|
||||
if err != nil {
|
||||
if len(msg) == 0 {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
t.Fatal(msg, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDriverExt_TapXY(t *testing.T) {
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package uixt
|
||||
|
||||
func (dExt *DriverExt) ForceTouch(pathname string, pressure float64, duration ...float64) (err error) {
|
||||
return dExt.ForceTouchOffset(pathname, pressure, 0.5, 0.5, duration...)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) ForceTouchOffset(pathname string, pressure, xOffset, yOffset float64, duration ...float64) (err error) {
|
||||
if len(duration) == 0 {
|
||||
duration = []float64{1.0}
|
||||
}
|
||||
var x, y, width, height float64
|
||||
if x, y, width, height, err = dExt.FindUIRectInUIKit(pathname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dExt.Driver.ForceTouchFloat(x+width*xOffset, y+height*yOffset, pressure, duration[0])
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) TouchAndHold(pathname string, duration ...float64) (err error) {
|
||||
return dExt.TouchAndHoldOffset(pathname, 0.5, 0.5, duration...)
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) TouchAndHoldOffset(pathname string, xOffset, yOffset float64, duration ...float64) (err error) {
|
||||
if len(duration) == 0 {
|
||||
duration = []float64{1.0}
|
||||
}
|
||||
var x, y, width, height float64
|
||||
if x, y, width, height, err = dExt.FindUIRectInUIKit(pathname); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return dExt.Driver.TouchAndHoldFloat(x+width*xOffset, y+height*yOffset, duration[0])
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
//go:build localtest
|
||||
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDriverExt_ForceTouch(t *testing.T) {
|
||||
driverExt, err := iosDevice.NewDriver(nil)
|
||||
checkErr(t, err)
|
||||
|
||||
pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/IMG_ft.png"
|
||||
|
||||
err = driverExt.ForceTouch(pathSearch, 0.5, 3)
|
||||
checkErr(t, err)
|
||||
|
||||
// err = driverExt.ForceTouchOffset(pathSearch, 0.5, 0.1, 0.9)
|
||||
// checkErr(t, err)
|
||||
|
||||
// err = driverExt.ForceTouchOffset(pathSearch, 0.2, 1.1, -1)
|
||||
// checkErr(t, err)
|
||||
}
|
||||
|
||||
func TestDriverExt_TouchAndHold(t *testing.T) {
|
||||
driverExt, err := iosDevice.NewDriver(nil)
|
||||
checkErr(t, err)
|
||||
|
||||
pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/IMG_ft.png"
|
||||
|
||||
// err = driverExt.TouchAndHold(pathSearch)
|
||||
// checkErr(t, err)
|
||||
|
||||
// err = driverExt.TouchAndHold(pathSearch, 3)
|
||||
// checkErr(t, err)
|
||||
|
||||
err = driverExt.TouchAndHoldOffset(pathSearch, 0.8, 0.1)
|
||||
checkErr(t, err)
|
||||
}
|
||||
@@ -428,7 +428,7 @@ func (r *CaseRunner) parseConfig() error {
|
||||
}
|
||||
device, err := uixt.NewAndroidDevice(uixt.GetAndroidDeviceOptions(androidDeviceConfig)...)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "init iOS device failed")
|
||||
return errors.Wrap(err, "init Android device failed")
|
||||
}
|
||||
client, err := device.NewDriver(nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -50,14 +50,6 @@ func (s *StepMobile) AppLaunch(bundleId string) *StepMobile {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) AppLaunchUnattached(bundleId string) *StepMobile {
|
||||
s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
|
||||
Method: uixt.AppLaunchUnattached,
|
||||
Params: bundleId,
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *StepMobile) AppTerminate(bundleId string) *StepMobile {
|
||||
s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
|
||||
Method: uixt.AppTerminate,
|
||||
|
||||
@@ -59,7 +59,7 @@ func TestIOSAppLaunch(t *testing.T) {
|
||||
NewStep("终止今日头条").
|
||||
IOS().AppTerminate("com.ss.iphone.article.News"),
|
||||
NewStep("启动今日头条").
|
||||
IOS().AppLaunchUnattached("com.ss.iphone.article.News"),
|
||||
IOS().AppLaunch("com.ss.iphone.article.News"),
|
||||
},
|
||||
}
|
||||
err := NewRunner(t).Run(testCase)
|
||||
|
||||
Reference in New Issue
Block a user