mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-12 16:01:27 +08:00
refactor: api
This commit is contained in:
12
config.go
12
config.go
@@ -119,9 +119,9 @@ func (c *TConfig) SetWebSocket(times, interval, timeout, size int64) *TConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *TConfig) SetIOS(opts ...option.IOSDeviceOption) *TConfig {
|
func (c *TConfig) SetIOS(opts ...option.IOSDeviceOption) *TConfig {
|
||||||
iosOptions := option.NewIOSDeviceConfig(opts...)
|
iosOptions := option.NewIOSDeviceOptions(opts...)
|
||||||
device := &uixt.IOSDevice{
|
device := &uixt.IOSDevice{
|
||||||
IOSDeviceConfig: iosOptions,
|
IOSDeviceOptions: iosOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// each device can have its own settings
|
// each device can have its own settings
|
||||||
@@ -140,9 +140,9 @@ func (c *TConfig) SetIOS(opts ...option.IOSDeviceOption) *TConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *TConfig) SetHarmony(opts ...option.HarmonyDeviceOption) *TConfig {
|
func (c *TConfig) SetHarmony(opts ...option.HarmonyDeviceOption) *TConfig {
|
||||||
harmonyOptions := option.NewHarmonyDeviceConfig(opts...)
|
harmonyOptions := option.NewHarmonyDeviceOptions(opts...)
|
||||||
device := &uixt.HarmonyDevice{
|
device := &uixt.HarmonyDevice{
|
||||||
HarmonyDeviceConfig: harmonyOptions,
|
HarmonyDeviceOptions: harmonyOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// each device can have its own settings
|
// each device can have its own settings
|
||||||
@@ -161,9 +161,9 @@ func (c *TConfig) SetHarmony(opts ...option.HarmonyDeviceOption) *TConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *TConfig) SetAndroid(opts ...option.AndroidDeviceOption) *TConfig {
|
func (c *TConfig) SetAndroid(opts ...option.AndroidDeviceOption) *TConfig {
|
||||||
uiaOptions := option.NewAndroidDeviceConfig(opts...)
|
uiaOptions := option.NewAndroidDeviceOptions(opts...)
|
||||||
device := &uixt.AndroidDevice{
|
device := &uixt.AndroidDevice{
|
||||||
AndroidDeviceConfig: uiaOptions,
|
AndroidDeviceOptions: uiaOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
// each device can have its own settings
|
// each device can have its own settings
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v5.0.0+2502062237
|
v5.0.0+2502071516
|
||||||
|
|||||||
@@ -27,19 +27,11 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
DouyinServerPort = 32316
|
|
||||||
|
|
||||||
// adb server
|
// adb server
|
||||||
AdbServerHost = "localhost"
|
AdbServerHost = "localhost"
|
||||||
AdbServerPort = gadb.AdbServerPort // 5037
|
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"
|
EvalInstallerPackageName = "sogou.mobile.explorer"
|
||||||
InstallViaInstallerCommand = "am start -S -n sogou.mobile.explorer/.PackageInstallerActivity -d"
|
InstallViaInstallerCommand = "am start -S -n sogou.mobile.explorer/.PackageInstallerActivity -d"
|
||||||
)
|
)
|
||||||
@@ -48,13 +40,8 @@ var (
|
|||||||
var evalite embed.FS
|
var evalite embed.FS
|
||||||
|
|
||||||
func NewAndroidDevice(opts ...option.AndroidDeviceOption) (device *AndroidDevice, err error) {
|
func NewAndroidDevice(opts ...option.AndroidDeviceOption) (device *AndroidDevice, err error) {
|
||||||
androidOptions := &option.AndroidDeviceConfig{
|
androidOptions := option.NewAndroidDeviceOptions(opts...)
|
||||||
UIA2IP: UIA2ServerHost,
|
|
||||||
UIA2Port: UIA2ServerPort,
|
|
||||||
}
|
|
||||||
for _, option := range opts {
|
|
||||||
option(androidOptions)
|
|
||||||
}
|
|
||||||
deviceList, err := GetAndroidDevices(androidOptions.SerialNumber)
|
deviceList, err := GetAndroidDevices(androidOptions.SerialNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
||||||
@@ -75,9 +62,9 @@ func NewAndroidDevice(opts ...option.AndroidDeviceOption) (device *AndroidDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
device = &AndroidDevice{
|
device = &AndroidDevice{
|
||||||
AndroidDeviceConfig: androidOptions,
|
Device: dev,
|
||||||
d: dev,
|
AndroidDeviceOptions: androidOptions,
|
||||||
logcat: NewAdbLogcat(device.SerialNumber),
|
Logcat: NewAdbLogcat(androidOptions.SerialNumber),
|
||||||
}
|
}
|
||||||
|
|
||||||
evalToolRaw, err := evalite.ReadFile("evalite")
|
evalToolRaw, err := evalite.ReadFile("evalite")
|
||||||
@@ -126,30 +113,30 @@ func GetAndroidDevices(serial ...string) (devices []*gadb.Device, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AndroidDevice struct {
|
type AndroidDevice struct {
|
||||||
*option.AndroidDeviceConfig
|
*gadb.Device
|
||||||
d *gadb.Device
|
*option.AndroidDeviceOptions
|
||||||
logcat *AdbLogcat
|
Logcat *AdbLogcat
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) Init() error {
|
func (dev *AndroidDevice) Setup() error {
|
||||||
dev.d.RunShellCommand("ime", "enable", UnicodeImePackageName)
|
dev.RunShellCommand("ime", "enable", UnicodeImePackageName)
|
||||||
dev.d.RunShellCommand("rm", "-r", config.DeviceActionLogFilePath)
|
dev.RunShellCommand("rm", "-r", config.DeviceActionLogFilePath)
|
||||||
|
|
||||||
if dev.UIA2 {
|
if dev.UIA2 {
|
||||||
// uiautomator2 server must be started before
|
// uiautomator2 server must be started before
|
||||||
|
|
||||||
// check uiautomator server package installed
|
// check uiautomator server package installed
|
||||||
if !dev.d.IsPackageInstalled(UIA2ServerPackageName) {
|
if !dev.IsPackageInstalled(dev.UIA2ServerPackageName) {
|
||||||
return errors.Wrapf(code.MobileUIDriverAppNotInstalled,
|
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,
|
return errors.Wrapf(code.MobileUIDriverAppNotInstalled,
|
||||||
"%s not installed", UIA2ServerTestPackageName)
|
"%s not installed", dev.UIA2ServerTestPackageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check uiautomator server package running
|
// TODO: check uiautomator server package running
|
||||||
// if dev.d.IsPackageRunning(UIA2ServerPackageName) {
|
// if dev.IsPackageRunning(UIA2ServerPackageName) {
|
||||||
// return nil
|
// return nil
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@@ -164,6 +151,10 @@ func (dev *AndroidDevice) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dev *AndroidDevice) Teardown() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) UUID() string {
|
func (dev *AndroidDevice) UUID() string {
|
||||||
return dev.SerialNumber
|
return dev.SerialNumber
|
||||||
}
|
}
|
||||||
@@ -173,13 +164,13 @@ func (dev *AndroidDevice) LogEnabled() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
|
func (dev *AndroidDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
|
||||||
var driver IWebDriver
|
var driver IDriver
|
||||||
if dev.UIA2 || dev.LogOn {
|
if dev.UIA2 || dev.LogOn {
|
||||||
driver, err = NewUIA2Driver(dev, opts...)
|
driver, err = NewUIA2Driver(dev)
|
||||||
} else if dev.STUB {
|
} else if dev.STUB {
|
||||||
driver, err = NewStubDriver(dev, opts...)
|
driver, err = NewStubDriver(dev)
|
||||||
} else {
|
} else {
|
||||||
driver, err = NewADBDriver(dev, opts...)
|
driver, err = NewADBDriver(dev)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to init UIA driver")
|
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 {
|
func (dev *AndroidDevice) Uninstall(packageName string) error {
|
||||||
_, err := dev.d.RunShellCommand("uninstall", packageName)
|
_, err := dev.RunShellCommand("uninstall", packageName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) Install(apkPath string, opts ...option.InstallOption) error {
|
func (dev *AndroidDevice) Install(apkPath string, opts ...option.InstallOption) error {
|
||||||
installOpts := option.NewInstallOptions(opts...)
|
installOpts := option.NewInstallOptions(opts...)
|
||||||
brand, err := dev.d.Brand()
|
brand, err := dev.Brand()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -245,7 +236,7 @@ func (dev *AndroidDevice) Install(apkPath string, opts ...option.InstallOption)
|
|||||||
case "vivo":
|
case "vivo":
|
||||||
return dev.installVivoSilent(apkPath, args...)
|
return dev.installVivoSilent(apkPath, args...)
|
||||||
case "oppo", "realme", "oneplus":
|
case "oppo", "realme", "oneplus":
|
||||||
if dev.d.IsPackageInstalled(EvalInstallerPackageName) {
|
if dev.IsPackageInstalled(EvalInstallerPackageName) {
|
||||||
return dev.installViaInstaller(apkPath, args...)
|
return dev.installViaInstaller(apkPath, args...)
|
||||||
}
|
}
|
||||||
log.Warn().Msg("oppo not install eval installer")
|
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 = verifyCode[:8]
|
||||||
verifyCode = "-V" + verifyCode
|
verifyCode = "-V" + verifyCode
|
||||||
args = append([]string{verifyCode}, args...)
|
args = append([]string{verifyCode}, args...)
|
||||||
_, err := dev.d.InstallAPK(apkPath, args...)
|
_, err := dev.InstallAPK(apkPath, args...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) error {
|
func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) error {
|
||||||
appRemotePath := "/data/local/tmp/" + strconv.FormatInt(time.Now().UnixMilli(), 10) + ".apk"
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -277,7 +268,7 @@ func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) er
|
|||||||
defer func() {
|
defer func() {
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
logcat := NewAdbLogcatWithCallback(dev.d.Serial(), func(line string) {
|
logcat := NewAdbLogcatWithCallback(dev.Serial(), func(line string) {
|
||||||
re := regexp.MustCompile(`\{.*?}`)
|
re := regexp.MustCompile(`\{.*?}`)
|
||||||
match := re.FindString(line)
|
match := re.FindString(line)
|
||||||
if match == "" {
|
if match == "" {
|
||||||
@@ -307,7 +298,7 @@ func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) er
|
|||||||
// 需要监听是否完成安装
|
// 需要监听是否完成安装
|
||||||
command := strings.Split(InstallViaInstallerCommand, " ")
|
command := strings.Split(InstallViaInstallerCommand, " ")
|
||||||
args = append(command, appRemotePath)
|
args = append(command, appRemotePath)
|
||||||
_, err = dev.d.RunShellCommand("am", args[1:]...)
|
_, err = dev.RunShellCommand("am", args[1:]...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -322,13 +313,13 @@ func (dev *AndroidDevice) installViaInstaller(apkPath string, args ...string) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) installCommon(apkPath string, args ...string) error {
|
func (dev *AndroidDevice) installCommon(apkPath string, args ...string) error {
|
||||||
_, err := dev.d.InstallAPK(apkPath, args...)
|
_, err := dev.InstallAPK(apkPath, args...)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error) {
|
func (dev *AndroidDevice) GetCurrentWindow() (windowInfo WindowInfo, err error) {
|
||||||
// adb shell dumpsys window | grep -E 'mCurrentFocus|mFocusedApp'
|
// 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 {
|
if err != nil {
|
||||||
return WindowInfo{}, errors.Wrap(err, "get current window failed")
|
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
|
// 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 {
|
if err != nil {
|
||||||
return WindowInfo{}, errors.Wrap(err, "get current activity failed")
|
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) {
|
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 {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "get package version failed")
|
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) {
|
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 {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "get package path failed")
|
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) {
|
func (dev *AndroidDevice) getPackageMD5(packagePath string) (string, error) {
|
||||||
output, err := dev.d.RunShellCommand("md5sum", packagePath)
|
output, err := dev.RunShellCommand("md5sum", packagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "get package md5 failed")
|
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 {
|
func (dev *AndroidDevice) startUIA2Server() error {
|
||||||
const maxRetries = 3
|
const maxRetries = 3
|
||||||
for attempt := 1; attempt <= maxRetries; attempt++ {
|
for attempt := 1; attempt <= maxRetries; attempt++ {
|
||||||
log.Info().Str("package", UIA2ServerTestPackageName).
|
log.Info().Str("package", dev.UIA2ServerTestPackageName).
|
||||||
Int("attempt", attempt).Msg("start uiautomator server")
|
Int("attempt", attempt).Msg("start uiautomator server")
|
||||||
// $ adb shell am instrument -w $UIA2ServerTestPackageName
|
// $ adb shell am instrument -w $UIA2ServerTestPackageName
|
||||||
// -w: wait for instrumentation to finish before returning.
|
// -w: wait for instrumentation to finish before returning.
|
||||||
// Required for test runners.
|
// 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 {
|
if err != nil {
|
||||||
return errors.Wrap(err, "start uiautomator server failed")
|
return errors.Wrap(err, "start uiautomator server failed")
|
||||||
}
|
}
|
||||||
@@ -469,7 +460,7 @@ func (dev *AndroidDevice) startUIA2Server() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dev *AndroidDevice) stopUIA2Server() error {
|
func (dev *AndroidDevice) stopUIA2Server() error {
|
||||||
_, err := dev.d.RunShellCommand("am", "force-stop", UIA2ServerPackageName)
|
_, err := dev.RunShellCommand("am", "force-stop", dev.UIA2ServerPackageName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/code"
|
"github.com/httprunner/httprunner/v5/code"
|
||||||
"github.com/httprunner/httprunner/v5/internal/config"
|
"github.com/httprunner/httprunner/v5/internal/config"
|
||||||
"github.com/httprunner/httprunner/v5/internal/utf7"
|
"github.com/httprunner/httprunner/v5/internal/utf7"
|
||||||
"github.com/httprunner/httprunner/v5/pkg/gadb"
|
|
||||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -32,20 +31,18 @@ const (
|
|||||||
UnicodeImePackageName = "io.appium.settings/.UnicodeIME"
|
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")
|
log.Info().Interface("device", device).Msg("init android adb driver")
|
||||||
driver := &ADBDriver{}
|
driver := &ADBDriver{}
|
||||||
driver.NewSession(nil)
|
driver.NewSession(nil)
|
||||||
driver.adbClient = device.d
|
driver.Device = device.Device
|
||||||
driver.logcat = device.logcat
|
driver.Logcat = device.Logcat
|
||||||
return driver, nil
|
return driver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ADBDriver struct {
|
type ADBDriver struct {
|
||||||
DriverClient
|
*AndroidDevice
|
||||||
|
*DriverClient
|
||||||
adbClient *gadb.Device
|
|
||||||
logcat *AdbLogcat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, err error) {
|
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
|
// adb shell screencap -p
|
||||||
if cmd == "screencap" {
|
if cmd == "screencap" {
|
||||||
resp, err := ad.adbClient.ScreenCap()
|
resp, err := ad.Device.ScreenCap()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
driverResult.ResponseBody = "OMITTED"
|
driverResult.ResponseBody = "OMITTED"
|
||||||
return string(resp), nil
|
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")
|
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)
|
driverResult.ResponseBody = strings.TrimSpace(output)
|
||||||
return output, err
|
return output, err
|
||||||
}
|
}
|
||||||
@@ -792,7 +789,7 @@ func (ad *ADBDriver) IsHealthy() (healthy bool, err error) {
|
|||||||
func (ad *ADBDriver) StartCaptureLog(identifier ...string) (err error) {
|
func (ad *ADBDriver) StartCaptureLog(identifier ...string) (err error) {
|
||||||
log.Info().Msg("start adb log recording")
|
log.Info().Msg("start adb log recording")
|
||||||
// start logcat
|
// start logcat
|
||||||
err = ad.logcat.CatchLogcat("iesqaMonitor:V")
|
err = ad.Logcat.CatchLogcat("iesqaMonitor:V")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(code.DeviceCaptureLogError,
|
err = errors.Wrap(code.DeviceCaptureLogError,
|
||||||
fmt.Sprintf("start adb log recording failed: %v", err))
|
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) {
|
func (ad *ADBDriver) StopCaptureLog() (result interface{}, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Info().Msg("stop adb log recording")
|
log.Info().Msg("stop adb log recording")
|
||||||
err = ad.logcat.Stop()
|
err = ad.Logcat.Stop()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to get adb log recording")
|
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 {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to close adb log writer")
|
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 {
|
if len(pointRes) == 0 {
|
||||||
log.Info().Msg("action log is null, use action file >>>")
|
log.Info().Msg("action log is null, use action file >>>")
|
||||||
logFilePathPrefix := fmt.Sprintf("%v/data", config.ActionLogFilePath)
|
logFilePathPrefix := fmt.Sprintf("%v/data", config.ActionLogFilePath)
|
||||||
files := []string{}
|
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 {
|
err = filepath.Walk(config.ActionLogFilePath, func(path string, info fs.FileInfo, err error) error {
|
||||||
// 只是需要日志文件
|
// 只是需要日志文件
|
||||||
if ok := strings.Contains(path, logFilePathPrefix); ok {
|
if ok := strings.Contains(path, logFilePathPrefix); ok {
|
||||||
@@ -898,7 +895,7 @@ func (ad *ADBDriver) SetIme(imeRegx string) error {
|
|||||||
if ime == "" {
|
if ime == "" {
|
||||||
return fmt.Errorf("failed to set ime by %s, ime list: %v", imeRegx, imeList)
|
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]
|
packageName := strings.Split(ime, "/")[0]
|
||||||
res, err := ad.runShellCommand("ime", "set", ime)
|
res, err := ad.runShellCommand("ime", "set", ime)
|
||||||
log.Info().Str("funcName", "SetIme").Interface("ime", 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
|
// scrcpy -s 7d21bb91 --record=file.mp4 -N
|
||||||
cmd := exec.Command(
|
cmd := exec.Command(
|
||||||
"scrcpy",
|
"scrcpy",
|
||||||
"-s", ad.adbClient.Serial(),
|
"-s", ad.Device.Serial(),
|
||||||
fmt.Sprintf("--record=%s", fileName),
|
fmt.Sprintf("--record=%s", fileName),
|
||||||
"-N",
|
"-N",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,17 +17,20 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
"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) {
|
func NewStubDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) {
|
||||||
socketLocalPort, err := device.d.Forward(StubSocketName)
|
socketLocalPort, err := device.Forward(StubSocketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||||
fmt.Sprintf("forward port %d->%s failed: %v",
|
fmt.Sprintf("forward port %d->%s failed: %v",
|
||||||
socketLocalPort, StubSocketName, err))
|
socketLocalPort, StubSocketName, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
serverLocalPort, err := device.d.Forward(DouyinServerPort)
|
serverLocalPort, err := device.Forward(DouyinServerPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
fmt.Sprintf("forward port %d->%d failed: %v",
|
||||||
@@ -53,16 +56,16 @@ func NewStubDriver(device *AndroidDevice, opts ...option.DriverOption) (driver *
|
|||||||
}
|
}
|
||||||
|
|
||||||
driver.NewSession(nil)
|
driver.NewSession(nil)
|
||||||
driver.adbClient = device.d
|
driver.Device = device.Device
|
||||||
driver.logcat = device.logcat
|
driver.Logcat = device.Logcat
|
||||||
return driver, nil
|
return driver, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type StubAndroidDriver struct {
|
type StubAndroidDriver struct {
|
||||||
|
*ADBDriver
|
||||||
socket net.Conn
|
socket net.Conn
|
||||||
seq int
|
seq int
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
ADBDriver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppLoginInfo struct {
|
type AppLoginInfo struct {
|
||||||
@@ -85,7 +88,7 @@ func (sad *StubAndroidDriver) httpGET(pathElem ...string) (rawResp rawResponse,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("adb forward: %w", err)
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("adb forward: %w", err)
|
return nil, fmt.Errorf("adb forward: %w", err)
|
||||||
}
|
}
|
||||||
sad.client = convertToHTTPClient(conn)
|
sad.Client = convertToHTTPClient(conn)
|
||||||
|
|
||||||
var bsJSON []byte = nil
|
var bsJSON []byte = nil
|
||||||
if data != nil {
|
if data != nil {
|
||||||
@@ -137,7 +140,7 @@ func (sad *StubAndroidDriver) sendCommand(packageName string, cmdType string, pa
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := sad.adbClient.RunStubCommand(append(data, '\n'), packageName)
|
res, err := sad.Device.RunStubCommand(append(data, '\n'), packageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -266,7 +269,7 @@ func (sad *StubAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sad *StubAndroidDriver) SetHDTStatus(status bool) error {
|
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 {
|
if err != nil {
|
||||||
log.Warn().Msg(fmt.Sprintf("failed to disable sso, error: %v", err))
|
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-"
|
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")
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||||
fmt.Sprintf("forward port %d->%d failed: %v",
|
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)
|
return nil, fmt.Errorf("adb forward: %w", err)
|
||||||
}
|
}
|
||||||
driver := new(UIA2Driver)
|
driver := new(UIA2Driver)
|
||||||
driver.client = convertToHTTPClient(conn)
|
driver.Client = convertToHTTPClient(conn)
|
||||||
driver.adbClient = device.d
|
driver.Device = device.Device
|
||||||
driver.logcat = device.logcat
|
driver.Logcat = device.Logcat
|
||||||
|
|
||||||
_, err = driver.NewSession(nil)
|
_, err = driver.NewSession(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -48,35 +48,7 @@ func NewUIA2Driver(device *AndroidDevice, opts ...option.DriverOption) (*UIA2Dri
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UIA2Driver struct {
|
type UIA2Driver struct {
|
||||||
ADBDriver
|
*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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ud *UIA2Driver) resetDriver() error {
|
func (ud *UIA2Driver) resetDriver() error {
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import (
|
|||||||
|
|
||||||
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
|
// current implemeted device: IOSDevice, AndroidDevice, HarmonyDevice
|
||||||
type IDevice interface {
|
type IDevice interface {
|
||||||
Init() error // init android device
|
Setup() error
|
||||||
|
Teardown() error
|
||||||
|
|
||||||
UUID() string // ios udid or android serial
|
UUID() string // ios udid or android serial
|
||||||
LogEnabled() bool
|
LogEnabled() bool
|
||||||
|
|
||||||
// TODO: add ctx to NewDriver
|
// TODO: remove
|
||||||
NewDriver(...option.DriverOption) (driverExt *DriverExt, err error)
|
NewDriver(...option.DriverOption) (driverExt *DriverExt, err error)
|
||||||
|
|
||||||
Install(appPath string, opts ...option.InstallOption) error
|
Install(appPath string, opts ...option.InstallOption) error
|
||||||
@@ -18,10 +20,4 @@ type IDevice interface {
|
|||||||
|
|
||||||
GetPackageInfo(packageName string) (AppInfo, error)
|
GetPackageInfo(packageName string) (AppInfo, error)
|
||||||
GetCurrentWindow() (windowInfo WindowInfo, err 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"
|
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IWebDriver defines methods supported by IWebDriver drivers.
|
// current implemeted driver: ADBDriver, UIA2Driver, WDADriver, HDCDriver
|
||||||
type IWebDriver interface {
|
type IDriver interface {
|
||||||
// NewSession starts a new session and returns the SessionInfo.
|
// NewSession starts a new session and returns the SessionInfo.
|
||||||
NewSession(capabilities option.Capabilities) (SessionInfo, error)
|
NewSession(capabilities option.Capabilities) (SessionInfo, error)
|
||||||
|
|
||||||
@@ -238,7 +238,7 @@ type DriverResult struct {
|
|||||||
|
|
||||||
type DriverClient struct {
|
type DriverClient struct {
|
||||||
urlPrefix *url.URL
|
urlPrefix *url.URL
|
||||||
client *http.Client
|
Client *http.Client
|
||||||
|
|
||||||
// cache to avoid repeated query
|
// cache to avoid repeated query
|
||||||
scale float64
|
scale float64
|
||||||
@@ -323,7 +323,7 @@ func (wd *DriverClient) Request(method string, rawURL string, rawBody []byte) (r
|
|||||||
|
|
||||||
driverResult.RequestTime = time.Now()
|
driverResult.RequestTime = time.Now()
|
||||||
var resp *http.Response
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
@@ -371,14 +371,14 @@ func convertToHTTPClient(conn net.Conn) *http.Client {
|
|||||||
type DriverExt struct {
|
type DriverExt struct {
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
Device IDevice
|
Device IDevice
|
||||||
Driver IWebDriver
|
Driver IDriver
|
||||||
ImageService IImageService // used to extract image data
|
ImageService IImageService // used to extract image data
|
||||||
|
|
||||||
// funplugin
|
// funplugin
|
||||||
plugin funplugin.IPlugin
|
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...)
|
options := option.NewDriverOptions(opts...)
|
||||||
|
|
||||||
dExt = &DriverExt{
|
dExt = &DriverExt{
|
||||||
@@ -404,7 +404,7 @@ func newDriverExt(device IDevice, driver IWebDriver, opts ...option.DriverOption
|
|||||||
return dExt, nil
|
return dExt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dExt *DriverExt) Init() error {
|
func (dExt *DriverExt) Setup() error {
|
||||||
// unlock device screen
|
// unlock device screen
|
||||||
err := dExt.Driver.Unlock()
|
err := dExt.Driver.Unlock()
|
||||||
if err != nil {
|
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 {
|
type HarmonyDevice struct {
|
||||||
*option.HarmonyDeviceConfig
|
*ghdc.Device
|
||||||
d *ghdc.Device
|
*option.HarmonyDeviceOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice, err error) {
|
func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice, err error) {
|
||||||
deviceConfig := option.NewHarmonyDeviceConfig(opts...)
|
deviceConfig := option.NewHarmonyDeviceOptions(opts...)
|
||||||
|
|
||||||
deviceList, err := GetHarmonyDevices(deviceConfig.ConnectKey)
|
deviceList, err := GetHarmonyDevices(deviceConfig.ConnectKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
||||||
@@ -43,8 +42,8 @@ func NewHarmonyDevice(opts ...option.HarmonyDeviceOption) (device *HarmonyDevice
|
|||||||
}
|
}
|
||||||
|
|
||||||
device = &HarmonyDevice{
|
device = &HarmonyDevice{
|
||||||
HarmonyDeviceConfig: deviceConfig,
|
HarmonyDeviceOptions: deviceConfig,
|
||||||
d: dev,
|
Device: dev,
|
||||||
}
|
}
|
||||||
log.Info().Str("connectKey", device.ConnectKey).Msg("init harmony device")
|
log.Info().Str("connectKey", device.ConnectKey).Msg("init harmony device")
|
||||||
return device, nil
|
return device, nil
|
||||||
@@ -83,7 +82,11 @@ func GetHarmonyDevices(serial ...string) (devices []*ghdc.Device, err error) {
|
|||||||
return devices, nil
|
return devices, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *HarmonyDevice) Init() error {
|
func (dev *HarmonyDevice) Setup() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dev *HarmonyDevice) Teardown() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +99,7 @@ func (dev *HarmonyDevice) LogEnabled() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dev *HarmonyDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
|
func (dev *HarmonyDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverExt, err error) {
|
||||||
driver, err := newHarmonyDriver(dev.d)
|
driver, err := newHarmonyDriver(dev.Device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to new harmony driver")
|
log.Error().Err(err).Msg("failed to new harmony driver")
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -110,8 +113,8 @@ func (dev *HarmonyDevice) NewDriver(opts ...option.DriverOption) (driverExt *Dri
|
|||||||
return driverExt, nil
|
return driverExt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *HarmonyDevice) NewUSBDriver(opts ...option.DriverOption) (driver IWebDriver, err error) {
|
func (dev *HarmonyDevice) NewUSBDriver(opts ...option.DriverOption) (driver IDriver, err error) {
|
||||||
harmonyDriver, err := newHarmonyDriver(dev.d)
|
harmonyDriver, err := newHarmonyDriver(dev.Device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to new harmony driver")
|
log.Error().Err(err).Msg("failed to new harmony driver")
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type hdcDriver struct {
|
type hdcDriver struct {
|
||||||
points []ExportPoint
|
*HarmonyDevice
|
||||||
DriverClient
|
*DriverClient
|
||||||
device *ghdc.Device
|
points []ExportPoint
|
||||||
uiDriver *ghdc.UIDriver
|
uiDriver *ghdc.UIDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ const (
|
|||||||
|
|
||||||
func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) {
|
func newHarmonyDriver(device *ghdc.Device) (driver *hdcDriver, err error) {
|
||||||
driver = new(hdcDriver)
|
driver = new(hdcDriver)
|
||||||
driver.device = device
|
driver.Device = device
|
||||||
uiDriver, err := ghdc.NewUIDriver(*device)
|
uiDriver, err := ghdc.NewUIDriver(*device)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to new harmony ui driver")
|
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) {
|
func (hd *hdcDriver) Unlock() (err error) {
|
||||||
// Todo 检查是否锁屏 hdc shell hidumper -s RenderService -a screen
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ func (hd *hdcDriver) AppLaunch(packageName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (hd *hdcDriver) AppTerminate(packageName string) (bool, 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 {
|
if err != nil {
|
||||||
log.Error().Err(err).Msg("failed to terminal app")
|
log.Error().Err(err).Msg("failed to terminal app")
|
||||||
return false, err
|
return false, err
|
||||||
|
|||||||
@@ -31,28 +31,6 @@ import (
|
|||||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
"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
|
var tunnelManager *tunnel.TunnelManager = nil
|
||||||
|
|
||||||
func GetIOSDevices(udid ...string) (deviceList []ios.DeviceEntry, err error) {
|
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) {
|
func NewIOSDevice(opts ...option.IOSDeviceOption) (device *IOSDevice, err error) {
|
||||||
deviceOptions := &option.IOSDeviceConfig{
|
deviceOptions := option.NewIOSDeviceOptions(opts...)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceList, err := GetIOSDevices(deviceOptions.UDID)
|
deviceList, err := GetIOSDevices(deviceOptions.UDID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
return nil, errors.Wrap(code.DeviceConnectionError, err.Error())
|
||||||
@@ -160,12 +125,12 @@ func NewIOSDevice(opts ...option.IOSDeviceOption) (device *IOSDevice, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
device = &IOSDevice{
|
device = &IOSDevice{
|
||||||
IOSDeviceConfig: deviceOptions,
|
IOSDeviceOptions: deviceOptions,
|
||||||
listeners: make(map[int]*forward.ConnListener),
|
listeners: make(map[int]*forward.ConnListener),
|
||||||
d: dev,
|
d: dev,
|
||||||
}
|
}
|
||||||
log.Info().Str("udid", device.UDID).Msg("init ios device")
|
log.Info().Str("udid", device.UDID).Msg("init ios device")
|
||||||
err = device.Init()
|
err = device.Setup()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = device.Teardown()
|
_ = device.Teardown()
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -174,7 +139,7 @@ func NewIOSDevice(opts ...option.IOSDeviceOption) (device *IOSDevice, err error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
type IOSDevice struct {
|
type IOSDevice struct {
|
||||||
*option.IOSDeviceConfig
|
*option.IOSDeviceOptions
|
||||||
d ios.DeviceEntry
|
d ios.DeviceEntry
|
||||||
listeners map[int]*forward.ConnListener
|
listeners map[int]*forward.ConnListener
|
||||||
}
|
}
|
||||||
@@ -207,7 +172,7 @@ const (
|
|||||||
ApplicationTypeAny ApplicationType = "Any"
|
ApplicationTypeAny ApplicationType = "Any"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (dev *IOSDevice) Init() error {
|
func (dev *IOSDevice) Setup() error {
|
||||||
images, err := dev.ListImages()
|
images, err := dev.ListImages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -270,7 +235,7 @@ func (dev *IOSDevice) NewDriver(opts ...option.DriverOption) (driverExt *DriverE
|
|||||||
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
|
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
|
||||||
}
|
}
|
||||||
|
|
||||||
var driver IWebDriver
|
var driver IDriver
|
||||||
if dev.STUB {
|
if dev.STUB {
|
||||||
driver, err = dev.NewStubDriver()
|
driver, err = dev.NewStubDriver()
|
||||||
if err != nil {
|
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.
|
// 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
|
var localPort int
|
||||||
localPort, err = strconv.Atoi(os.Getenv("WDA_LOCAL_PORT"))
|
localPort, err = strconv.Atoi(os.Getenv("WDA_LOCAL_PORT"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -570,7 +535,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IW
|
|||||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||||
fmt.Sprintf("get free port failed: %v", err))
|
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,
|
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
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,
|
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||||
fmt.Sprintf("get free port failed: %v", err))
|
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,
|
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
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")
|
Msg("init WDA HTTP driver")
|
||||||
|
|
||||||
wd := new(wdaDriver)
|
wd := new(wdaDriver)
|
||||||
wd.device = dev
|
wd.IOSDevice = dev
|
||||||
wd.udid = dev.UDID
|
wd.udid = dev.UDID
|
||||||
wd.client = &http.Client{
|
wd.Client = &http.Client{
|
||||||
Timeout: time.Second * 10, // 设置超时时间为 10 秒
|
Timeout: time.Second * 10, // 设置超时时间为 10 秒
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,7 +599,12 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver IW
|
|||||||
return wd, nil
|
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()
|
localStubPort, err := builtin.GetFreePort()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
type stubIOSDriver struct {
|
type stubIOSDriver struct {
|
||||||
*wdaDriver
|
*wdaDriver
|
||||||
DriverClient
|
|
||||||
|
|
||||||
bightInsightPrefix string
|
bightInsightPrefix string
|
||||||
serverPrefix string
|
serverPrefix string
|
||||||
@@ -32,7 +31,7 @@ func newStubIOSDriver(bightInsightAddr, serverAddr string, dev *IOSDevice, readT
|
|||||||
driver.bightInsightPrefix = bightInsightAddr
|
driver.bightInsightPrefix = bightInsightAddr
|
||||||
driver.serverPrefix = serverAddr
|
driver.serverPrefix = serverAddr
|
||||||
driver.timeout = timeout
|
driver.timeout = timeout
|
||||||
driver.DriverClient.client = &http.Client{
|
driver.DriverClient.Client = &http.Client{
|
||||||
Timeout: time.Second * 10, // 设置超时时间为 10 秒
|
Timeout: time.Second * 10, // 设置超时时间为 10 秒
|
||||||
}
|
}
|
||||||
return driver, nil
|
return driver, nil
|
||||||
@@ -516,7 +515,7 @@ func (s *stubIOSDriver) LogoutNoneUI(packageName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *stubIOSDriver) TearDown() error {
|
func (s *stubIOSDriver) TearDown() error {
|
||||||
s.DriverClient.client.CloseIdleConnections()
|
s.DriverClient.Client.CloseIdleConnections()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
iOSStubDriver IWebDriver
|
iOSStubDriver IDriver
|
||||||
iOSDevice *IOSDevice
|
iOSDevice *IOSDevice
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type wdaDriver struct {
|
type wdaDriver struct {
|
||||||
DriverClient
|
*IOSDevice
|
||||||
|
*DriverClient
|
||||||
udid string
|
udid string
|
||||||
device *IOSDevice
|
|
||||||
mjpegHTTPConn net.Conn // via HTTP
|
mjpegHTTPConn net.Conn // via HTTP
|
||||||
mjpegClient *http.Client
|
mjpegClient *http.Client
|
||||||
mjpegUrl string
|
mjpegUrl string
|
||||||
@@ -467,7 +467,7 @@ func (wd *wdaDriver) GetForegroundApp() (appInfo AppInfo, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return appInfo, err
|
return appInfo, err
|
||||||
}
|
}
|
||||||
apps, err := wd.device.ListApps(ApplicationTypeAny)
|
apps, err := wd.ListApps(ApplicationTypeAny)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return appInfo, err
|
return appInfo, err
|
||||||
}
|
}
|
||||||
@@ -1021,7 +1021,7 @@ func (wd *wdaDriver) GetDriverResults() []*DriverResult {
|
|||||||
|
|
||||||
func (wd *wdaDriver) TearDown() error {
|
func (wd *wdaDriver) TearDown() error {
|
||||||
wd.mjpegClient.CloseIdleConnections()
|
wd.mjpegClient.CloseIdleConnections()
|
||||||
wd.client.CloseIdleConnections()
|
wd.Client.CloseIdleConnections()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
bundleId = "com.apple.Preferences"
|
bundleId = "com.apple.Preferences"
|
||||||
driver IWebDriver
|
driver IDriver
|
||||||
iOSDriverExt *DriverExt
|
iOSDriverExt *DriverExt
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,31 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
type AndroidDeviceConfig struct {
|
import "github.com/httprunner/httprunner/v5/pkg/gadb"
|
||||||
|
|
||||||
|
type AndroidDeviceOptions struct {
|
||||||
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
|
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
|
||||||
STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub
|
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
|
|
||||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
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 != "" {
|
if dev.SerialNumber != "" {
|
||||||
deviceOptions = append(deviceOptions, WithSerialNumber(dev.SerialNumber))
|
deviceOptions = append(deviceOptions, WithSerialNumber(dev.SerialNumber))
|
||||||
}
|
}
|
||||||
|
if dev.STUB {
|
||||||
|
deviceOptions = append(deviceOptions, WithStub(true))
|
||||||
|
}
|
||||||
if dev.UIA2 {
|
if dev.UIA2 {
|
||||||
deviceOptions = append(deviceOptions, WithUIA2(true))
|
deviceOptions = append(deviceOptions, WithUIA2(true))
|
||||||
}
|
}
|
||||||
@@ -28,54 +41,87 @@ func (dev *AndroidDeviceConfig) Options() (deviceOptions []AndroidDeviceOption)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAndroidDeviceConfig(opts ...AndroidDeviceOption) *AndroidDeviceConfig {
|
const (
|
||||||
config := &AndroidDeviceConfig{}
|
// 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 {
|
for _, opt := range opts {
|
||||||
opt(config)
|
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
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
type AndroidDeviceOption func(*AndroidDeviceConfig)
|
type AndroidDeviceOption func(*AndroidDeviceOptions)
|
||||||
|
|
||||||
func WithDriverTypeADB() AndroidDeviceOption {
|
func WithDriverTypeADB() AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.STUB = false
|
device.STUB = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithSerialNumber(serial string) AndroidDeviceOption {
|
func WithSerialNumber(serial string) AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.SerialNumber = serial
|
device.SerialNumber = serial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithUIA2(uia2On bool) AndroidDeviceOption {
|
func WithUIA2(uia2On bool) AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.UIA2 = uia2On
|
device.UIA2 = uia2On
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithStub(stubOn bool) AndroidDeviceOption {
|
func WithStub(stubOn bool) AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.STUB = stubOn
|
device.STUB = stubOn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithUIA2IP(ip string) AndroidDeviceOption {
|
func WithUIA2IP(ip string) AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.UIA2IP = ip
|
device.UIA2IP = ip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithUIA2Port(port int) AndroidDeviceOption {
|
func WithUIA2Port(port int) AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.UIA2Port = port
|
device.UIA2Port = port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithAdbLogOn(logOn bool) AndroidDeviceOption {
|
func WithAdbLogOn(logOn bool) AndroidDeviceOption {
|
||||||
return func(device *AndroidDeviceConfig) {
|
return func(device *AndroidDeviceOptions) {
|
||||||
device.LogOn = logOn
|
device.LogOn = logOn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
type HarmonyDeviceConfig struct {
|
type HarmonyDeviceOptions struct {
|
||||||
ConnectKey string `json:"connect_key,omitempty" yaml:"connect_key,omitempty"`
|
ConnectKey string `json:"connect_key,omitempty" yaml:"connect_key,omitempty"`
|
||||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,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 != "" {
|
if dev.ConnectKey != "" {
|
||||||
deviceOptions = append(deviceOptions, WithConnectKey(dev.ConnectKey))
|
deviceOptions = append(deviceOptions, WithConnectKey(dev.ConnectKey))
|
||||||
}
|
}
|
||||||
@@ -15,24 +15,24 @@ func (dev *HarmonyDeviceConfig) Options() (deviceOptions []HarmonyDeviceOption)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHarmonyDeviceConfig(opts ...HarmonyDeviceOption) (device *HarmonyDeviceConfig) {
|
func NewHarmonyDeviceOptions(opts ...HarmonyDeviceOption) (device *HarmonyDeviceOptions) {
|
||||||
device = &HarmonyDeviceConfig{}
|
device = &HarmonyDeviceOptions{}
|
||||||
for _, option := range opts {
|
for _, option := range opts {
|
||||||
option(device)
|
option(device)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type HarmonyDeviceOption func(*HarmonyDeviceConfig)
|
type HarmonyDeviceOption func(*HarmonyDeviceOptions)
|
||||||
|
|
||||||
func WithConnectKey(connectKey string) HarmonyDeviceOption {
|
func WithConnectKey(connectKey string) HarmonyDeviceOption {
|
||||||
return func(device *HarmonyDeviceConfig) {
|
return func(device *HarmonyDeviceOptions) {
|
||||||
device.ConnectKey = connectKey
|
device.ConnectKey = connectKey
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithLogOn(logOn bool) HarmonyDeviceOption {
|
func WithLogOn(logOn bool) HarmonyDeviceOption {
|
||||||
return func(device *HarmonyDeviceConfig) {
|
return func(device *HarmonyDeviceOptions) {
|
||||||
device.LogOn = logOn
|
device.LogOn = logOn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package option
|
package option
|
||||||
|
|
||||||
type IOSDeviceConfig struct {
|
type IOSDeviceOptions struct {
|
||||||
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
|
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
|
||||||
Port int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
|
WDAPort int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
|
||||||
MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG 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
|
STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub
|
||||||
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
|
||||||
|
|
||||||
// switch to iOS springboard before init WDA session
|
// switch to iOS springboard before init WDA session
|
||||||
ResetHomeOnStartup bool `json:"reset_home_on_startup,omitempty" yaml:"reset_home_on_startup,omitempty"`
|
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"`
|
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 != "" {
|
if dev.UDID != "" {
|
||||||
deviceOptions = append(deviceOptions, WithUDID(dev.UDID))
|
deviceOptions = append(deviceOptions, WithUDID(dev.UDID))
|
||||||
}
|
}
|
||||||
if dev.Port != 0 {
|
if dev.WDAPort != 0 {
|
||||||
deviceOptions = append(deviceOptions, WithWDAPort(dev.Port))
|
deviceOptions = append(deviceOptions, WithWDAPort(dev.WDAPort))
|
||||||
}
|
}
|
||||||
if dev.MjpegPort != 0 {
|
if dev.WDAMjpegPort != 0 {
|
||||||
deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.MjpegPort))
|
deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.WDAMjpegPort))
|
||||||
}
|
}
|
||||||
if dev.STUB {
|
if dev.STUB {
|
||||||
deviceOptions = append(deviceOptions, WithIOSStub(true))
|
deviceOptions = append(deviceOptions, WithIOSStub(true))
|
||||||
@@ -47,66 +47,108 @@ func (dev *IOSDeviceConfig) Options() (deviceOptions []IOSDeviceOption) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIOSDeviceConfig(opts ...IOSDeviceOption) *IOSDeviceConfig {
|
const (
|
||||||
config := &IOSDeviceConfig{}
|
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 {
|
for _, opt := range opts {
|
||||||
opt(config)
|
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
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
type IOSDeviceOption func(*IOSDeviceConfig)
|
type IOSDeviceOption func(*IOSDeviceOptions)
|
||||||
|
|
||||||
func WithUDID(udid string) IOSDeviceOption {
|
func WithUDID(udid string) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.UDID = udid
|
device.UDID = udid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithWDAPort(port int) IOSDeviceOption {
|
func WithWDAPort(port int) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.Port = port
|
device.WDAPort = port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithWDAMjpegPort(port int) IOSDeviceOption {
|
func WithWDAMjpegPort(port int) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.MjpegPort = port
|
device.WDAMjpegPort = port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithWDALogOn(logOn bool) IOSDeviceOption {
|
func WithWDALogOn(logOn bool) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.LogOn = logOn
|
device.LogOn = logOn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithIOSStub(stub bool) IOSDeviceOption {
|
func WithIOSStub(stub bool) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.STUB = stub
|
device.STUB = stub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithResetHomeOnStartup(reset bool) IOSDeviceOption {
|
func WithResetHomeOnStartup(reset bool) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.ResetHomeOnStartup = reset
|
device.ResetHomeOnStartup = reset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithSnapshotMaxDepth(depth int) IOSDeviceOption {
|
func WithSnapshotMaxDepth(depth int) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.SnapshotMaxDepth = depth
|
device.SnapshotMaxDepth = depth
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithAcceptAlertButtonSelector(selector string) IOSDeviceOption {
|
func WithAcceptAlertButtonSelector(selector string) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.AcceptAlertButtonSelector = selector
|
device.AcceptAlertButtonSelector = selector
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithDismissAlertButtonSelector(selector string) IOSDeviceOption {
|
func WithDismissAlertButtonSelector(selector string) IOSDeviceOption {
|
||||||
return func(device *IOSDeviceConfig) {
|
return func(device *IOSDeviceOptions) {
|
||||||
device.DismissAlertButtonSelector = selector
|
device.DismissAlertButtonSelector = selector
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package uixt
|
package uixt
|
||||||
|
|
||||||
import "math"
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
type DeviceStatus struct {
|
type DeviceStatus struct {
|
||||||
Message string `json:"message"`
|
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 {
|
type Size struct {
|
||||||
Width int `json:"width"`
|
Width int `json:"width"`
|
||||||
Height int `json:"height"`
|
Height int `json:"height"`
|
||||||
@@ -233,7 +264,7 @@ type Rotation struct {
|
|||||||
Z int `json:"z"`
|
Z int `json:"z"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Condition func(wd IWebDriver) (bool, error)
|
type Condition func(wd IDriver) (bool, error)
|
||||||
|
|
||||||
type Direction string
|
type Direction string
|
||||||
|
|
||||||
|
|||||||
12
runner.go
12
runner.go
@@ -427,14 +427,14 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "init android device failed")
|
return nil, errors.Wrap(err, "init android device failed")
|
||||||
}
|
}
|
||||||
if err := device.Init(); err != nil {
|
if err := device.Setup(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
driver, err := device.NewDriver()
|
driver, err := device.NewDriver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := driver.Init(); err != nil {
|
if err := driver.Setup(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.uixtDrivers[device.SerialNumber] = driver
|
r.uixtDrivers[device.SerialNumber] = driver
|
||||||
@@ -450,14 +450,14 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "init ios device failed")
|
return nil, errors.Wrap(err, "init ios device failed")
|
||||||
}
|
}
|
||||||
if err := device.Init(); err != nil {
|
if err := device.Setup(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
driver, err := device.NewDriver()
|
driver, err := device.NewDriver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := driver.Init(); err != nil {
|
if err := driver.Setup(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.uixtDrivers[device.UDID] = driver
|
r.uixtDrivers[device.UDID] = driver
|
||||||
@@ -473,14 +473,14 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "init harmony device failed")
|
return nil, errors.Wrap(err, "init harmony device failed")
|
||||||
}
|
}
|
||||||
if err := device.Init(); err != nil {
|
if err := device.Setup(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
driver, err := device.NewDriver()
|
driver, err := device.NewDriver()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := driver.Init(); err != nil {
|
if err := driver.Setup(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r.uixtDrivers[device.ConnectKey] = driver
|
r.uixtDrivers[device.ConnectKey] = driver
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func handleDeviceContext() gin.HandlerFunc {
|
|||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
device.Init()
|
device.Setup()
|
||||||
|
|
||||||
driver, err := device.NewDriver(
|
driver, err := device.NewDriver(
|
||||||
option.WithDriverImageService(true),
|
option.WithDriverImageService(true),
|
||||||
|
|||||||
@@ -760,7 +760,7 @@ func (s *StepRequest) MobileUI() *StepMobile {
|
|||||||
|
|
||||||
// Android creates a new android step session
|
// Android creates a new android step session
|
||||||
func (s *StepRequest) Android(opts ...option.AndroidDeviceOption) *StepMobile {
|
func (s *StepRequest) Android(opts ...option.AndroidDeviceOption) *StepMobile {
|
||||||
androidOptions := option.NewAndroidDeviceConfig(opts...)
|
androidOptions := option.NewAndroidDeviceOptions(opts...)
|
||||||
return &StepMobile{
|
return &StepMobile{
|
||||||
StepConfig: s.StepConfig,
|
StepConfig: s.StepConfig,
|
||||||
Android: &MobileUI{
|
Android: &MobileUI{
|
||||||
@@ -771,7 +771,7 @@ func (s *StepRequest) Android(opts ...option.AndroidDeviceOption) *StepMobile {
|
|||||||
|
|
||||||
// IOS creates a new ios step session
|
// IOS creates a new ios step session
|
||||||
func (s *StepRequest) IOS(opts ...option.IOSDeviceOption) *StepMobile {
|
func (s *StepRequest) IOS(opts ...option.IOSDeviceOption) *StepMobile {
|
||||||
iosOptions := option.NewIOSDeviceConfig(opts...)
|
iosOptions := option.NewIOSDeviceOptions(opts...)
|
||||||
return &StepMobile{
|
return &StepMobile{
|
||||||
StepConfig: s.StepConfig,
|
StepConfig: s.StepConfig,
|
||||||
IOS: &MobileUI{
|
IOS: &MobileUI{
|
||||||
@@ -782,7 +782,7 @@ func (s *StepRequest) IOS(opts ...option.IOSDeviceOption) *StepMobile {
|
|||||||
|
|
||||||
// Harmony creates a new harmony step session
|
// Harmony creates a new harmony step session
|
||||||
func (s *StepRequest) Harmony(opts ...option.HarmonyDeviceOption) *StepMobile {
|
func (s *StepRequest) Harmony(opts ...option.HarmonyDeviceOption) *StepMobile {
|
||||||
harmonyOptions := option.NewHarmonyDeviceConfig(opts...)
|
harmonyOptions := option.NewHarmonyDeviceOptions(opts...)
|
||||||
return &StepMobile{
|
return &StepMobile{
|
||||||
StepConfig: s.StepConfig,
|
StepConfig: s.StepConfig,
|
||||||
Harmony: &MobileUI{
|
Harmony: &MobileUI{
|
||||||
|
|||||||
Reference in New Issue
Block a user