From e03e8832a9a8e4a918b92a8b225d438c7fe4929d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=99=E6=B3=93=E9=93=AE?= Date: Mon, 29 Jul 2024 15:51:05 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81shoots=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE,=20app=5Fprocess=E8=8E=B7=E5=8F=96=E5=89=8D=E5=8F=B0?= =?UTF-8?q?=E5=BA=94=E7=94=A8=EF=BC=8C=20=E4=BF=AE=E5=A4=8D=E9=83=A8?= =?UTF-8?q?=E5=88=86bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hrp/internal/version/VERSION | 2 +- hrp/pkg/server/exception.go | 2 +- hrp/pkg/server/model.go | 8 + hrp/pkg/server/server.go | 62 +++++++- hrp/pkg/uixt/android_adb_driver.go | 35 +++-- hrp/pkg/uixt/android_device.go | 28 +++- hrp/pkg/uixt/android_shoots_driver.go | 164 +++++++++++++++++++-- hrp/pkg/uixt/android_shoots_driver_test.go | 36 +++-- hrp/pkg/uixt/android_test.go | 7 +- hrp/pkg/uixt/interface.go | 4 + hrp/pkg/uixt/ios_driver.go | 15 ++ 11 files changed, 310 insertions(+), 53 deletions(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 2707569b..a67821a0 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.6.0 +v4.6.2 \ No newline at end of file diff --git a/hrp/pkg/server/exception.go b/hrp/pkg/server/exception.go index dbf2dc3e..26cfadda 100644 --- a/hrp/pkg/server/exception.go +++ b/hrp/pkg/server/exception.go @@ -3,7 +3,7 @@ package server // 常见的错误代码和消息 const ( InternalServerErrorCode = 100001 - InternalServerErrorMsg = "Invalid Server Error" + InternalServerErrorMsg = "Internal Server Error" InvalidParamErrorCode = 100002 InvalidParamErrorMsg = "Invalid %s Param" diff --git a/hrp/pkg/server/model.go b/hrp/pkg/server/model.go index b6ac0b12..c499c214 100644 --- a/hrp/pkg/server/model.go +++ b/hrp/pkg/server/model.go @@ -29,6 +29,10 @@ type KeycodeRequest struct { Keycode int `json:"keycode"` } +type AppClearRequest struct { + PackageName string `json:"packageName"` +} + type AppLaunchRequest struct { PackageName string `json:"packageName"` } @@ -42,3 +46,7 @@ type LoginRequest struct { PhoneNumber string `json:"phoneNumber"` Captcha string `json:"captcha"` } + +type LogoutRequest struct { + PackageName string `json:"packageName"` +} diff --git a/hrp/pkg/server/server.go b/hrp/pkg/server/server.go index fca459d9..d53512d3 100644 --- a/hrp/pkg/server/server.go +++ b/hrp/pkg/server/server.go @@ -24,12 +24,14 @@ func NewServer(port int) error { router.POST("/api/v1/:platform/:serial/key/home", parseDeviceInfo(), homeHandler) router.POST("/api/v1/:platform/:serial/key", parseDeviceInfo(), keycodeHandler) router.GET("/api/v1/:platform/:serial/app/foreground", parseDeviceInfo(), foregroundAppHandler) + router.POST("/api/v1/:platform/:serial/app/clear", parseDeviceInfo(), clearAppHandler) router.POST("/api/v1/:platform/:serial/app/launch", parseDeviceInfo(), launchAppHandler) router.POST("/api/v1/:platform/:serial/app/terminal", parseDeviceInfo(), terminalAppHandler) router.GET("/api/v1/:platform/:serial/screenshot", parseDeviceInfo(), screenshotHandler) router.GET("/api/v1/:platform/:serial/shoots/source", parseDeviceInfo(), sourceHandler) router.GET("/api/v1/:platform/:serial/adb/source", parseDeviceInfo(), adbSourceHandler) router.POST("/api/v1/:platform/:serial/shoots/login", parseDeviceInfo(), loginHandler) + router.POST("/api/v1/:platform/:serial/shoots/logout", parseDeviceInfo(), logoutHandler) err := router.Run(fmt.Sprintf("127.0.0.1:%d", port)) if err != nil { @@ -301,6 +303,35 @@ func foregroundAppHandler(c *gin.Context) { return } +func clearAppHandler(c *gin.Context) { + var appClearReq AppClearRequest + if err := c.ShouldBindJSON(&appClearReq); err != nil { + log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName())) + c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")}) + c.Abort() + return + } + + driverObj, exists := c.Get("driver") + if !exists { + log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName())) + c.JSON(http.StatusBadRequest, HttpResponse{Result: false, ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")}) + c.Abort() + return + } + + dExt := driverObj.(*uixt.DriverExt) + err := dExt.Driver.Clear(appClearReq.PackageName) + if err != nil { + log.Err(err).Msg(fmt.Sprintf("[%s]: failed to unlick screen", c.HandlerName())) + c.JSON(http.StatusInternalServerError, HttpResponse{Result: false, ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg}) + c.Abort() + return + } + c.JSON(http.StatusOK, HttpResponse{Result: true}) + return +} + func launchAppHandler(c *gin.Context) { var appLaunchReq AppLaunchRequest if err := c.ShouldBindJSON(&appLaunchReq); err != nil { @@ -455,7 +486,36 @@ func loginHandler(c *gin.Context) { dExt := driverObj.(*uixt.DriverExt) err := dExt.Driver.LoginNoneUI(loginReq.PackageName, loginReq.PhoneNumber, loginReq.Captcha) if err != nil { - log.Err(err).Msg(fmt.Sprintf("[%s]: failed to get foreground app", c.HandlerName())) + log.Err(err).Msg(fmt.Sprintf("[%s]: failed to login", c.HandlerName())) + c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg}) + c.Abort() + return + } + c.JSON(http.StatusOK, HttpResponse{Result: true}) + return +} + +func logoutHandler(c *gin.Context) { + var logoutReq LogoutRequest + if err := c.ShouldBindJSON(&logoutReq); err != nil { + log.Err(err).Msg(fmt.Sprintf("[%s]: Invalid Request", c.HandlerName())) + c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "request")}) + c.Abort() + return + } + + driverObj, exists := c.Get("driver") + if !exists { + log.Error().Msg(fmt.Sprintf("[%s]: Driver Not exsit", c.HandlerName())) + c.JSON(http.StatusBadRequest, HttpResponse{Result: "", ErrorCode: InvalidParamErrorCode, ErrorMsg: fmt.Sprintf(InvalidParamErrorMsg, "driver")}) + c.Abort() + return + } + + dExt := driverObj.(*uixt.DriverExt) + err := dExt.Driver.LogoutNoneUI(logoutReq.PackageName) + if err != nil { + log.Err(err).Msg(fmt.Sprintf("[%s]: failed to login", c.HandlerName())) c.JSON(http.StatusInternalServerError, HttpResponse{Result: "", ErrorCode: InternalServerErrorCode, ErrorMsg: InternalServerErrorMsg}) c.Abort() return diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 148749fe..2adf325c 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -475,6 +475,15 @@ func (ad *adbDriver) Input(text string, options ...ActionOption) (err error) { return ad.SendKeys(text, options...) } +func (ad *adbDriver) Clear(packageName string) error { + if _, err := ad.adbClient.RunShellCommand("pm", "clear", packageName); err != nil { + log.Error().Str("packageName", packageName).Err(err).Msg("failed to clear package cache") + return err + } + + return nil +} + func (ad *adbDriver) PressButton(devBtn DeviceButton) (err error) { err = errDriverNotImplemented return @@ -520,6 +529,10 @@ func (ad *adbDriver) LoginNoneUI(packageName, phoneNumber string, captcha string return errDriverNotImplemented } +func (ad *adbDriver) LogoutNoneUI(packageName string) error { + return errDriverNotImplemented +} + func (ad *adbDriver) sourceTree(srcOpt ...SourceOption) (sourceTree *Hierarchy, err error) { source, err := ad.Source() if err != nil { @@ -715,20 +728,6 @@ func (ad *adbDriver) GetForegroundApp() (app AppInfo, err error) { return } -func (ad *adbDriver) GetFocusedPackage() (packageName string, err error) { - res, err := ad.adbClient.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "-E", "'mResumedActivity'") - if err != nil { - return "", err - } - match := regexp.MustCompile(`mResumedActivity:.*? (\S+)/`).FindStringSubmatch(res) - if len(match) > 1 { - packageName = match[1] - return - } - log.Error().Str("dumpsys", res).Msg("failed to get focused package") - return "", fmt.Errorf("failed to get focused package") -} - func (ad *adbDriver) SetIme(imeRegx string) error { imeList := ad.ListIme() ime := "" @@ -754,13 +753,13 @@ func (ad *adbDriver) SetIme(imeRegx string) error { time.Sleep(1 * time.Second) pid, _ := ad.adbClient.RunShellCommand("pidof", packageName) if strings.TrimSpace(pid) == "" { - focusedPackage, err := ad.GetFocusedPackage() + appInfo, err := ad.GetForegroundApp() _ = ad.AppLaunch(packageName) if err == nil && packageName != UnicodeImePackageName { time.Sleep(10 * time.Second) - currentPackage, err := ad.GetFocusedPackage() - log.Info().Str("beforeFocusedPackage", focusedPackage).Str("afterFocusedPackage", currentPackage).Msg("") - if err == nil && currentPackage != focusedPackage { + nextAppInfo, err := ad.GetForegroundApp() + log.Info().Str("beforeFocusedPackage", appInfo.PackageName).Str("afterFocusedPackage", nextAppInfo.PackageName).Msg("") + if err == nil && nextAppInfo.PackageName != appInfo.PackageName { _ = ad.PressKeyCodes(KCBack, KMEmpty) } } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index a05bfb14..217e9bcd 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -21,10 +21,11 @@ import ( ) var ( - AdbServerHost = "localhost" - AdbServerPort = gadb.AdbServerPort // 5037 - UIA2ServerHost = "localhost" - UIA2ServerPort = 6790 + AdbServerHost = "localhost" + AdbServerPort = gadb.AdbServerPort // 5037 + UIA2ServerHost = "localhost" + UIA2ServerPort = 6790 + DouyinServerPort = 32316 ) //go:embed eval_tool @@ -248,16 +249,27 @@ func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver WebDri } func (dev *AndroidDevice) NewShootsDriver(capabilities Capabilities) (driver *ShootsAndroidDriver, err error) { - localPort, err := dev.d.Forward(ShootsSocketName) + socketLocalPort, err := dev.d.Forward(ShootsSocketName) if err != nil { return nil, errors.Wrap(code.AndroidDeviceConnectionError, fmt.Sprintf("forward port %d->%s failed: %v", - localPort, ShootsSocketName, err)) + socketLocalPort, ShootsSocketName, err)) } - shootsDriver, err := newShootsAndroidDriver(fmt.Sprintf("127.0.0.1:%d", localPort)) + serverLocalPort, err := dev.d.Forward(DouyinServerPort) if err != nil { - _ = dev.d.ForwardKill(localPort) + return nil, errors.Wrap(code.AndroidDeviceConnectionError, + fmt.Sprintf("forward port %d->%d failed: %v", + serverLocalPort, DouyinServerPort, err)) + } + + rawURL := fmt.Sprintf("http://%s%d:%d", + forwardToPrefix, serverLocalPort, DouyinServerPort) + + shootsDriver, err := newShootsAndroidDriver(fmt.Sprintf("127.0.0.1:%d", socketLocalPort), rawURL) + if err != nil { + _ = dev.d.ForwardKill(socketLocalPort) + _ = dev.d.ForwardKill(serverLocalPort) return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error()) } shootsDriver.adbClient = dev.d diff --git a/hrp/pkg/uixt/android_shoots_driver.go b/hrp/pkg/uixt/android_shoots_driver.go index cfe3bb26..02b07c87 100644 --- a/hrp/pkg/uixt/android_shoots_driver.go +++ b/hrp/pkg/uixt/android_shoots_driver.go @@ -7,6 +7,10 @@ import ( "fmt" "io" "net" + "net/http" + "net/url" + "strconv" + "strings" "time" "github.com/rs/zerolog/log" @@ -25,7 +29,7 @@ const ShootsSocketName = "com.bytest.device" // newShootsAndroidDriver // 创建shoots Driver address为forward后的端口格式127.0.0.1:${port} -func newShootsAndroidDriver(address string, readTimeout ...time.Duration) (*ShootsAndroidDriver, error) { +func newShootsAndroidDriver(address string, urlPrefix string, readTimeout ...time.Duration) (*ShootsAndroidDriver, error) { timeout := 10 * time.Second if len(readTimeout) > 0 { timeout = readTimeout[0] @@ -37,10 +41,59 @@ func newShootsAndroidDriver(address string, readTimeout ...time.Duration) (*Shoo return nil, err } - return &ShootsAndroidDriver{ + driver := &ShootsAndroidDriver{ socket: conn, timeout: timeout, - }, nil + } + + if driver.urlPrefix, err = url.Parse(urlPrefix); err != nil { + return nil, err + } + + return driver, nil +} + +func (sad *ShootsAndroidDriver) httpGET(pathElem ...string) (rawResp rawResponse, err error) { + var localPort int + { + tmpURL, _ := url.Parse(sad.urlPrefix.String()) + hostname := tmpURL.Hostname() + if strings.HasPrefix(hostname, forwardToPrefix) { + localPort, _ = strconv.Atoi(strings.TrimPrefix(hostname, forwardToPrefix)) + } + } + + conn, err := net.Dial("tcp", fmt.Sprintf(":%d", localPort)) + if err != nil { + return nil, fmt.Errorf("adb forward: %w", err) + } + sad.client = convertToHTTPClient(conn) + return sad.httpRequest(http.MethodGet, sad.concatURL(nil, pathElem...), nil) +} + +func (sad *ShootsAndroidDriver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) { + var localPort int + { + tmpURL, _ := url.Parse(sad.urlPrefix.String()) + hostname := tmpURL.Hostname() + if strings.HasPrefix(hostname, forwardToPrefix) { + localPort, _ = strconv.Atoi(strings.TrimPrefix(hostname, forwardToPrefix)) + } + } + + conn, err := net.Dial("tcp", fmt.Sprintf(":%d", localPort)) + if err != nil { + return nil, fmt.Errorf("adb forward: %w", err) + } + sad.client = convertToHTTPClient(conn) + + var bsJSON []byte = nil + if data != nil { + if bsJSON, err = json.Marshal(data); err != nil { + return nil, err + } + } + return sad.httpRequest(http.MethodPost, sad.concatURL(nil, pathElem...), bsJSON) } func (sad *ShootsAndroidDriver) NewSession(capabilities Capabilities) (SessionInfo, error) { @@ -197,9 +250,9 @@ func (sad *ShootsAndroidDriver) Source(srcOpt ...SourceOption) (source string, e return res.(string), nil } -func (sad *ShootsAndroidDriver) LoginNoneUI(packageName, phoneNumber string, captcha string) error { +func (sad *ShootsAndroidDriver) LoginNoneUIBak(packageName, phoneNumber, captcha string) error { _, err := sad.adbClient.RunShellCommand("am", "broadcast", "-a", fmt.Sprintf("%s.util.crony.action_login", packageName), "-e", "phone", phoneNumber, "-e", "code", captcha) - time.Sleep(5 * time.Second) + time.Sleep(10 * time.Second) login, err := sad.isLogin(packageName) if err != nil || !login { log.Err(err).Msg("failed to login") @@ -208,7 +261,87 @@ func (sad *ShootsAndroidDriver) LoginNoneUI(packageName, phoneNumber string, cap return err } +func (sad *ShootsAndroidDriver) LoginNoneUI(packageName, phoneNumber, captcha string) error { + params := map[string]interface{}{ + "phone": phoneNumber, + "code": captcha, + } + resp, err := sad.httpPOST(params, "/host", "/login", "account") + res, err := resp.valueConvertToJsonObject() + if err != nil { + return err + } + log.Info().Msgf("%v", res) + if res["isSuccess"] != true { + err = fmt.Errorf("falied to logout %s", res["data"]) + log.Err(err).Msgf("%v", res) + return err + } + time.Sleep(10 * time.Second) + login, err := sad.isLogin(packageName) + if err != nil { + return err + } + if !login { + return fmt.Errorf("failed to login") + } + return nil +} + +func (sad *ShootsAndroidDriver) LogoutNoneUI(packageName string) error { + resp, err := sad.httpGET("/host", "/logout") + res, err := resp.valueConvertToJsonObject() + if err != nil { + return err + } + log.Info().Msgf("%v", res) + if res["isSuccess"] != true { + err = fmt.Errorf("falied to logout %s", res["data"]) + log.Err(err).Msgf("%v", res) + return err + } + fmt.Printf("%v", resp) + if err != nil { + return err + } + return nil +} + +func (sad *ShootsAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string, captcha string) error { + params := map[string]interface{}{ + "ClassName": "qe.python.test.LoginUtil", + "Method": "loginSync", + "RetType": "", + "Args": []string{phoneNumber, captcha}, + } + res, err := sad.sendCommand(packageName, "CallStaticMethod", params) + if err != nil { + return err + } + log.Info().Msg(res.(string)) + return nil +} + func (sad *ShootsAndroidDriver) isLogin(packageName string) (login bool, err error) { + resp, err := sad.httpGET("/host", "/login", "/check") + res, err := resp.valueConvertToJsonObject() + if err != nil { + return false, err + } + log.Info().Msgf("%v", res) + if res["isSuccess"] != true { + err = fmt.Errorf("falied to get is login %s", res["data"]) + log.Err(err).Msgf("%v", res) + return false, err + } + fmt.Printf("%v", resp) + if err != nil { + return false, err + } + return true, nil +} + +func (sad *ShootsAndroidDriver) isLoginBak(packageName string) (login bool, err error) { params := map[string]interface{}{ "ClassName": "com.ss.android.ugc.aweme.account.AccountProxyService", "Method": "userService", @@ -222,10 +355,11 @@ func (sad *ShootsAndroidDriver) isLogin(packageName string) (login bool, err err } params = map[string]interface{}{ - "Method": "isLogin", - "RetType": "", - "Args": []string{}, - "ObjectId": int(id.(float64)), + "ClassName": "com.ss.android.ugc.aweme.account.service.IAccountUserService", + "Method": "isLogin", + "RetType": "", + "Args": []string{}, + "ObjectId": int(id.(float64)), } loginObj, err := sad.sendCommand(packageName, "CallMethod", params) if err != nil { @@ -233,3 +367,15 @@ func (sad *ShootsAndroidDriver) isLogin(packageName string) (login bool, err err } return loginObj.(bool), nil } + +func calculatePortNumber(packageName string) int { + asciiSum := 0 + for _, char := range packageName { + asciiSum += int(char) + } + + portRange := 65536 - 30000 + calculatedPortNumber := (asciiSum % portRange) + 30000 + + return calculatedPortNumber +} diff --git a/hrp/pkg/uixt/android_shoots_driver_test.go b/hrp/pkg/uixt/android_shoots_driver_test.go index 5f320497..99d1db7b 100644 --- a/hrp/pkg/uixt/android_shoots_driver_test.go +++ b/hrp/pkg/uixt/android_shoots_driver_test.go @@ -2,19 +2,19 @@ package uixt import "testing" -var driver *ShootsAndroidDriver +var shootsDriver *ShootsAndroidDriver -func setupAndroid(t *testing.T) { +func setupShootsDriver(t *testing.T) { device, err := NewAndroidDevice() checkErr(t, err) device.SHOOTS = true - driver, err = device.NewShootsDriver(Capabilities{}) + shootsDriver, err = device.NewShootsDriver(Capabilities{}) checkErr(t, err) } func TestHello(t *testing.T) { - setupAndroid(t) - status, err := driver.Status() + setupShootsDriver(t) + status, err := shootsDriver.Status() if err != nil { t.Fatal(err) } @@ -22,19 +22,35 @@ func TestHello(t *testing.T) { } func TestSource(t *testing.T) { - setupAndroid(t) - source, err := driver.Source() + setupShootsDriver(t) + source, err := shootsDriver.Source() if err != nil { t.Fatal(err) } t.Log(source) } -func TestLogin(t *testing.T) { - setupAndroid(t) - res, err := driver.isLogin("com.ss.android.ugc.aweme") +func TestIsLogin(t *testing.T) { + setupShootsDriver(t) + res, err := shootsDriver.isLogin("com.ss.android.ugc.aweme") if err != nil { t.Fatal(err) } t.Log(res) } + +func TestLogin(t *testing.T) { + setupShootsDriver(t) + err := shootsDriver.LoginNoneUI("com.ss.android.ugc.aweme", "12342316231", "8517") + if err != nil { + t.Fatal(err) + } +} + +func TestLogout(t *testing.T) { + setupShootsDriver(t) + err := shootsDriver.LogoutNoneUI("com.ss.android.ugc.aweme") + if err != nil { + t.Fatal(err) + } +} diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index 591893d0..0aeb0d51 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -125,12 +125,9 @@ func TestDriver_DeviceSize(t *testing.T) { } func TestDriver_Source(t *testing.T) { - driver, err := NewUIADriver(nil, uiaServerURL) - if err != nil { - t.Fatal(err) - } + setupAndroid(t) - source, err := driver.Source() + source, err := driverExt.Driver.Source() if err != nil { t.Fatal(err) } diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index 0817466a..4920cc73 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -610,6 +610,8 @@ type WebDriver interface { // Input works like SendKeys Input(text string, options ...ActionOption) error + Clear(packageName string) error + // PressButton Presses the corresponding hardware button on the device PressButton(devBtn DeviceButton) error @@ -625,6 +627,8 @@ type WebDriver interface { LoginNoneUI(packageName, phoneNumber string, captcha string) error + LogoutNoneUI(packageName string) error + TapByText(text string, options ...ActionOption) error TapByTexts(actions ...TapTextAction) error diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index a725b87c..b94a8eff 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -615,6 +615,10 @@ func (wd *wdaDriver) Input(text string, options ...ActionOption) (err error) { return wd.SendKeys(text, options...) } +func (wd *wdaDriver) Clear(packageName string) error { + return errDriverNotImplemented +} + // PressBack simulates a short press on the BACK button. func (wd *wdaDriver) PressBack(options ...ActionOption) (err error) { actionOptions := NewActionOptions(options...) @@ -663,6 +667,10 @@ func (wd *wdaDriver) LoginNoneUI(packageName, phoneNumber string, captcha string return errDriverNotImplemented } +func (wd *wdaDriver) LogoutNoneUI(packageName string) error { + return errDriverNotImplemented +} + func (wd *wdaDriver) StartCamera() (err error) { // start camera, alias for app_launch com.apple.camera return wd.AppLaunch("com.apple.camera") @@ -958,6 +966,13 @@ func (r rawResponse) valueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, return } +func (r rawResponse) valueConvertToJsonObject() (obj map[string]interface{}, err error) { + if err = json.Unmarshal(r, &obj); err != nil { + return nil, err + } + return +} + func (r rawResponse) valueDecodeAsBase64() (raw *bytes.Buffer, err error) { str, err := r.valueConvertToString() if err != nil {