mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-12 02:21:29 +08:00
refactor: api
This commit is contained in:
@@ -27,19 +27,11 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
var (
|
||||
DouyinServerPort = 32316
|
||||
|
||||
const (
|
||||
// adb server
|
||||
AdbServerHost = "localhost"
|
||||
AdbServerPort = gadb.AdbServerPort // 5037
|
||||
|
||||
// uiautomator2 server
|
||||
UIA2ServerHost = "localhost"
|
||||
UIA2ServerPort = 6790
|
||||
UIA2ServerPackageName = "io.appium.uiautomator2.server"
|
||||
UIA2ServerTestPackageName = "io.appium.uiautomator2.server.test"
|
||||
|
||||
EvalInstallerPackageName = "sogou.mobile.explorer"
|
||||
InstallViaInstallerCommand = "am start -S -n sogou.mobile.explorer/.PackageInstallerActivity -d"
|
||||
)
|
||||
@@ -48,13 +40,8 @@ var (
|
||||
var evalite embed.FS
|
||||
|
||||
func NewAndroidDevice(opts ...option.AndroidDeviceOption) (device *AndroidDevice, err error) {
|
||||
androidOptions := &option.AndroidDeviceConfig{
|
||||
UIA2IP: UIA2ServerHost,
|
||||
UIA2Port: UIA2ServerPort,
|
||||
}
|
||||
for _, option := range opts {
|
||||
option(androidOptions)
|
||||
}
|
||||
androidOptions := option.NewAndroidDeviceOptions(opts...)
|
||||
|
||||
deviceList, err := GetAndroidDevices(androidOptions.SerialNumber)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
||||
@@ -75,9 +62,9 @@ func NewAndroidDevice(opts ...option.AndroidDeviceOption) (device *AndroidDevice
|
||||
}
|
||||
|
||||
device = &AndroidDevice{
|
||||
AndroidDeviceConfig: androidOptions,
|
||||
d: dev,
|
||||
logcat: NewAdbLogcat(device.SerialNumber),
|
||||
Device: dev,
|
||||
AndroidDeviceOptions: androidOptions,
|
||||
Logcat: NewAdbLogcat(androidOptions.SerialNumber),
|
||||
}
|
||||
|
||||
evalToolRaw, err := evalite.ReadFile("evalite")
|
||||
@@ -126,30 +113,30 @@ func GetAndroidDevices(serial ...string) (devices []*gadb.Device, err error) {
|
||||
}
|
||||
|
||||
type AndroidDevice struct {
|
||||
*option.AndroidDeviceConfig
|
||||
d *gadb.Device
|
||||
logcat *AdbLogcat
|
||||
*gadb.Device
|
||||
*option.AndroidDeviceOptions
|
||||
Logcat *AdbLogcat
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) Init() error {
|
||||
dev.d.RunShellCommand("ime", "enable", UnicodeImePackageName)
|
||||
dev.d.RunShellCommand("rm", "-r", config.DeviceActionLogFilePath)
|
||||
func (dev *AndroidDevice) Setup() error {
|
||||
dev.RunShellCommand("ime", "enable", UnicodeImePackageName)
|
||||
dev.RunShellCommand("rm", "-r", config.DeviceActionLogFilePath)
|
||||
|
||||
if dev.UIA2 {
|
||||
// uiautomator2 server must be started before
|
||||
|
||||
// check uiautomator server package installed
|
||||
if !dev.d.IsPackageInstalled(UIA2ServerPackageName) {
|
||||
if !dev.IsPackageInstalled(dev.UIA2ServerPackageName) {
|
||||
return errors.Wrapf(code.MobileUIDriverAppNotInstalled,
|
||||
"%s not installed", UIA2ServerPackageName)
|
||||
"%s not installed", dev.UIA2ServerPackageName)
|
||||
}
|
||||
if !dev.d.IsPackageInstalled(UIA2ServerTestPackageName) {
|
||||
if !dev.IsPackageInstalled(dev.UIA2ServerTestPackageName) {
|
||||
return errors.Wrapf(code.MobileUIDriverAppNotInstalled,
|
||||
"%s not installed", UIA2ServerTestPackageName)
|
||||
"%s not installed", dev.UIA2ServerTestPackageName)
|
||||
}
|
||||
|
||||
// TODO: check uiautomator server package running
|
||||
// if dev.d.IsPackageRunning(UIA2ServerPackageName) {
|
||||
// if dev.IsPackageRunning(UIA2ServerPackageName) {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
@@ -164,6 +151,10 @@ func (dev *AndroidDevice) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) Teardown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) UUID() string {
|
||||
return dev.SerialNumber
|
||||
}
|
||||
@@ -173,13 +164,13 @@ func (dev *AndroidDevice) LogEnabled() bool {
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
|
||||
var driver IWebDriver
|
||||
var driver IDriver
|
||||
if dev.UIA2 || dev.LogOn {
|
||||
driver, err = NewUIA2Driver(dev, opts...)
|
||||
driver, err = NewUIA2Driver(dev)
|
||||
} else if dev.STUB {
|
||||
driver, err = NewStubDriver(dev, opts...)
|
||||
driver, err = NewStubDriver(dev)
|
||||
} else {
|
||||
driver, err = NewADBDriver(dev, opts...)
|
||||
driver, err = NewADBDriver(dev)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to init UIA driver")
|
||||
@@ -221,13 +212,13 @@ func (dev *AndroidDevice) StopPcap() string {
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) Uninstall(packageName string) error {
|
||||
_, err := dev.d.RunShellCommand("uninstall", packageName)
|
||||
_, err := dev.RunShellCommand("uninstall", packageName)
|
||||
return err
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) Install(apkPath string, opts ...option.InstallOption) error {
|
||||
installOpts := option.NewInstallOptions(opts...)
|
||||
brand, err := dev.d.Brand()
|
||||
brand, err := dev.Brand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -245,7 +236,7 @@ func (dev *AndroidDevice) Install(apkPath string, opts ...option.InstallOption)
|
||||
case "vivo":
|
||||
return dev.installVivoSilent(apkPath, args...)
|
||||
case "oppo", "realme", "oneplus":
|
||||
if dev.d.IsPackageInstalled(EvalInstallerPackageName) {
|
||||
if dev.IsPackageInstalled(EvalInstallerPackageName) {
|
||||
return dev.installViaInstaller(apkPath, args...)
|
||||
}
|
||||
log.Warn().Msg("oppo not install eval installer")
|
||||
@@ -263,13 +254,13 @@ func (dev *AndroidDevice) installVivoSilent(apkPath string, args ...string) erro
|
||||
verifyCode = verifyCode[:8]
|
||||
verifyCode = "-V" + verifyCode
|
||||
args = append([]string{verifyCode}, args...)
|
||||
_, err := dev.d.InstallAPK(apkPath, args...)
|
||||
_, err := dev.InstallAPK(apkPath, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) error {
|
||||
appRemotePath := "/data/local/tmp/" + strconv.FormatInt(time.Now().UnixMilli(), 10) + ".apk"
|
||||
err := dev.d.PushFile(apkPath, appRemotePath, time.Now())
|
||||
err := dev.PushFile(apkPath, appRemotePath, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -277,7 +268,7 @@ func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) er
|
||||
defer func() {
|
||||
close(done)
|
||||
}()
|
||||
logcat := NewAdbLogcatWithCallback(dev.d.Serial(), func(line string) {
|
||||
logcat := NewAdbLogcatWithCallback(dev.Serial(), func(line string) {
|
||||
re := regexp.MustCompile(`\{.*?}`)
|
||||
match := re.FindString(line)
|
||||
if match == "" {
|
||||
@@ -307,7 +298,7 @@ func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) er
|
||||
// 需要监听是否完成安装
|
||||
command := strings.Split(InstallViaInstallerCommand, " ")
|
||||
args = append(command, appRemotePath)
|
||||
_, err = dev.d.RunShellCommand("am", args[1:]...)
|
||||
_, err = dev.RunShellCommand("am", args[1:]...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -322,13 +313,13 @@ func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) er
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) installCommon(apkPath string, args ...string) error {
|
||||
_, err := dev.d.InstallAPK(apkPath, args...)
|
||||
_, err := dev.InstallAPK(apkPath, args...)
|
||||
return err
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error) {
|
||||
// adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'
|
||||
output, err := dev.d.RunShellCommand("dumpsys", "window", "|", "grep", "-E", "'mCurrentFocus|mFocusedApp'")
|
||||
output, err := dev.RunShellCommand("dumpsys", "window", "|", "grep", "-E", "'mCurrentFocus|mFocusedApp'")
|
||||
if err != nil {
|
||||
return WindowInfo{}, errors.Wrap(err, "get current window failed")
|
||||
}
|
||||
@@ -354,7 +345,7 @@ func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error)
|
||||
}
|
||||
|
||||
// adb shell dumpsys activity activities | grep mResumedActivity
|
||||
output, err = dev.d.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "mResumedActivity")
|
||||
output, err = dev.RunShellCommand("dumpsys", "activity", "activities", "|", "grep", "mResumedActivity")
|
||||
if err != nil {
|
||||
return WindowInfo{}, errors.Wrap(err, "get current activity failed")
|
||||
}
|
||||
@@ -408,7 +399,7 @@ func (dev *AndroidDevice) GetPackageInfo(packageName string) (AppInfo, error) {
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) getPackageVersion(packageName string) (string, error) {
|
||||
output, err := dev.d.RunShellCommand("dumpsys", "package", packageName, "|", "grep", "versionName")
|
||||
output, err := dev.RunShellCommand("dumpsys", "package", packageName, "|", "grep", "versionName")
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "get package version failed")
|
||||
}
|
||||
@@ -423,7 +414,7 @@ func (dev *AndroidDevice) getPackageVersion(packageName string) (string, error)
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) getPackagePath(packageName string) (string, error) {
|
||||
output, err := dev.d.RunShellCommand("pm", "path", packageName)
|
||||
output, err := dev.RunShellCommand("pm", "path", packageName)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "get package path failed")
|
||||
}
|
||||
@@ -436,7 +427,7 @@ func (dev *AndroidDevice) getPackagePath(packageName string) (string, error) {
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) getPackageMD5(packagePath string) (string, error) {
|
||||
output, err := dev.d.RunShellCommand("md5sum", packagePath)
|
||||
output, err := dev.RunShellCommand("md5sum", packagePath)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "get package md5 failed")
|
||||
}
|
||||
@@ -450,12 +441,12 @@ func (dev *AndroidDevice) getPackageMD5(packagePath string) (string, error) {
|
||||
func (dev *AndroidDevice) startUIA2Server() error {
|
||||
const maxRetries = 3
|
||||
for attempt := 1; attempt <= maxRetries; attempt++ {
|
||||
log.Info().Str("package", UIA2ServerTestPackageName).
|
||||
log.Info().Str("package", dev.UIA2ServerTestPackageName).
|
||||
Int("attempt", attempt).Msg("start uiautomator server")
|
||||
// $ adb shell am instrument -w $UIA2ServerTestPackageName
|
||||
// -w: wait for instrumentation to finish before returning.
|
||||
// Required for test runners.
|
||||
out, err := dev.d.RunShellCommand("am", "instrument", "-w", UIA2ServerTestPackageName)
|
||||
out, err := dev.RunShellCommand("am", "instrument", "-w", dev.UIA2ServerTestPackageName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "start uiautomator server failed")
|
||||
}
|
||||
@@ -469,7 +460,7 @@ func (dev *AndroidDevice) startUIA2Server() error {
|
||||
}
|
||||
|
||||
func (dev *AndroidDevice) stopUIA2Server() error {
|
||||
_, err := dev.d.RunShellCommand("am", "force-stop", UIA2ServerPackageName)
|
||||
_, err := dev.RunShellCommand("am", "force-stop", dev.UIA2ServerPackageName)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/code"
|
||||
"github.com/httprunner/httprunner/v5/internal/config"
|
||||
"github.com/httprunner/httprunner/v5/internal/utf7"
|
||||
"github.com/httprunner/httprunner/v5/pkg/gadb"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
@@ -32,20 +31,18 @@ const (
|
||||
UnicodeImePackageName = "io.appium.settings/.UnicodeIME"
|
||||
)
|
||||
|
||||
func NewADBDriver(device *AndroidDevice, opts ...option.DriverOption) (*ADBDriver, error) {
|
||||
func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) {
|
||||
log.Info().Interface("device", device).Msg("init android adb driver")
|
||||
driver := &ADBDriver{}
|
||||
driver.NewSession(nil)
|
||||
driver.adbClient = device.d
|
||||
driver.logcat = device.logcat
|
||||
driver.Device = device.Device
|
||||
driver.Logcat = device.Logcat
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
type ADBDriver struct {
|
||||
DriverClient
|
||||
|
||||
adbClient *gadb.Device
|
||||
logcat *AdbLogcat
|
||||
*AndroidDevice
|
||||
*DriverClient
|
||||
}
|
||||
|
||||
func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, err error) {
|
||||
@@ -68,7 +65,7 @@ func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string,
|
||||
|
||||
// adb shell screencap -p
|
||||
if cmd == "screencap" {
|
||||
resp, err := ad.adbClient.ScreenCap()
|
||||
resp, err := ad.Device.ScreenCap()
|
||||
if err == nil {
|
||||
driverResult.ResponseBody = "OMITTED"
|
||||
return string(resp), nil
|
||||
@@ -76,7 +73,7 @@ func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string,
|
||||
return "", errors.Wrap(err, "adb screencap failed")
|
||||
}
|
||||
|
||||
output, err = ad.adbClient.RunShellCommand(cmd, args...)
|
||||
output, err = ad.Device.RunShellCommand(cmd, args...)
|
||||
driverResult.ResponseBody = strings.TrimSpace(output)
|
||||
return output, err
|
||||
}
|
||||
@@ -792,7 +789,7 @@ func (ad *ADBDriver) IsHealthy() (healthy bool, err error) {
|
||||
func (ad *ADBDriver) StartCaptureLog(identifier ...string) (err error) {
|
||||
log.Info().Msg("start adb log recording")
|
||||
// start logcat
|
||||
err = ad.logcat.CatchLogcat("iesqaMonitor:V")
|
||||
err = ad.Logcat.CatchLogcat("iesqaMonitor:V")
|
||||
if err != nil {
|
||||
err = errors.Wrap(code.DeviceCaptureLogError,
|
||||
fmt.Sprintf("start adb log recording failed: %v", err))
|
||||
@@ -804,7 +801,7 @@ func (ad *ADBDriver) StartCaptureLog(identifier ...string) (err error) {
|
||||
func (ad *ADBDriver) StopCaptureLog() (result interface{}, err error) {
|
||||
defer func() {
|
||||
log.Info().Msg("stop adb log recording")
|
||||
err = ad.logcat.Stop()
|
||||
err = ad.Logcat.Stop()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to get adb log recording")
|
||||
}
|
||||
@@ -812,14 +809,14 @@ func (ad *ADBDriver) StopCaptureLog() (result interface{}, err error) {
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to close adb log writer")
|
||||
}
|
||||
pointRes := ConvertPoints(ad.logcat.logs)
|
||||
pointRes := ConvertPoints(ad.Logcat.logs)
|
||||
|
||||
// 没有解析到打点日志,走兜底逻辑
|
||||
if len(pointRes) == 0 {
|
||||
log.Info().Msg("action log is null, use action file >>>")
|
||||
logFilePathPrefix := fmt.Sprintf("%v/data", config.ActionLogFilePath)
|
||||
files := []string{}
|
||||
ad.adbClient.RunShellCommand("pull", config.DeviceActionLogFilePath, config.ActionLogFilePath)
|
||||
ad.Device.RunShellCommand("pull", config.DeviceActionLogFilePath, config.ActionLogFilePath)
|
||||
err = filepath.Walk(config.ActionLogFilePath, func(path string, info fs.FileInfo, err error) error {
|
||||
// 只是需要日志文件
|
||||
if ok := strings.Contains(path, logFilePathPrefix); ok {
|
||||
@@ -898,7 +895,7 @@ func (ad *ADBDriver) SetIme(imeRegx string) error {
|
||||
if ime == "" {
|
||||
return fmt.Errorf("failed to set ime by %s, ime list: %v", imeRegx, imeList)
|
||||
}
|
||||
brand, _ := ad.adbClient.Brand()
|
||||
brand, _ := ad.Device.Brand()
|
||||
packageName := strings.Split(ime, "/")[0]
|
||||
res, err := ad.runShellCommand("ime", "set", ime)
|
||||
log.Info().Str("funcName", "SetIme").Interface("ime", ime).
|
||||
@@ -1061,7 +1058,7 @@ func (ad *ADBDriver) RecordScreen(folderPath string, duration time.Duration) (vi
|
||||
// scrcpy -s 7d21bb91 --record=file.mp4 -N
|
||||
cmd := exec.Command(
|
||||
"scrcpy",
|
||||
"-s", ad.adbClient.Serial(),
|
||||
"-s", ad.Device.Serial(),
|
||||
fmt.Sprintf("--record=%s", fileName),
|
||||
"-N",
|
||||
)
|
||||
|
||||
@@ -17,17 +17,20 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
const StubSocketName = "com.bytest.device"
|
||||
const (
|
||||
StubSocketName = "com.bytest.device"
|
||||
DouyinServerPort = 32316
|
||||
)
|
||||
|
||||
func NewStubDriver(device *AndroidDevice, opts ...option.DriverOption) (driver *StubAndroidDriver, err error) {
|
||||
socketLocalPort, err := device.d.Forward(StubSocketName)
|
||||
func NewStubDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) {
|
||||
socketLocalPort, err := device.Forward(StubSocketName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||
fmt.Sprintf("forward port %d->%s failed: %v",
|
||||
socketLocalPort, StubSocketName, err))
|
||||
}
|
||||
|
||||
serverLocalPort, err := device.d.Forward(DouyinServerPort)
|
||||
serverLocalPort, err := device.Forward(DouyinServerPort)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
||||
@@ -53,16 +56,16 @@ func NewStubDriver(device *AndroidDevice, opts ...option.DriverOption) (driver *
|
||||
}
|
||||
|
||||
driver.NewSession(nil)
|
||||
driver.adbClient = device.d
|
||||
driver.logcat = device.logcat
|
||||
driver.Device = device.Device
|
||||
driver.Logcat = device.Logcat
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
type StubAndroidDriver struct {
|
||||
*ADBDriver
|
||||
socket net.Conn
|
||||
seq int
|
||||
timeout time.Duration
|
||||
ADBDriver
|
||||
}
|
||||
|
||||
type AppLoginInfo struct {
|
||||
@@ -85,7 +88,7 @@ func (sad *StubAndroidDriver) httpGET(pathElem ...string) (rawResp rawResponse,
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("adb forward: %w", err)
|
||||
}
|
||||
sad.client = convertToHTTPClient(conn)
|
||||
sad.Client = convertToHTTPClient(conn)
|
||||
return sad.Request(http.MethodGet, sad.concatURL(nil, pathElem...), nil)
|
||||
}
|
||||
|
||||
@@ -103,7 +106,7 @@ func (sad *StubAndroidDriver) httpPOST(data interface{}, pathElem ...string) (ra
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("adb forward: %w", err)
|
||||
}
|
||||
sad.client = convertToHTTPClient(conn)
|
||||
sad.Client = convertToHTTPClient(conn)
|
||||
|
||||
var bsJSON []byte = nil
|
||||
if data != nil {
|
||||
@@ -137,7 +140,7 @@ func (sad *StubAndroidDriver) sendCommand(packageName string, cmdType string, pa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := sad.adbClient.RunStubCommand(append(data, '\n'), packageName)
|
||||
res, err := sad.Device.RunStubCommand(append(data, '\n'), packageName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -266,7 +269,7 @@ func (sad *StubAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) SetHDTStatus(status bool) error {
|
||||
_, err := sad.adbClient.RunShellCommand("settings", "put", "global", "feedbacker_sso_bypass_token", "default_sso_bypass_token")
|
||||
_, err := sad.Device.RunShellCommand("settings", "put", "global", "feedbacker_sso_bypass_token", "default_sso_bypass_token")
|
||||
if err != nil {
|
||||
log.Warn().Msg(fmt.Sprintf("failed to disable sso, error: %v", err))
|
||||
}
|
||||
|
||||
@@ -23,9 +23,9 @@ var errDriverNotImplemented = errors.New("driver method not implemented")
|
||||
|
||||
const forwardToPrefix = "forward-to-"
|
||||
|
||||
func NewUIA2Driver(device *AndroidDevice, opts ...option.DriverOption) (*UIA2Driver, error) {
|
||||
func NewUIA2Driver(device *AndroidDevice) (*UIA2Driver, error) {
|
||||
log.Info().Interface("device", device).Msg("init android UIA2 driver")
|
||||
localPort, err := device.d.Forward(device.UIA2Port)
|
||||
localPort, err := device.Forward(device.UIA2Port)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
||||
@@ -36,9 +36,9 @@ func NewUIA2Driver(device *AndroidDevice, opts ...option.DriverOption) (*UIA2Dri
|
||||
return nil, fmt.Errorf("adb forward: %w", err)
|
||||
}
|
||||
driver := new(UIA2Driver)
|
||||
driver.client = convertToHTTPClient(conn)
|
||||
driver.adbClient = device.d
|
||||
driver.logcat = device.logcat
|
||||
driver.Client = convertToHTTPClient(conn)
|
||||
driver.Device = device.Device
|
||||
driver.Logcat = device.Logcat
|
||||
|
||||
_, err = driver.NewSession(nil)
|
||||
if err != nil {
|
||||
@@ -48,35 +48,7 @@ func NewUIA2Driver(device *AndroidDevice, opts ...option.DriverOption) (*UIA2Dri
|
||||
}
|
||||
|
||||
type UIA2Driver struct {
|
||||
ADBDriver
|
||||
}
|
||||
|
||||
type BatteryStatus int
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
BatteryStatusUnknown BatteryStatus = iota
|
||||
BatteryStatusCharging
|
||||
BatteryStatusDischarging
|
||||
BatteryStatusNotCharging
|
||||
BatteryStatusFull
|
||||
)
|
||||
|
||||
func (bs BatteryStatus) String() string {
|
||||
switch bs {
|
||||
case BatteryStatusUnknown:
|
||||
return "unknown"
|
||||
case BatteryStatusCharging:
|
||||
return "charging"
|
||||
case BatteryStatusDischarging:
|
||||
return "discharging"
|
||||
case BatteryStatusNotCharging:
|
||||
return "not charging"
|
||||
case BatteryStatusFull:
|
||||
return "full"
|
||||
default:
|
||||
return fmt.Sprintf("unknown status code (%d)", bs)
|
||||
}
|
||||
*ADBDriver
|
||||
}
|
||||
|
||||
func (ud *UIA2Driver) resetDriver() error {
|
||||
|
||||
@@ -6,11 +6,13 @@ import (
|
||||
|
||||
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
|
||||
type IDevice interface {
|
||||
Init() error // init android device
|
||||
Setup() error
|
||||
Teardown() error
|
||||
|
||||
UUID() string // ios udid or android serial
|
||||
LogEnabled() bool
|
||||
|
||||
// TODO: add ctx to NewDriver
|
||||
// TODO: remove
|
||||
NewDriver(...option.DriverOption) (driverExt *DriverExt, err error)
|
||||
|
||||
Install(appPath string, opts ...option.InstallOption) error
|
||||
@@ -18,10 +20,4 @@ type IDevice interface {
|
||||
|
||||
GetPackageInfo(packageName string) (AppInfo, error)
|
||||
GetCurrentWindow() (windowInfo WindowInfo, err error)
|
||||
|
||||
// Teardown() error
|
||||
}
|
||||
|
||||
func NewDriver(device IDevice, opts ...option.DriverOption) (driver IWebDriver, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
// IWebDriver defines methods supported by IWebDriver drivers.
|
||||
type IWebDriver interface {
|
||||
// current implemeted driver: ADBDriver, UIA2Driver, WDADriver, HDCDriver
|
||||
type IDriver interface {
|
||||
// NewSession starts a new session and returns the SessionInfo.
|
||||
NewSession(capabilities option.Capabilities) (SessionInfo, error)
|
||||
|
||||
@@ -238,7 +238,7 @@ type DriverResult struct {
|
||||
|
||||
type DriverClient struct {
|
||||
urlPrefix *url.URL
|
||||
client *http.Client
|
||||
Client *http.Client
|
||||
|
||||
// cache to avoid repeated query
|
||||
scale float64
|
||||
@@ -323,7 +323,7 @@ func (wd *DriverClient) Request(method string, rawURL string, rawBody []byte) (r
|
||||
|
||||
driverResult.RequestTime = time.Now()
|
||||
var resp *http.Response
|
||||
if resp, err = wd.client.Do(req); err != nil {
|
||||
if resp, err = wd.Client.Do(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
@@ -371,14 +371,14 @@ func convertToHTTPClient(conn net.Conn) *http.Client {
|
||||
type DriverExt struct {
|
||||
Ctx context.Context
|
||||
Device IDevice
|
||||
Driver IWebDriver
|
||||
Driver IDriver
|
||||
ImageService IImageService // used to extract image data
|
||||
|
||||
// funplugin
|
||||
plugin funplugin.IPlugin
|
||||
}
|
||||
|
||||
func newDriverExt(device IDevice, driver IWebDriver, opts ...option.DriverOption) (dExt *DriverExt, err error) {
|
||||
func newDriverExt(device IDevice, driver IDriver, opts ...option.DriverOption) (dExt *DriverExt, err error) {
|
||||
options := option.NewDriverOptions(opts...)
|
||||
|
||||
dExt = &DriverExt{
|
||||
@@ -404,7 +404,7 @@ func newDriverExt(device IDevice, driver IWebDriver, opts ...option.DriverOption
|
||||
return dExt, nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) Init() error {
|
||||
func (dExt *DriverExt) Setup() error {
|
||||
// unlock device screen
|
||||
err := dExt.Driver.Unlock()
|
||||
if err != nil {
|
||||
|
||||
318
pkg/uixt/ext/android_stub.go
Normal file
318
pkg/uixt/ext/android_stub.go
Normal file
@@ -0,0 +1,318 @@
|
||||
package uixt_ext
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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/pkg/uixt"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
const (
|
||||
StubSocketName = "com.bytest.device"
|
||||
DouyinServerPort = 32316
|
||||
)
|
||||
|
||||
func NewStubDriver(device *uixt.AndroidDevice, opts ...option.DriverOption) (driver *StubAndroidDriver, err error) {
|
||||
socketLocalPort, err := device.Forward(StubSocketName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||
fmt.Sprintf("forward port %d->%s failed: %v",
|
||||
socketLocalPort, StubSocketName, err))
|
||||
}
|
||||
|
||||
serverLocalPort, err := device.Forward(DouyinServerPort)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
||||
serverLocalPort, DouyinServerPort, err))
|
||||
}
|
||||
|
||||
address := fmt.Sprintf("127.0.0.1:%d", socketLocalPort)
|
||||
conn, err := net.Dial("tcp", address)
|
||||
if err != nil {
|
||||
log.Err(err).Msg(fmt.Sprintf("failed to connect %s", address))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driver = &StubAndroidDriver{
|
||||
socket: conn,
|
||||
timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
rawURL := fmt.Sprintf("http://forward-to-%d:%d",
|
||||
serverLocalPort, DouyinServerPort)
|
||||
if driver.urlPrefix, err = url.Parse(rawURL); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
driver.Device = device.Device
|
||||
driver.Logcat = device.Logcat
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
type StubAndroidDriver struct {
|
||||
uixt.ADBDriver
|
||||
socket net.Conn
|
||||
seq int
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
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 (sad *StubAndroidDriver) 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.Request(http.MethodGet, sad.concatURL(nil, pathElem...), nil)
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) 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.Request(http.MethodPost, sad.concatURL(nil, pathElem...), bsJSON)
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) sendCommand(packageName string, cmdType string, params map[string]interface{}, readTimeout ...time.Duration) (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) DeleteSession() error {
|
||||
return sad.close()
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) close() error {
|
||||
if sad.socket != nil {
|
||||
return sad.socket.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) Status() (uixt.DeviceStatus, error) {
|
||||
app, err := sad.GetForegroundApp()
|
||||
if err != nil {
|
||||
return uixt.DeviceStatus{}, err
|
||||
}
|
||||
res, err := sad.sendCommand(app.PackageName, "Hello", nil)
|
||||
if err != nil {
|
||||
return uixt.DeviceStatus{}, err
|
||||
}
|
||||
log.Info().Msg(fmt.Sprintf("ping stub result :%v", res))
|
||||
return uixt.DeviceStatus{}, nil
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) Source(srcOpt ...option.SourceOption) (source string, err error) {
|
||||
app, err := sad.GetForegroundApp()
|
||||
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 {
|
||||
return "", err
|
||||
}
|
||||
return res.(string), nil
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) LoginNoneUI(packageName, phoneNumber string, 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")
|
||||
}
|
||||
resp, err := sad.httpPOST(params, "/host", "/login", "account")
|
||||
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 {
|
||||
resp, err := sad.httpGET("/host", "/logout")
|
||||
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
|
||||
}
|
||||
fmt.Printf("%v", resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
time.Sleep(3 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) 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 *StubAndroidDriver) SetHDTStatus(status bool) error {
|
||||
_, err := sad.Device.RunShellCommand("settings", "put", "global", "feedbacker_sso_bypass_token", "default_sso_bypass_token")
|
||||
if err != nil {
|
||||
log.Warn().Msg(fmt.Sprintf("failed to disable sso, error: %v", err))
|
||||
}
|
||||
params := map[string]interface{}{
|
||||
"ClassName": "com.bytedance.ies.stark.framework.HybridDevTool",
|
||||
"Method": "setEnabled",
|
||||
"RetType": "",
|
||||
"Args": []bool{status},
|
||||
}
|
||||
res, err := sad.sendCommand("com.ss.android.ugc.aweme", "CallStaticMethod", params)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to set hds status %v, error: %v", status, err)
|
||||
}
|
||||
log.Info().Msg(fmt.Sprintf("set hdt status result: %s", res))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sad *StubAndroidDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) {
|
||||
resp, err := sad.httpGET("/host", "/app", "/info")
|
||||
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 convertToHTTPClient(conn net.Conn) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
|
||||
return conn, nil
|
||||
},
|
||||
},
|
||||
Timeout: 30 * time.Second,
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,12 @@ var (
|
||||
)
|
||||
|
||||
type HarmonyDevice struct {
|
||||
*option.HarmonyDeviceConfig
|
||||
d *ghdc.Device
|
||||
*ghdc.Device
|
||||
*option.HarmonyDeviceOptions
|
||||
}
|
||||
|
||||
func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice, err error) {
|
||||
deviceConfig := option.NewHarmonyDeviceConfig(opts...)
|
||||
|
||||
deviceConfig := option.NewHarmonyDeviceOptions(opts...)
|
||||
deviceList, err := GetHarmonyDevices(deviceConfig.ConnectKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
||||
@@ -43,8 +42,8 @@ func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice
|
||||
}
|
||||
|
||||
device = &HarmonyDevice{
|
||||
HarmonyDeviceConfig: deviceConfig,
|
||||
d: dev,
|
||||
HarmonyDeviceOptions: deviceConfig,
|
||||
Device: dev,
|
||||
}
|
||||
log.Info().Str("connectKey", device.ConnectKey).Msg("init harmony device")
|
||||
return device, nil
|
||||
@@ -83,7 +82,11 @@ func GetHarmonyDevices(serial ...string) (devices []*ghdc.Device, err error) {
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (dev *HarmonyDevice) Init() error {
|
||||
func (dev *HarmonyDevice) Setup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dev *HarmonyDevice) Teardown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -96,7 +99,7 @@ func (dev *HarmonyDevice) LogEnabled() bool {
|
||||
}
|
||||
|
||||
func (dev *HarmonyDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
|
||||
driver, err := newHarmonyDriver(dev.d)
|
||||
driver, err := newHarmonyDriver(dev.Device)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to new harmony driver")
|
||||
return nil, err
|
||||
@@ -110,8 +113,8 @@ func (dev *HarmonyDevice) NewDriver(opts ...option.DriverOption) (driverExt *Dri
|
||||
return driverExt, nil
|
||||
}
|
||||
|
||||
func (dev *HarmonyDevice) NewUSBDriver(opts ...option.DriverOption) (driver IWebDriver, err error) {
|
||||
harmonyDriver, err := newHarmonyDriver(dev.d)
|
||||
func (dev *HarmonyDevice) NewUSBDriver(opts ...option.DriverOption) (driver IDriver, err error) {
|
||||
harmonyDriver, err := newHarmonyDriver(dev.Device)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to new harmony driver")
|
||||
return nil, err
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
)
|
||||
|
||||
type hdcDriver struct {
|
||||
points []ExportPoint
|
||||
DriverClient
|
||||
device *ghdc.Device
|
||||
*HarmonyDevice
|
||||
*DriverClient
|
||||
points []ExportPoint
|
||||
uiDriver *ghdc.UIDriver
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ const (
|
||||
|
||||
func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) {
|
||||
driver = new(hdcDriver)
|
||||
driver.device = device
|
||||
driver.Device = device
|
||||
uiDriver, err := ghdc.NewUIDriver(*device)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to new harmony ui driver")
|
||||
@@ -100,7 +100,7 @@ func (hd *hdcDriver) Homescreen() error {
|
||||
|
||||
func (hd *hdcDriver) Unlock() (err error) {
|
||||
// Todo 检查是否锁屏 hdc shell hidumper -s RenderService -a screen
|
||||
screenInfo, err := hd.device.RunShellCommand("hidumper", "-s", "RenderService", "-a", "screen")
|
||||
screenInfo, err := hd.RunShellCommand("hidumper", "-s", "RenderService", "-a", "screen")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func (hd *hdcDriver) AppLaunch(packageName string) error {
|
||||
}
|
||||
|
||||
func (hd *hdcDriver) AppTerminate(packageName string) (bool, error) {
|
||||
_, err := hd.device.RunShellCommand("aa", "force-stop", packageName)
|
||||
_, err := hd.RunShellCommand("aa", "force-stop", packageName)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to terminal app")
|
||||
return false, err
|
||||
|
||||
@@ -31,28 +31,6 @@ import (
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWDAPort = 8100
|
||||
defaultMjpegPort = 9100
|
||||
defaultBightInsightPort = 8000
|
||||
defaultDouyinServerPort = 32921
|
||||
)
|
||||
|
||||
const (
|
||||
// Changes the value of maximum depth for traversing elements source tree.
|
||||
// It may help to prevent out of memory or timeout errors while getting the elements source tree,
|
||||
// but it might restrict the depth of source tree.
|
||||
// A part of elements source tree might be lost if the value was too small. Defaults to 50
|
||||
snapshotMaxDepth = 10
|
||||
// Allows to customize accept/dismiss alert button selector.
|
||||
// It helps you to handle an arbitrary element as accept button in accept alert command.
|
||||
// The selector should be a valid class chain expression, where the search root is the alert element itself.
|
||||
// The default button location algorithm is used if the provided selector is wrong or does not match any element.
|
||||
// e.g. **/XCUIElementTypeButton[`label CONTAINS[c] ‘accept’`]
|
||||
acceptAlertButtonSelector = "**/XCUIElementTypeButton[`label IN {'允许','好','仅在使用应用期间','稍后再说'}`]"
|
||||
dismissAlertButtonSelector = "**/XCUIElementTypeButton[`label IN {'不允许','暂不'}`]"
|
||||
)
|
||||
|
||||
var tunnelManager *tunnel.TunnelManager = nil
|
||||
|
||||
func GetIOSDevices(udid ...string) (deviceList []ios.DeviceEntry, err error) {
|
||||
@@ -126,20 +104,7 @@ func RebootTunnel() (err error) {
|
||||
}
|
||||
|
||||
func NewIOSDevice(opts ...option.IOSDeviceOption) (device *IOSDevice, err error) {
|
||||
deviceOptions := &option.IOSDeviceConfig{
|
||||
Port: defaultWDAPort,
|
||||
MjpegPort: defaultMjpegPort,
|
||||
SnapshotMaxDepth: snapshotMaxDepth,
|
||||
AcceptAlertButtonSelector: acceptAlertButtonSelector,
|
||||
DismissAlertButtonSelector: dismissAlertButtonSelector,
|
||||
// switch to iOS springboard before init WDA session
|
||||
// avoid getting stuck when some super app is active such as douyin or wexin
|
||||
ResetHomeOnStartup: true,
|
||||
}
|
||||
for _, option := range opts {
|
||||
option(deviceOptions)
|
||||
}
|
||||
|
||||
deviceOptions := option.NewIOSDeviceOptions(opts...)
|
||||
deviceList, err := GetIOSDevices(deviceOptions.UDID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
||||
@@ -160,12 +125,12 @@ func NewIOSDevice(opts ...option.IOSDeviceOption) (device *IOSDevice, err error)
|
||||
}
|
||||
|
||||
device = &IOSDevice{
|
||||
IOSDeviceConfig: deviceOptions,
|
||||
listeners: make(map[int]*forward.ConnListener),
|
||||
d: dev,
|
||||
IOSDeviceOptions: deviceOptions,
|
||||
listeners: make(map[int]*forward.ConnListener),
|
||||
d: dev,
|
||||
}
|
||||
log.Info().Str("udid", device.UDID).Msg("init ios device")
|
||||
err = device.Init()
|
||||
err = device.Setup()
|
||||
if err != nil {
|
||||
_ = device.Teardown()
|
||||
return nil, err
|
||||
@@ -174,7 +139,7 @@ func NewIOSDevice(opts ...option.IOSDeviceOption) (device *IOSDevice, err error)
|
||||
}
|
||||
|
||||
type IOSDevice struct {
|
||||
*option.IOSDeviceConfig
|
||||
*option.IOSDeviceOptions
|
||||
d ios.DeviceEntry
|
||||
listeners map[int]*forward.ConnListener
|
||||
}
|
||||
@@ -207,7 +172,7 @@ const (
|
||||
ApplicationTypeAny ApplicationType = "Any"
|
||||
)
|
||||
|
||||
func (dev *IOSDevice) Init() error {
|
||||
func (dev *IOSDevice) Setup() error {
|
||||
images, err := dev.ListImages()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -270,7 +235,7 @@ func (dev *IOSDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverE
|
||||
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
|
||||
}
|
||||
|
||||
var driver IWebDriver
|
||||
var driver IDriver
|
||||
if dev.STUB {
|
||||
driver, err = dev.NewStubDriver()
|
||||
if err != nil {
|
||||
@@ -561,7 +526,7 @@ func (dev *IOSDevice) Reboot() error {
|
||||
}
|
||||
|
||||
// NewHTTPDriver creates new remote HTTP client, this will also start a new session.
|
||||
func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IWebDriver, err error) {
|
||||
func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IDriver, err error) {
|
||||
var localPort int
|
||||
localPort, err = strconv.Atoi(os.Getenv("WDA_LOCAL_PORT"))
|
||||
if err != nil {
|
||||
@@ -570,7 +535,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IW
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("get free port failed: %v", err))
|
||||
}
|
||||
if err = dev.forward(localPort, dev.Port); err != nil {
|
||||
if err = dev.forward(localPort, dev.WDAPort); err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
||||
}
|
||||
@@ -586,7 +551,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IW
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("get free port failed: %v", err))
|
||||
}
|
||||
if err = dev.forward(localMjpegPort, dev.MjpegPort); err != nil {
|
||||
if err = dev.forward(localMjpegPort, dev.WDAMjpegPort); err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
||||
}
|
||||
@@ -600,9 +565,9 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IW
|
||||
Msg("init WDA HTTP driver")
|
||||
|
||||
wd := new(wdaDriver)
|
||||
wd.device = dev
|
||||
wd.IOSDevice = dev
|
||||
wd.udid = dev.UDID
|
||||
wd.client = &http.Client{
|
||||
wd.Client = &http.Client{
|
||||
Timeout: time.Second * 10, // 设置超时时间为 10 秒
|
||||
}
|
||||
|
||||
@@ -634,7 +599,12 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IW
|
||||
return wd, nil
|
||||
}
|
||||
|
||||
func (dev *IOSDevice) NewStubDriver() (driver IWebDriver, err error) {
|
||||
const (
|
||||
defaultBightInsightPort = 8000
|
||||
defaultDouyinServerPort = 32921
|
||||
)
|
||||
|
||||
func (dev *IOSDevice) NewStubDriver() (driver IDriver, err error) {
|
||||
localStubPort, err := builtin.GetFreePort()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
|
||||
type stubIOSDriver struct {
|
||||
*wdaDriver
|
||||
DriverClient
|
||||
|
||||
bightInsightPrefix string
|
||||
serverPrefix string
|
||||
@@ -32,7 +31,7 @@ func newStubIOSDriver(bightInsightAddr, serverAddr string, dev *IOSDevice, readT
|
||||
driver.bightInsightPrefix = bightInsightAddr
|
||||
driver.serverPrefix = serverAddr
|
||||
driver.timeout = timeout
|
||||
driver.DriverClient.client = &http.Client{
|
||||
driver.DriverClient.Client = &http.Client{
|
||||
Timeout: time.Second * 10, // 设置超时时间为 10 秒
|
||||
}
|
||||
return driver, nil
|
||||
@@ -516,7 +515,7 @@ func (s *stubIOSDriver) LogoutNoneUI(packageName string) error {
|
||||
}
|
||||
|
||||
func (s *stubIOSDriver) TearDown() error {
|
||||
s.DriverClient.client.CloseIdleConnections()
|
||||
s.DriverClient.Client.CloseIdleConnections()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
iOSStubDriver IWebDriver
|
||||
iOSStubDriver IDriver
|
||||
iOSDevice *IOSDevice
|
||||
)
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ import (
|
||||
)
|
||||
|
||||
type wdaDriver struct {
|
||||
DriverClient
|
||||
*IOSDevice
|
||||
*DriverClient
|
||||
udid string
|
||||
device *IOSDevice
|
||||
mjpegHTTPConn net.Conn // via HTTP
|
||||
mjpegClient *http.Client
|
||||
mjpegUrl string
|
||||
@@ -467,7 +467,7 @@ func (wd *wdaDriver) GetForegroundApp() (appInfo AppInfo, err error) {
|
||||
if err != nil {
|
||||
return appInfo, err
|
||||
}
|
||||
apps, err := wd.device.ListApps(ApplicationTypeAny)
|
||||
apps, err := wd.ListApps(ApplicationTypeAny)
|
||||
if err != nil {
|
||||
return appInfo, err
|
||||
}
|
||||
@@ -1021,7 +1021,7 @@ func (wd *wdaDriver) GetDriverResults() []*DriverResult {
|
||||
|
||||
func (wd *wdaDriver) TearDown() error {
|
||||
wd.mjpegClient.CloseIdleConnections()
|
||||
wd.client.CloseIdleConnections()
|
||||
wd.Client.CloseIdleConnections()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
var (
|
||||
bundleId = "com.apple.Preferences"
|
||||
driver IWebDriver
|
||||
driver IDriver
|
||||
iOSDriverExt *DriverExt
|
||||
)
|
||||
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
package option
|
||||
|
||||
type AndroidDeviceConfig struct {
|
||||
import "github.com/httprunner/httprunner/v5/pkg/gadb"
|
||||
|
||||
type AndroidDeviceOptions struct {
|
||||
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
|
||||
STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub
|
||||
UIA2 bool `json:"uia2,omitempty" yaml:"uia2,omitempty"` // use uiautomator2
|
||||
UIA2IP string `json:"uia2_ip,omitempty" yaml:"uia2_ip,omitempty"` // uiautomator2 server ip
|
||||
UIA2Port int `json:"uia2_port,omitempty" yaml:"uia2_port,omitempty"` // uiautomator2 server port
|
||||
STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub
|
||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
||||
|
||||
// adb
|
||||
AdbServerHost string `json:"adb_server_host,omitempty" yaml:"adb_server_host,omitempty"`
|
||||
AdbServerPort int `json:"adb_server_port,omitempty" yaml:"adb_server_port,omitempty"`
|
||||
|
||||
// uiautomator2
|
||||
UIA2 bool `json:"uia2,omitempty" yaml:"uia2,omitempty"` // use uiautomator2
|
||||
UIA2IP string `json:"uia2_ip,omitempty" yaml:"uia2_ip,omitempty"` // uiautomator2 server ip
|
||||
UIA2Port int `json:"uia2_port,omitempty" yaml:"uia2_port,omitempty"` // uiautomator2 server port
|
||||
UIA2ServerPackageName string `json:"uia2_server_package_name,omitempty" yaml:"uia2_server_package_name,omitempty"`
|
||||
UIA2ServerTestPackageName string `json:"uia2_server_test_package_name,omitempty" yaml:"uia2_server_test_package_name,omitempty"`
|
||||
}
|
||||
|
||||
func (dev *AndroidDeviceConfig) Options() (deviceOptions []AndroidDeviceOption) {
|
||||
func (dev *AndroidDeviceOptions) Options() (deviceOptions []AndroidDeviceOption) {
|
||||
if dev.SerialNumber != "" {
|
||||
deviceOptions = append(deviceOptions, WithSerialNumber(dev.SerialNumber))
|
||||
}
|
||||
if dev.STUB {
|
||||
deviceOptions = append(deviceOptions, WithStub(true))
|
||||
}
|
||||
if dev.UIA2 {
|
||||
deviceOptions = append(deviceOptions, WithUIA2(true))
|
||||
}
|
||||
@@ -28,54 +41,87 @@ func (dev *AndroidDeviceConfig) Options() (deviceOptions []AndroidDeviceOption)
|
||||
return
|
||||
}
|
||||
|
||||
func NewAndroidDeviceConfig(opts ...AndroidDeviceOption) *AndroidDeviceConfig {
|
||||
config := &AndroidDeviceConfig{}
|
||||
const (
|
||||
// adb server
|
||||
defaultAdbServerHost = "localhost"
|
||||
defaultAdbServerPort = gadb.AdbServerPort // 5037
|
||||
|
||||
// uiautomator2 server
|
||||
defaultUIA2ServerHost = "localhost"
|
||||
defaultUIA2ServerPort = 6790
|
||||
defaultUIA2ServerPackageName = "io.appium.uiautomator2.server"
|
||||
defaultUIA2ServerTestPackageName = "io.appium.uiautomator2.server.test"
|
||||
)
|
||||
|
||||
func NewAndroidDeviceOptions(opts ...AndroidDeviceOption) *AndroidDeviceOptions {
|
||||
config := &AndroidDeviceOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(config)
|
||||
}
|
||||
|
||||
// set default
|
||||
if config.AdbServerHost == "" {
|
||||
config.AdbServerHost = defaultAdbServerHost
|
||||
}
|
||||
if config.AdbServerPort == 0 {
|
||||
config.AdbServerPort = defaultAdbServerPort
|
||||
}
|
||||
|
||||
if config.UIA2 {
|
||||
if config.UIA2IP == "" && config.UIA2Port == 0 {
|
||||
config.UIA2IP = defaultUIA2ServerHost
|
||||
config.UIA2Port = defaultUIA2ServerPort
|
||||
}
|
||||
if config.UIA2ServerPackageName == "" {
|
||||
config.UIA2ServerPackageName = defaultUIA2ServerPackageName
|
||||
}
|
||||
if config.UIA2ServerTestPackageName == "" {
|
||||
config.UIA2ServerTestPackageName = defaultUIA2ServerTestPackageName
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
type AndroidDeviceOption func(*AndroidDeviceConfig)
|
||||
type AndroidDeviceOption func(*AndroidDeviceOptions)
|
||||
|
||||
func WithDriverTypeADB() AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.STUB = false
|
||||
}
|
||||
}
|
||||
|
||||
func WithSerialNumber(serial string) AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.SerialNumber = serial
|
||||
}
|
||||
}
|
||||
|
||||
func WithUIA2(uia2On bool) AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.UIA2 = uia2On
|
||||
}
|
||||
}
|
||||
|
||||
func WithStub(stubOn bool) AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.STUB = stubOn
|
||||
}
|
||||
}
|
||||
|
||||
func WithUIA2IP(ip string) AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.UIA2IP = ip
|
||||
}
|
||||
}
|
||||
|
||||
func WithUIA2Port(port int) AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.UIA2Port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithAdbLogOn(logOn bool) AndroidDeviceOption {
|
||||
return func(device *AndroidDeviceConfig) {
|
||||
return func(device *AndroidDeviceOptions) {
|
||||
device.LogOn = logOn
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package option
|
||||
|
||||
type HarmonyDeviceConfig struct {
|
||||
type HarmonyDeviceOptions struct {
|
||||
ConnectKey string `json:"connect_key,omitempty" yaml:"connect_key,omitempty"`
|
||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
||||
}
|
||||
|
||||
func (dev *HarmonyDeviceConfig) Options() (deviceOptions []HarmonyDeviceOption) {
|
||||
func (dev *HarmonyDeviceOptions) Options() (deviceOptions []HarmonyDeviceOption) {
|
||||
if dev.ConnectKey != "" {
|
||||
deviceOptions = append(deviceOptions, WithConnectKey(dev.ConnectKey))
|
||||
}
|
||||
@@ -15,24 +15,24 @@ func (dev *HarmonyDeviceConfig) Options() (deviceOptions []HarmonyDeviceOption)
|
||||
return
|
||||
}
|
||||
|
||||
func NewHarmonyDeviceConfig(opts ...HarmonyDeviceOption) (device *HarmonyDeviceConfig) {
|
||||
device = &HarmonyDeviceConfig{}
|
||||
func NewHarmonyDeviceOptions(opts ...HarmonyDeviceOption) (device *HarmonyDeviceOptions) {
|
||||
device = &HarmonyDeviceOptions{}
|
||||
for _, option := range opts {
|
||||
option(device)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type HarmonyDeviceOption func(*HarmonyDeviceConfig)
|
||||
type HarmonyDeviceOption func(*HarmonyDeviceOptions)
|
||||
|
||||
func WithConnectKey(connectKey string) HarmonyDeviceOption {
|
||||
return func(device *HarmonyDeviceConfig) {
|
||||
return func(device *HarmonyDeviceOptions) {
|
||||
device.ConnectKey = connectKey
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogOn(logOn bool) HarmonyDeviceOption {
|
||||
return func(device *HarmonyDeviceConfig) {
|
||||
return func(device *HarmonyDeviceOptions) {
|
||||
device.LogOn = logOn
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package option
|
||||
|
||||
type IOSDeviceConfig struct {
|
||||
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
|
||||
Port int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
|
||||
MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port
|
||||
STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub
|
||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
||||
type IOSDeviceOptions struct {
|
||||
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
|
||||
WDAPort int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
|
||||
WDAMjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port
|
||||
STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub
|
||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
||||
|
||||
// switch to iOS springboard before init WDA session
|
||||
ResetHomeOnStartup bool `json:"reset_home_on_startup,omitempty" yaml:"reset_home_on_startup,omitempty"`
|
||||
@@ -16,15 +16,15 @@ type IOSDeviceConfig struct {
|
||||
DismissAlertButtonSelector string `json:"dismiss_alert_button_selector,omitempty" yaml:"dismiss_alert_button_selector,omitempty"`
|
||||
}
|
||||
|
||||
func (dev *IOSDeviceConfig) Options() (deviceOptions []IOSDeviceOption) {
|
||||
func (dev *IOSDeviceOptions) Options() (deviceOptions []IOSDeviceOption) {
|
||||
if dev.UDID != "" {
|
||||
deviceOptions = append(deviceOptions, WithUDID(dev.UDID))
|
||||
}
|
||||
if dev.Port != 0 {
|
||||
deviceOptions = append(deviceOptions, WithWDAPort(dev.Port))
|
||||
if dev.WDAPort != 0 {
|
||||
deviceOptions = append(deviceOptions, WithWDAPort(dev.WDAPort))
|
||||
}
|
||||
if dev.MjpegPort != 0 {
|
||||
deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.MjpegPort))
|
||||
if dev.WDAMjpegPort != 0 {
|
||||
deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.WDAMjpegPort))
|
||||
}
|
||||
if dev.STUB {
|
||||
deviceOptions = append(deviceOptions, WithIOSStub(true))
|
||||
@@ -47,66 +47,108 @@ func (dev *IOSDeviceConfig) Options() (deviceOptions []IOSDeviceOption) {
|
||||
return
|
||||
}
|
||||
|
||||
func NewIOSDeviceConfig(opts ...IOSDeviceOption) *IOSDeviceConfig {
|
||||
config := &IOSDeviceConfig{}
|
||||
const (
|
||||
defaultWDAPort = 8100
|
||||
defaultMjpegPort = 9100
|
||||
)
|
||||
|
||||
const (
|
||||
// Changes the value of maximum depth for traversing elements source tree.
|
||||
// It may help to prevent out of memory or timeout errors while getting the elements source tree,
|
||||
// but it might restrict the depth of source tree.
|
||||
// A part of elements source tree might be lost if the value was too small. Defaults to 50
|
||||
defaultSnapshotMaxDepth = 10
|
||||
// Allows to customize accept/dismiss alert button selector.
|
||||
// It helps you to handle an arbitrary element as accept button in accept alert command.
|
||||
// The selector should be a valid class chain expression, where the search root is the alert element itself.
|
||||
// The default button location algorithm is used if the provided selector is wrong or does not match any element.
|
||||
// e.g. **/XCUIElementTypeButton[`label CONTAINS[c] ‘accept’`]
|
||||
acceptAlertButtonSelector = "**/XCUIElementTypeButton[`label IN {'允许','好','仅在使用应用期间','稍后再说'}`]"
|
||||
dismissAlertButtonSelector = "**/XCUIElementTypeButton[`label IN {'不允许','暂不'}`]"
|
||||
)
|
||||
|
||||
func NewIOSDeviceOptions(opts ...IOSDeviceOption) *IOSDeviceOptions {
|
||||
config := &IOSDeviceOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(config)
|
||||
}
|
||||
|
||||
if config.WDAPort == 0 {
|
||||
config.WDAPort = defaultWDAPort
|
||||
}
|
||||
if config.WDAMjpegPort == 0 {
|
||||
config.WDAMjpegPort = defaultMjpegPort
|
||||
}
|
||||
|
||||
if config.SnapshotMaxDepth == 0 {
|
||||
config.SnapshotMaxDepth = defaultSnapshotMaxDepth
|
||||
}
|
||||
if config.AcceptAlertButtonSelector == "" {
|
||||
config.AcceptAlertButtonSelector = acceptAlertButtonSelector
|
||||
}
|
||||
if config.DismissAlertButtonSelector == "" {
|
||||
config.DismissAlertButtonSelector = dismissAlertButtonSelector
|
||||
}
|
||||
|
||||
// switch to iOS springboard before init WDA session
|
||||
// avoid getting stuck when some super app is active such as douyin or wexin
|
||||
config.ResetHomeOnStartup = true
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
type IOSDeviceOption func(*IOSDeviceConfig)
|
||||
type IOSDeviceOption func(*IOSDeviceOptions)
|
||||
|
||||
func WithUDID(udid string) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.UDID = udid
|
||||
}
|
||||
}
|
||||
|
||||
func WithWDAPort(port int) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
device.Port = port
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.WDAPort = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithWDAMjpegPort(port int) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
device.MjpegPort = port
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.WDAMjpegPort = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithWDALogOn(logOn bool) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.LogOn = logOn
|
||||
}
|
||||
}
|
||||
|
||||
func WithIOSStub(stub bool) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.STUB = stub
|
||||
}
|
||||
}
|
||||
|
||||
func WithResetHomeOnStartup(reset bool) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.ResetHomeOnStartup = reset
|
||||
}
|
||||
}
|
||||
|
||||
func WithSnapshotMaxDepth(depth int) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.SnapshotMaxDepth = depth
|
||||
}
|
||||
}
|
||||
|
||||
func WithAcceptAlertButtonSelector(selector string) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.AcceptAlertButtonSelector = selector
|
||||
}
|
||||
}
|
||||
|
||||
func WithDismissAlertButtonSelector(selector string) IOSDeviceOption {
|
||||
return func(device *IOSDeviceConfig) {
|
||||
return func(device *IOSDeviceOptions) {
|
||||
device.DismissAlertButtonSelector = selector
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package uixt
|
||||
|
||||
import "math"
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type DeviceStatus struct {
|
||||
Message string `json:"message"`
|
||||
@@ -122,6 +125,34 @@ func (v BatteryState) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
type BatteryStatus int
|
||||
|
||||
const (
|
||||
_ = iota
|
||||
BatteryStatusUnknown BatteryStatus = iota
|
||||
BatteryStatusCharging
|
||||
BatteryStatusDischarging
|
||||
BatteryStatusNotCharging
|
||||
BatteryStatusFull
|
||||
)
|
||||
|
||||
func (bs BatteryStatus) String() string {
|
||||
switch bs {
|
||||
case BatteryStatusUnknown:
|
||||
return "unknown"
|
||||
case BatteryStatusCharging:
|
||||
return "charging"
|
||||
case BatteryStatusDischarging:
|
||||
return "discharging"
|
||||
case BatteryStatusNotCharging:
|
||||
return "not charging"
|
||||
case BatteryStatusFull:
|
||||
return "full"
|
||||
default:
|
||||
return fmt.Sprintf("unknown status code (%d)", bs)
|
||||
}
|
||||
}
|
||||
|
||||
type Size struct {
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
@@ -233,7 +264,7 @@ type Rotation struct {
|
||||
Z int `json:"z"`
|
||||
}
|
||||
|
||||
type Condition func(wd IWebDriver) (bool, error)
|
||||
type Condition func(wd IDriver) (bool, error)
|
||||
|
||||
type Direction string
|
||||
|
||||
|
||||
Reference in New Issue
Block a user