mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-07 07:21:23 +08:00
refactor: move uixt ext to EvalTools
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/httprunner/httprunner/v5/server"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
server_ext "github.com/httprunner/httprunner/v5/server/ext"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// serverCmd represents the server command
|
// serverCmd represents the server command
|
||||||
@@ -13,8 +12,7 @@ var serverCmd = &cobra.Command{
|
|||||||
Long: `start hrp server, call httprunner by HTTP`,
|
Long: `start hrp server, call httprunner by HTTP`,
|
||||||
Args: cobra.MinimumNArgs(1),
|
Args: cobra.MinimumNArgs(1),
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return server_ext.NewExtRouter().Run(port)
|
return server.NewRouter().Run(port)
|
||||||
// return server.NewRouter().Run(port)
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0-beta-2503112242
|
v5.0.0-beta-2503141055
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
package server_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/server"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *RouterExt) installAppHandler(c *gin.Context) {
|
|
||||||
var appInstallReq AppInstallRequest
|
|
||||||
if err := c.ShouldBindJSON(&appInstallReq); err != nil {
|
|
||||||
server.RenderErrorValidateRequest(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
driver, err := r.GetDriver(c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = driver.InstallByUrl(appInstallReq.AppUrl)
|
|
||||||
if err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if androidDevice, ok := driver.GetDevice().(*uixt.AndroidDevice); ok {
|
|
||||||
_ = driver.Home()
|
|
||||||
if appInstallReq.MappingUrl == "" || appInstallReq.ResourceMappingUrl == "" {
|
|
||||||
server.RenderSuccess(c, true)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
localMappingPath, err := uixt.DownloadFileByUrl(appInstallReq.MappingUrl)
|
|
||||||
if err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = os.Remove(localMappingPath)
|
|
||||||
}()
|
|
||||||
if err = androidDevice.PushFile(
|
|
||||||
localMappingPath,
|
|
||||||
fmt.Sprintf("/data/local/tmp/%s_map.txt", appInstallReq.PackageName)); err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
localResourceMappingPath, err := uixt.DownloadFileByUrl(
|
|
||||||
appInstallReq.ResourceMappingUrl)
|
|
||||||
if err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = os.Remove(localResourceMappingPath)
|
|
||||||
}()
|
|
||||||
if err = androidDevice.PushFile(localResourceMappingPath,
|
|
||||||
fmt.Sprintf("/data/local/tmp/%s_resmap.txt", appInstallReq.PackageName)); err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
server.RenderSuccess(c, true)
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package server_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/server"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/ai"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/driver_ext"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *RouterExt) GetDriver(c *gin.Context) (driverExt *driver_ext.StubXTDriver, err error) {
|
|
||||||
var device uixt.IDevice
|
|
||||||
var driver driver_ext.IStubDriver
|
|
||||||
deviceObj, exists := c.Get("device")
|
|
||||||
if !exists {
|
|
||||||
device, err = r.GetDevice(c)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
device = deviceObj.(uixt.IDevice)
|
|
||||||
}
|
|
||||||
platform := c.Param("platform")
|
|
||||||
switch strings.ToLower(platform) {
|
|
||||||
case "android":
|
|
||||||
driver, err = driver_ext.NewStubAndroidDriver(device.(*uixt.AndroidDevice))
|
|
||||||
case "ios":
|
|
||||||
driver, err = driver_ext.NewStubIOSDriver(device.(*uixt.IOSDevice))
|
|
||||||
case "browser":
|
|
||||||
driver, err = driver_ext.NewStubBrowserDriver(device.(*uixt.BrowserDevice))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
server.RenderErrorInitDriver(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
driverExt = driver_ext.NewStubXTDriver(driver,
|
|
||||||
ai.WithCVService(ai.CVServiceTypeVEDEM))
|
|
||||||
c.Set("driver", driverExt)
|
|
||||||
return driverExt, nil
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package server_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (r *RouterExt) loginHandler(c *gin.Context) {
|
|
||||||
var loginReq LoginRequest
|
|
||||||
if err := c.ShouldBindJSON(&loginReq); err != nil {
|
|
||||||
server.RenderErrorValidateRequest(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
driver, err := r.GetDriver(c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
info, err := driver.LoginNoneUI(
|
|
||||||
loginReq.PackageName, loginReq.PhoneNumber,
|
|
||||||
loginReq.Captcha, loginReq.Password)
|
|
||||||
if err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.RenderSuccess(c, info)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RouterExt) logoutHandler(c *gin.Context) {
|
|
||||||
var logoutReq LogoutRequest
|
|
||||||
if err := c.ShouldBindJSON(&logoutReq); err != nil {
|
|
||||||
server.RenderErrorValidateRequest(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
driver, err := r.GetDriver(c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = driver.LogoutNoneUI(logoutReq.PackageName)
|
|
||||||
if err != nil {
|
|
||||||
server.RenderError(c, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
server.RenderSuccess(c, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RouterExt) sourceHandler(c *gin.Context) {
|
|
||||||
driver, err := r.GetDriver(c)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
source, err := driver.Source()
|
|
||||||
if err != nil {
|
|
||||||
log.Warn().Err(err).Msg("get source failed")
|
|
||||||
}
|
|
||||||
if source == "{}" || source == "" {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
source, err = driver.Source()
|
|
||||||
}
|
|
||||||
server.RenderSuccess(c, source)
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package server_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/httprunner/httprunner/v5/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RouterExt struct {
|
|
||||||
*server.Router
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewExtRouter() *RouterExt {
|
|
||||||
router := &RouterExt{
|
|
||||||
Router: server.NewRouter(),
|
|
||||||
}
|
|
||||||
router.Init()
|
|
||||||
return router
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RouterExt) Init() {
|
|
||||||
apiV1PlatformSerial := r.Group("/api/v1").Group("/:platform").Group("/:serial")
|
|
||||||
|
|
||||||
apiV1PlatformSerial.GET("/stub/source", r.sourceHandler)
|
|
||||||
apiV1PlatformSerial.POST("/stub/login", r.loginHandler)
|
|
||||||
apiV1PlatformSerial.POST("/stub/logout", r.logoutHandler)
|
|
||||||
apiV1PlatformSerial.POST("/app/install", r.installAppHandler)
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package server_ext
|
|
||||||
|
|
||||||
type AppInstallRequest struct {
|
|
||||||
AppUrl string `json:"appUrl" binding:"required"`
|
|
||||||
MappingUrl string `json:"mappingUrl"`
|
|
||||||
ResourceMappingUrl string `json:"resourceMappingUrl"`
|
|
||||||
PackageName string `json:"packageName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LoginRequest struct {
|
|
||||||
PackageName string `json:"packageName"`
|
|
||||||
PhoneNumber string `json:"phoneNumber"`
|
|
||||||
Captcha string `json:"captcha" binding:"required_without=Password"`
|
|
||||||
Password string `json:"password" binding:"required_without=Captcha"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LogoutRequest struct {
|
|
||||||
PackageName string `json:"packageName"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type HttpResponse struct {
|
|
||||||
Code int `json:"errorCode"`
|
|
||||||
Message string `json:"errorMsg"`
|
|
||||||
Result interface{} `json:"result,omitempty"`
|
|
||||||
}
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
package driver_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/code"
|
|
||||||
"github.com/httprunner/httprunner/v5/internal/json"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StubAndroidDriver struct {
|
|
||||||
*uixt.ADBDriver
|
|
||||||
|
|
||||||
seq int
|
|
||||||
timeout time.Duration
|
|
||||||
douyinUrlPrefix string
|
|
||||||
douyinLiteUrlPrefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
StubSocketName = "com.bytest.device"
|
|
||||||
AndroidDouyinPort = 32316
|
|
||||||
AndroidDouyinLitePort = 32792
|
|
||||||
)
|
|
||||||
|
|
||||||
type AppLoginInfo struct {
|
|
||||||
Did string `json:"did,omitempty" yaml:"did,omitempty"`
|
|
||||||
Uid string `json:"uid,omitempty" yaml:"uid,omitempty"`
|
|
||||||
IsLogin bool `json:"is_login,omitempty" yaml:"is_login,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStubAndroidDriver(dev *uixt.AndroidDevice) (*StubAndroidDriver, error) {
|
|
||||||
adbDriver, err := uixt.NewADBDriver(dev)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
driver := &StubAndroidDriver{
|
|
||||||
timeout: 10 * time.Second,
|
|
||||||
ADBDriver: adbDriver,
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup driver
|
|
||||||
if err = driver.Setup(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return driver, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) GetDriver() uixt.IDriver {
|
|
||||||
return sad.ADBDriver
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) Setup() error {
|
|
||||||
socketLocalPort, err := sad.Device.Forward(StubSocketName)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(code.DeviceConnectionError,
|
|
||||||
fmt.Sprintf("forward port %d->%s failed: %v",
|
|
||||||
socketLocalPort, StubSocketName, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
douyinLocalPort, err := sad.Device.Forward(AndroidDouyinPort)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(code.DeviceConnectionError,
|
|
||||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
|
||||||
douyinLocalPort, AndroidDouyinPort, err))
|
|
||||||
}
|
|
||||||
sad.douyinUrlPrefix = fmt.Sprintf("http://127.0.0.1:%d", douyinLocalPort)
|
|
||||||
|
|
||||||
douyinLiteLocalPort, err := sad.Device.Forward(AndroidDouyinLitePort)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(code.DeviceConnectionError,
|
|
||||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
|
||||||
douyinLiteLocalPort, AndroidDouyinLitePort, err))
|
|
||||||
}
|
|
||||||
sad.douyinLiteUrlPrefix = fmt.Sprintf("http://127.0.0.1:%d", douyinLiteLocalPort)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) sendCommand(packageName string, cmdType string, params map[string]interface{}) (
|
|
||||||
interface{}, error) {
|
|
||||||
sad.seq++
|
|
||||||
packet := map[string]interface{}{
|
|
||||||
"Seq": sad.seq,
|
|
||||||
"Cmd": cmdType,
|
|
||||||
"v": "",
|
|
||||||
}
|
|
||||||
for key, value := range params {
|
|
||||||
if key == "Cmd" || key == "Seq" {
|
|
||||||
return "", errors.New("params cannot be Cmd or Seq")
|
|
||||||
}
|
|
||||||
packet[key] = value
|
|
||||||
}
|
|
||||||
data, err := json.Marshal(packet)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := sad.Device.RunStubCommand(append(data, '\n'), packageName)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var resultMap map[string]interface{}
|
|
||||||
if err := json.Unmarshal([]byte(res), &resultMap); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if resultMap["Error"] != nil {
|
|
||||||
return nil, fmt.Errorf("failed to call stub command: %s", resultMap["Error"].(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
return resultMap["Result"], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) AppLaunch(packageName string) (err error) {
|
|
||||||
_ = sad.EnableDevtool(packageName, true)
|
|
||||||
err = sad.ADBDriver.AppLaunch(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) Status() (types.DeviceStatus, error) {
|
|
||||||
app, err := sad.ForegroundInfo()
|
|
||||||
if err != nil {
|
|
||||||
return types.DeviceStatus{}, err
|
|
||||||
}
|
|
||||||
res, err := sad.sendCommand(app.PackageName, "Hello", nil)
|
|
||||||
if err != nil {
|
|
||||||
return types.DeviceStatus{}, err
|
|
||||||
}
|
|
||||||
log.Info().Msg(fmt.Sprintf("ping stub result :%v", res))
|
|
||||||
return types.DeviceStatus{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) Source(srcOpt ...option.SourceOption) (source string, err error) {
|
|
||||||
app, err := sad.ForegroundInfo()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"ClassName": "com.bytedance.byteinsight.MockOperator",
|
|
||||||
"Method": "getLayout",
|
|
||||||
"RetType": "",
|
|
||||||
"Args": []string{},
|
|
||||||
}
|
|
||||||
res, err := sad.sendCommand(app.PackageName, "CallStaticMethod", params)
|
|
||||||
if err != nil {
|
|
||||||
if app.PackageName == "com.ss.android.ugc.aweme" {
|
|
||||||
log.Error().Err(err).Msg("failed to get source")
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
if res.(string) == "{}" {
|
|
||||||
res, err = sad.sendCommand(app.PackageName, "CallStaticMethod", params)
|
|
||||||
if err != nil {
|
|
||||||
if app.PackageName == "com.ss.android.ugc.aweme" {
|
|
||||||
log.Error().Err(err).Msg("failed to get source")
|
|
||||||
}
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.(string), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) LoginNoneUI(packageName, phoneNumber, captcha, password string) (
|
|
||||||
info AppLoginInfo, err error) {
|
|
||||||
app, err := sad.ForegroundInfo()
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
// app.PackageName in ["com.ss.android.ugc.aweme", "com.ss.android.ugc.aweme.lite"]
|
|
||||||
if app.PackageName == "com.ss.android.ugc.aweme" || app.PackageName == "com.ss.android.ugc.aweme.lite" {
|
|
||||||
return sad.LoginDouyin(app.PackageName, phoneNumber, captcha, password)
|
|
||||||
} else if app.PackageName == "com.ss.android.article.video" {
|
|
||||||
return sad.LoginXigua(app.PackageName, phoneNumber, captcha, password)
|
|
||||||
} else {
|
|
||||||
return info, fmt.Errorf("not support app %s", app.PackageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) LoginXigua(packageName, phoneNumber, captcha, password string) (
|
|
||||||
info AppLoginInfo, err error) {
|
|
||||||
loginSchema := ""
|
|
||||||
if captcha != "" {
|
|
||||||
loginSchema = fmt.Sprintf("snssdk32://local_channel_autologin?login_type=1&account=%s&smscode=%s",
|
|
||||||
phoneNumber, captcha)
|
|
||||||
} else if password != "" {
|
|
||||||
loginSchema = fmt.Sprintf("snssdk32://local_channel_autologin?login_type=2&account=%s&password=%s",
|
|
||||||
phoneNumber, password)
|
|
||||||
} else {
|
|
||||||
return info, fmt.Errorf("password and capcha is empty")
|
|
||||||
}
|
|
||||||
info.IsLogin = true
|
|
||||||
return info, sad.OpenUrl(loginSchema)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) LoginDouyin(packageName, phoneNumber, captcha, password string) (
|
|
||||||
info AppLoginInfo, err error) {
|
|
||||||
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"phone": phoneNumber,
|
|
||||||
}
|
|
||||||
if captcha != "" {
|
|
||||||
params["captcha"] = captcha
|
|
||||||
} else if password != "" {
|
|
||||||
params["password"] = password
|
|
||||||
} else {
|
|
||||||
return info, fmt.Errorf("password and capcha is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
info, err = sad.getLoginAppInfo(packageName)
|
|
||||||
if err != nil {
|
|
||||||
log.Err(err).Msg("failed to get login info")
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
if info.Did == "" {
|
|
||||||
_ = sad.Home()
|
|
||||||
_ = sad.AppLaunch(packageName)
|
|
||||||
time.Sleep(20 * time.Second)
|
|
||||||
}
|
|
||||||
if info.IsLogin {
|
|
||||||
_ = sad.LogoutNoneUI(packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
urlPrefix, err := sad.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/login/account/"
|
|
||||||
resp, err := sad.Session.POST(params, fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
res, err := resp.ValueConvertToJsonObject()
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
log.Info().Msgf("%v", res)
|
|
||||||
if res["isSuccess"] != true {
|
|
||||||
err = fmt.Errorf("falied to login %s", res["data"])
|
|
||||||
log.Err(err).Msgf("%v", res)
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
time.Sleep(20 * time.Second)
|
|
||||||
info, err = sad.getLoginAppInfo(packageName)
|
|
||||||
if err != nil || !info.IsLogin {
|
|
||||||
return info, fmt.Errorf("falied to login %v", info)
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) LogoutNoneUI(packageName string) error {
|
|
||||||
urlPrefix, err := sad.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/logout"
|
|
||||||
resp, err := sad.Session.GET(fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) EnableDevtool(packageName string, enable bool) error {
|
|
||||||
urlPrefix, err := sad.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/devtool/enable"
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"enable": enable,
|
|
||||||
}
|
|
||||||
resp, err := sad.Session.POST(params, fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res, err := resp.ValueConvertToJsonObject()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info().Msgf("%v", res)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) {
|
|
||||||
urlPrefix, err := sad.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/app/info"
|
|
||||||
resp, err := sad.Session.GET(fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
res, err := resp.ValueConvertToJsonObject()
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
if res["isSuccess"] != true {
|
|
||||||
err = fmt.Errorf("falied to get app info %s", res["data"])
|
|
||||||
log.Err(err).Msgf("%v", res)
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = json.Unmarshal([]byte(res["data"].(string)), &info)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("falied to parse app info %s", res["data"])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) getUrlPrefix(packageName string) (urlPrefix string, err error) {
|
|
||||||
if packageName == "com.ss.android.ugc.aweme" {
|
|
||||||
urlPrefix = sad.douyinUrlPrefix
|
|
||||||
} else if packageName == "com.ss.android.ugc.aweme.lite" {
|
|
||||||
urlPrefix = sad.douyinLiteUrlPrefix
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("not support app %s", packageName)
|
|
||||||
}
|
|
||||||
return urlPrefix, nil
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
package driver_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setupAndroidStubDriver(t *testing.T) *StubAndroidDriver {
|
|
||||||
device, err := uixt.NewAndroidDevice()
|
|
||||||
require.Nil(t, err)
|
|
||||||
device.Options.UIA2 = false
|
|
||||||
device.Options.LogOn = false
|
|
||||||
driver, err := NewStubAndroidDriver(device)
|
|
||||||
require.Nil(t, err)
|
|
||||||
return driver
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAndroidStubDriver_LoginNoneUI(t *testing.T) {
|
|
||||||
androidStubDriver := setupAndroidStubDriver(t)
|
|
||||||
info, err := androidStubDriver.LoginNoneUI("com.ss.android.ugc.aweme", "12343418541", "", "im112233")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
t.Logf("login info: %+v", info)
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package driver_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StubBrowserDriver struct {
|
|
||||||
*uixt.BrowserDriver
|
|
||||||
|
|
||||||
sessionId string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStubBrowserDriver(device *uixt.BrowserDevice) (driver *StubBrowserDriver, err error) {
|
|
||||||
browserDriver, err := uixt.NewBrowserDriver(device)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "create browser session failed")
|
|
||||||
}
|
|
||||||
driver = &StubBrowserDriver{
|
|
||||||
BrowserDriver: browserDriver,
|
|
||||||
}
|
|
||||||
driver.sessionId = device.UUID()
|
|
||||||
return driver, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *StubBrowserDriver) GetDriver() uixt.IDriver {
|
|
||||||
return wd.BrowserDriver
|
|
||||||
}
|
|
||||||
|
|
||||||
// Source Return application elements tree
|
|
||||||
func (wd *StubBrowserDriver) Source(srcOpt ...option.SourceOption) (string, error) {
|
|
||||||
resp, err := wd.BrowserDriver.HttpGet(http.MethodGet, wd.sessionId, "stub/source")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonData, err := json.Marshal(resp.Data)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(jsonData), err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *StubBrowserDriver) LoginNoneUI(packageName, phoneNumber, captcha, password string) (
|
|
||||||
info AppLoginInfo, err error) {
|
|
||||||
data := map[string]interface{}{
|
|
||||||
"url": packageName,
|
|
||||||
"web_cookie": password,
|
|
||||||
}
|
|
||||||
resp, err := wd.HttpPOST(data, wd.sessionId, "stub/login")
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
respdata := resp.Data.(map[string]interface{})
|
|
||||||
loginSuccss := AppLoginInfo{
|
|
||||||
IsLogin: true,
|
|
||||||
Uid: respdata["webid"].(string),
|
|
||||||
Did: password,
|
|
||||||
}
|
|
||||||
return loginSuccss, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (wd *StubBrowserDriver) LogoutNoneUI(packageName string) error {
|
|
||||||
return errors.New("not implemented")
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
package driver_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/ai"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ IStubDriver = (*StubAndroidDriver)(nil)
|
|
||||||
_ IStubDriver = (*StubIOSDriver)(nil)
|
|
||||||
_ IStubDriver = (*StubBrowserDriver)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type IStubDriver interface {
|
|
||||||
GetDriver() uixt.IDriver
|
|
||||||
LoginNoneUI(packageName, phoneNumber, captcha, password string) (info AppLoginInfo, err error)
|
|
||||||
LogoutNoneUI(packageName string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewStubXTDriver(stubDriver IStubDriver, opts ...ai.AIServiceOption) *StubXTDriver {
|
|
||||||
services := ai.NewAIService(opts...)
|
|
||||||
driverExt := &StubXTDriver{
|
|
||||||
XTDriver: &uixt.XTDriver{
|
|
||||||
IDriver: stubDriver.GetDriver(),
|
|
||||||
CVService: services.ICVService,
|
|
||||||
LLMService: services.ILLMService,
|
|
||||||
},
|
|
||||||
IStubDriver: stubDriver,
|
|
||||||
}
|
|
||||||
return driverExt
|
|
||||||
}
|
|
||||||
|
|
||||||
type StubXTDriver struct {
|
|
||||||
*uixt.XTDriver
|
|
||||||
IStubDriver
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dExt *StubXTDriver) InstallByUrl(url string, opts ...option.InstallOption) error {
|
|
||||||
appPath, err := uixt.DownloadFileByUrl(url)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = dExt.Install(appPath, opts...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dExt *StubXTDriver) Install(filePath string, opts ...option.InstallOption) error {
|
|
||||||
if _, ok := dExt.GetDevice().(*uixt.AndroidDevice); ok {
|
|
||||||
stopChan := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
go func() {
|
|
||||||
_ = dExt.TapByOCR("^(.*无视风险安装|正在扫描.*|我知道了|稍后继续|稍后提醒|继续安装|知道了|确定|继续|完成|点击继续安装|继续安装旧版本|替换|.*正在安装|安装|授权本次安装|重新安装|仍要安装|更多详情|我知道了|已了解此应用未经检测.)$", option.WithRegex(true), option.WithIgnoreNotFoundError(true))
|
|
||||||
//_ = dExt.IDriver.TapByHierarchy("^(.*无视风险安装|正在扫描.*|我知道了|稍后继续|稍后提醒|继续安装|知道了|确定|继续|完成|点击继续安装|继续安装旧版本|替换|.*正在安装|安装|授权本次安装|重新安装|仍要安装|更多详情|我知道了|已了解此应用未经检测.)$", option.WithRegex(true), option.WithIgnoreNotFoundError(true))
|
|
||||||
}()
|
|
||||||
case <-stopChan:
|
|
||||||
log.Info().Msg("install complete")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
defer func() {
|
|
||||||
close(stopChan)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
return dExt.GetDevice().Install(filePath, opts...)
|
|
||||||
}
|
|
||||||
@@ -1,518 +0,0 @@
|
|||||||
package driver_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/code"
|
|
||||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/types"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
type StubIOSDriver struct {
|
|
||||||
Device *uixt.IOSDevice
|
|
||||||
Session *uixt.DriverSession
|
|
||||||
WDADriver *uixt.WDADriver
|
|
||||||
timeout time.Duration
|
|
||||||
douyinUrlPrefix string
|
|
||||||
douyinLiteUrlPrefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
IOSDouyinPort = 32921
|
|
||||||
IOSDouyinLitePort = 33461
|
|
||||||
defaultBightInsightPort = 8000
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewStubIOSDriver(dev *uixt.IOSDevice) (*StubIOSDriver, error) {
|
|
||||||
driver := &StubIOSDriver{
|
|
||||||
Device: dev,
|
|
||||||
timeout: 10 * time.Second,
|
|
||||||
Session: uixt.NewDriverSession(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup driver
|
|
||||||
if err := driver.Setup(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return driver, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) SetupWda() (err error) {
|
|
||||||
if s.WDADriver != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
s.WDADriver, err = uixt.NewWDADriver(s.Device)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) GetDriver() uixt.IDriver {
|
|
||||||
return s.WDADriver
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Setup() error {
|
|
||||||
localPort, err := s.getLocalPort()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s.Session.SetBaseURL(fmt.Sprintf("http://127.0.0.1:%d", localPort))
|
|
||||||
|
|
||||||
localDouyinPort, err := builtin.GetFreePort()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError,
|
|
||||||
fmt.Sprintf("get free port failed: %v", err))
|
|
||||||
}
|
|
||||||
if err = s.Device.Forward(localDouyinPort, IOSDouyinPort); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError,
|
|
||||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
|
||||||
}
|
|
||||||
s.douyinUrlPrefix = fmt.Sprintf("http://127.0.0.1:%d", localDouyinPort)
|
|
||||||
|
|
||||||
localDouyinLitePort, err := builtin.GetFreePort()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError,
|
|
||||||
fmt.Sprintf("get free port failed: %v", err))
|
|
||||||
}
|
|
||||||
if err = s.Device.Forward(localDouyinLitePort, IOSDouyinLitePort); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError,
|
|
||||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
|
||||||
}
|
|
||||||
s.douyinLiteUrlPrefix = fmt.Sprintf("http://127.0.0.1:%d", localDouyinLitePort)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) getLocalPort() (int, error) {
|
|
||||||
localStubPort, err := builtin.GetFreePort()
|
|
||||||
if err != nil {
|
|
||||||
return 0, errors.Wrap(code.DeviceHTTPDriverError,
|
|
||||||
fmt.Sprintf("get free port failed: %v", err))
|
|
||||||
}
|
|
||||||
if err = s.Device.Forward(localStubPort, defaultBightInsightPort); err != nil {
|
|
||||||
return 0, errors.Wrap(code.DeviceHTTPDriverError,
|
|
||||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
|
||||||
}
|
|
||||||
return localStubPort, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) {
|
|
||||||
resp, err := s.Session.GET("/source?format=json&onlyWeb=false")
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("get source err")
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
return string(resp), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) OpenUrl(urlStr string, opts ...option.ActionOption) (err error) {
|
|
||||||
targetUrl := fmt.Sprintf("/openURL?url=%s", url.QueryEscape(urlStr))
|
|
||||||
_, err = s.Session.GET(targetUrl)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("get source err")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) LoginNoneUI(packageName, phoneNumber, captcha, password string) (info AppLoginInfo, err error) {
|
|
||||||
appInfo, err := s.ForegroundInfo()
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
if appInfo.BundleId == "com.ss.iphone.ugc.AwemeInhouse" || appInfo.BundleId == "com.ss.iphone.ugc.awemeinhouse.lite" {
|
|
||||||
return s.LoginDouyin(appInfo.BundleId, phoneNumber, captcha, password)
|
|
||||||
} else if appInfo.BundleId == "com.ss.iphone.InHouse.article.Video" {
|
|
||||||
return s.LoginXigua(appInfo.BundleId, phoneNumber, captcha, password)
|
|
||||||
} else {
|
|
||||||
return info, fmt.Errorf("not support app")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) LoginXigua(packageName, phoneNumber, captcha, password string) (info AppLoginInfo, err error) {
|
|
||||||
loginSchema := ""
|
|
||||||
if captcha != "" {
|
|
||||||
loginSchema = fmt.Sprintf("snssdk32://local_channel_autologin?login_type=1&account=%s&smscode=%s", phoneNumber, captcha)
|
|
||||||
} else if password != "" {
|
|
||||||
loginSchema = fmt.Sprintf("snssdk32://local_channel_autologin?login_type=2&account=%s&password=%s", phoneNumber, password)
|
|
||||||
} else {
|
|
||||||
return info, fmt.Errorf("password and capcha is empty")
|
|
||||||
}
|
|
||||||
info.IsLogin = true
|
|
||||||
return info, s.OpenUrl(loginSchema)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) LoginDouyin(packageName, phoneNumber, captcha, password string) (info AppLoginInfo, err error) {
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"phone": phoneNumber,
|
|
||||||
}
|
|
||||||
if captcha != "" {
|
|
||||||
params["captcha"] = captcha
|
|
||||||
} else if password != "" {
|
|
||||||
params["password"] = password
|
|
||||||
} else {
|
|
||||||
return info, fmt.Errorf("password and capcha is empty")
|
|
||||||
}
|
|
||||||
urlPrefix, err := s.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/login/account/"
|
|
||||||
resp, err := s.Session.POST(params, fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
res, err := resp.ValueConvertToJsonObject()
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
log.Info().Msgf("%v", res)
|
|
||||||
// {'isSuccess': True, 'data': '登录成功', 'code': 0}
|
|
||||||
if res["isSuccess"] != true {
|
|
||||||
err = fmt.Errorf("falied to logout %s", res["data"])
|
|
||||||
log.Err(err).Msgf("%v", res)
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
time.Sleep(20 * time.Second)
|
|
||||||
info, err = s.getLoginAppInfo(packageName)
|
|
||||||
if err != nil || !info.IsLogin {
|
|
||||||
return info, fmt.Errorf("falied to login %v", info)
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) LogoutNoneUI(packageName string) error {
|
|
||||||
urlPrefix, err := s.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/loginout/"
|
|
||||||
resp, err := s.Session.GET(fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) EnableDevtool(packageName string, enable bool) (err error) {
|
|
||||||
urlPrefix, err := s.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/devtool/enable"
|
|
||||||
|
|
||||||
params := map[string]interface{}{
|
|
||||||
"enable": enable,
|
|
||||||
}
|
|
||||||
resp, err := s.Session.POST(params, fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
res, err := resp.ValueConvertToJsonObject()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Info().Msgf("%v", res)
|
|
||||||
if res["isSuccess"] != true {
|
|
||||||
err = fmt.Errorf("falied to enable devtool %s", res["data"])
|
|
||||||
log.Err(err).Msgf("%v", res)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) {
|
|
||||||
urlPrefix, err := s.getUrlPrefix(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
fullUrl := urlPrefix + "/host/app/info/"
|
|
||||||
|
|
||||||
resp, err := s.Session.GET(fullUrl)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
res, err := resp.ValueConvertToJsonObject()
|
|
||||||
if err != nil {
|
|
||||||
return info, 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 info, err
|
|
||||||
}
|
|
||||||
err = json.Unmarshal([]byte(res["data"].(string)), &info)
|
|
||||||
if err != nil {
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) getUrlPrefix(packageName string) (urlPrefix string, err error) {
|
|
||||||
if packageName == "com.ss.iphone.ugc.AwemeInhouse" {
|
|
||||||
urlPrefix = s.douyinUrlPrefix
|
|
||||||
} else if packageName == "com.ss.iphone.ugc.awemeinhouse.lite" {
|
|
||||||
urlPrefix = s.douyinLiteUrlPrefix
|
|
||||||
} else {
|
|
||||||
return "", fmt.Errorf("not support app %s", packageName)
|
|
||||||
}
|
|
||||||
return urlPrefix, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) ScreenShot(opts ...option.ActionOption) (*bytes.Buffer, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return nil, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.ScreenShot(opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) AppLaunch(packageName string) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
err := s.WDADriver.AppLaunch(packageName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
_ = s.EnableDevtool(packageName, true)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) GetDevice() uixt.IDevice {
|
|
||||||
return s.Device
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) TearDown() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// session
|
|
||||||
func (s *StubIOSDriver) InitSession(capabilities option.Capabilities) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.InitSession(capabilities)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) GetSession() *uixt.DriverSession {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
_ = errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return s.WDADriver.GetSession()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) DeleteSession() error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.DeleteSession()
|
|
||||||
}
|
|
||||||
|
|
||||||
// device info and status
|
|
||||||
func (s *StubIOSDriver) Status() (types.DeviceStatus, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return types.DeviceStatus{}, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Status()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) DeviceInfo() (types.DeviceInfo, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return types.DeviceInfo{}, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.DeviceInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) BatteryInfo() (types.BatteryInfo, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return types.BatteryInfo{}, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.BatteryInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) ForegroundInfo() (types.AppInfo, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return types.AppInfo{}, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.ForegroundInfo()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) WindowSize() (types.Size, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return types.Size{}, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.WindowSize()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) ScreenRecord(opts ...option.ActionOption) (videoPath string, err error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return "", errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.ScreenRecord(opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Orientation() (types.Orientation, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return "", errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Orientation()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Rotation() (types.Rotation, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return types.Rotation{}, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Rotation()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) SetRotation(rotation types.Rotation) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.SetRotation(rotation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) SetIme(ime string) error {
|
|
||||||
return types.ErrDriverNotImplemented
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Home() error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Home()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Unlock() error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Back() error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Back()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) TapXY(x, y float64, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.TapXY(x, y, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) TapAbsXY(x, y float64, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.TapAbsXY(x, y, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.DoubleTap(x, y, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.TouchAndHold(x, y, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Drag(fromX, fromY, toX, toY, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Swipe(fromX, fromY, toX, toY, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Input(text string, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Input(text, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) Backspace(count int, opts ...option.ActionOption) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.Backspace(count, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) AppTerminate(packageName string) (bool, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return false, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.AppTerminate(packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) AppClear(packageName string) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.AppClear(packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// image related
|
|
||||||
func (s *StubIOSDriver) PushImage(localPath string) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.PushImage(localPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) ClearImages() error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.ClearImages()
|
|
||||||
}
|
|
||||||
|
|
||||||
// triggers the log capture and returns the log entries
|
|
||||||
func (s *StubIOSDriver) StartCaptureLog(identifier ...string) error {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.StartCaptureLog(identifier...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *StubIOSDriver) StopCaptureLog() (interface{}, error) {
|
|
||||||
if err := s.SetupWda(); err != nil {
|
|
||||||
return nil, errors.Wrap(code.DeviceHTTPDriverError, err.Error())
|
|
||||||
}
|
|
||||||
return s.WDADriver.StopCaptureLog()
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package driver_ext
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt"
|
|
||||||
"github.com/httprunner/httprunner/v5/uixt/option"
|
|
||||||
)
|
|
||||||
|
|
||||||
func setupIOSStubDriver(t *testing.T) *StubIOSDriver {
|
|
||||||
iOSDevice, err := uixt.NewIOSDevice(
|
|
||||||
option.WithWDAPort(8700),
|
|
||||||
option.WithWDAMjpegPort(8800),
|
|
||||||
option.WithResetHomeOnStartup(false))
|
|
||||||
require.Nil(t, err)
|
|
||||||
iOSStubDriver, err := NewStubIOSDriver(iOSDevice)
|
|
||||||
require.Nil(t, err)
|
|
||||||
return iOSStubDriver
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIOSStubDriver_LoginNoneUI(t *testing.T) {
|
|
||||||
iOSStubDriver := setupIOSStubDriver(t)
|
|
||||||
info, err := iOSStubDriver.LoginNoneUI("com.ss.iphone.ugc.AwemeInhouse", "12343418541", "", "im112233")
|
|
||||||
assert.Nil(t, err)
|
|
||||||
t.Logf("login info: %+v", info)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user