mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-21 04:22:30 +08:00
refactor: move shoots driver to driver_ext
This commit is contained in:
257
pkg/uixt/driver_ext/shoots_android_dirver.go
Normal file
257
pkg/uixt/driver_ext/shoots_android_dirver.go
Normal file
@@ -0,0 +1,257 @@
|
||||
package driver_ext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"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"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
|
||||
)
|
||||
|
||||
const (
|
||||
shootsSocketName = "com.bytest.device"
|
||||
douyinServerPort = 32316
|
||||
forwardToPrefix = "forward-to-"
|
||||
)
|
||||
|
||||
func NewShootsAndroidDriver(device *uixt.AndroidDevice) (driver *ShootsAndroidDriver, err error) {
|
||||
socketLocalPort, err := device.Forward(shootsSocketName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceConnectionError,
|
||||
fmt.Sprintf("forward port %d->%s failed: %v",
|
||||
socketLocalPort, shootsSocketName, 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 = &ShootsAndroidDriver{
|
||||
socket: conn,
|
||||
timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
driver.InitSession(nil)
|
||||
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))
|
||||
}
|
||||
rawURL := fmt.Sprintf("http://forward-to-%d:%d",
|
||||
serverLocalPort, douyinServerPort)
|
||||
driver.Session.Init(rawURL)
|
||||
|
||||
driver.Device = device.Device
|
||||
driver.Logcat = device.Logcat
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
type ShootsAndroidDriver struct {
|
||||
*uixt.ADBDriver
|
||||
socket net.Conn
|
||||
seq int
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func (sad *ShootsAndroidDriver) 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 shoots command: %s", resultMap["Error"].(string))
|
||||
}
|
||||
|
||||
return resultMap["Result"], nil
|
||||
}
|
||||
|
||||
func (sad *ShootsAndroidDriver) DeleteSession() error {
|
||||
return sad.close()
|
||||
}
|
||||
|
||||
func (sad *ShootsAndroidDriver) close() error {
|
||||
if sad.socket != nil {
|
||||
return sad.socket.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sad *ShootsAndroidDriver) Status() (types.DeviceStatus, error) {
|
||||
app, err := sad.GetForegroundApp()
|
||||
if err != nil {
|
||||
return types.DeviceStatus{}, err
|
||||
}
|
||||
res, err := sad.sendCommand(app.PackageName, "Hello", nil)
|
||||
if err != nil {
|
||||
return types.DeviceStatus{}, err
|
||||
}
|
||||
log.Info().Msg(fmt.Sprintf("ping shoots result :%v", res))
|
||||
return types.DeviceStatus{}, nil
|
||||
}
|
||||
|
||||
func (sad *ShootsAndroidDriver) 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 *ShootsAndroidDriver) 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.Session.POST(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 *ShootsAndroidDriver) LogoutNoneUI(packageName string) error {
|
||||
resp, err := sad.Session.GET("/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 *ShootsAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string, captcha string) error {
|
||||
params := map[string]interface{}{
|
||||
"ClassName": "qe.python.test.LoginUtil",
|
||||
"Method": "loginSync",
|
||||
"RetType": "",
|
||||
"Args": []string{phoneNumber, captcha},
|
||||
}
|
||||
res, err := sad.sendCommand(packageName, "CallStaticMethod", params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info().Msg(res.(string))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sad *ShootsAndroidDriver) 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 *ShootsAndroidDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) {
|
||||
resp, err := sad.Session.GET("/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
|
||||
}
|
||||
|
||||
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"`
|
||||
}
|
||||
154
pkg/uixt/driver_ext/shoots_android_driver_test.go
Normal file
154
pkg/uixt/driver_ext/shoots_android_driver_test.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package driver_ext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
var shootsAndroidDriver *ShootsAndroidDriver
|
||||
|
||||
func setupShootsAndroidDriver(t *testing.T) {
|
||||
device, err := uixt.NewAndroidDevice()
|
||||
checkErr(t, err)
|
||||
shootsAndroidDriver, err = NewShootsAndroidDriver(device)
|
||||
checkErr(t, err)
|
||||
}
|
||||
|
||||
func checkErr(t *testing.T, err error, msg ...string) {
|
||||
if err != nil {
|
||||
if len(msg) == 0 {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
t.Fatal(msg, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHello(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
status, err := shootsAndroidDriver.Status()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(status)
|
||||
}
|
||||
|
||||
func TestSource(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
source, err := shootsAndroidDriver.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(source)
|
||||
}
|
||||
|
||||
func TestLogin(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
info, err := shootsAndroidDriver.LoginNoneUI("com.ss.android.ugc.aweme", "12342316231", "8517", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(info)
|
||||
}
|
||||
|
||||
func TestLogout(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.LogoutNoneUI("com.ss.android.ugc.aweme")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSwipe(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.Swipe(878, 2375, 672, 2375)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTap(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.Tap(900, 400)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDoubleTap(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.DoubleTap(500, 500)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLongPress(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.Swipe(1036, 1076, 1036, 1076,
|
||||
option.WithDuration(3))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInput(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.Input("\"哈哈\"")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSave(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
raw, err := shootsAndroidDriver.Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
source, err := shootsAndroidDriver.Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
step := 14
|
||||
file, err := os.Create(fmt.Sprintf("/Users/bytedance/workcode/wings_algorithm/testcases/data/cases/0/%d.jpg", step))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Write(raw.Bytes())
|
||||
|
||||
file, err = os.Create(fmt.Sprintf("/Users/bytedance/workcode/wings_algorithm/testcases/data/cases/0/%d.json", step))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Write([]byte(source))
|
||||
}
|
||||
|
||||
func TestAppLaunch(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
err := shootsAndroidDriver.AppLaunch("com.ss.android.ugc.aweme")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppTerminal(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
_, err := shootsAndroidDriver.AppTerminate("com.ss.android.ugc.aweme")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppInfo(t *testing.T) {
|
||||
setupShootsAndroidDriver(t)
|
||||
info, err := shootsAndroidDriver.getLoginAppInfo("com.ss.android.ugc.aweme")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Log(info)
|
||||
}
|
||||
552
pkg/uixt/driver_ext/shoots_ios_driver.go
Normal file
552
pkg/uixt/driver_ext/shoots_ios_driver.go
Normal file
@@ -0,0 +1,552 @@
|
||||
package driver_ext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/code"
|
||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/ai"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultBightInsightPort = 8000
|
||||
defaultDouyinServerPort = 32921
|
||||
)
|
||||
|
||||
func NewShootsIOSDriver(device *uixt.IOSDevice) (driver *ShootsIOSDriver, err error) {
|
||||
localShootsPort, err := builtin.GetFreePort()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("get free port failed: %v", err))
|
||||
}
|
||||
|
||||
if err = device.Forward(localShootsPort, defaultBightInsightPort); err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
||||
}
|
||||
|
||||
localServerPort, err := builtin.GetFreePort()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("get free port failed: %v", err))
|
||||
}
|
||||
if err = device.Forward(localServerPort, defaultDouyinServerPort); err != nil {
|
||||
return nil, errors.Wrap(code.DeviceHTTPDriverError,
|
||||
fmt.Sprintf("forward tcp port failed: %v", err))
|
||||
}
|
||||
|
||||
host := "localhost"
|
||||
timeout := 10 * time.Second
|
||||
driver = &ShootsIOSDriver{}
|
||||
driver.device = device
|
||||
driver.bightInsightPrefix = fmt.Sprintf("http://%s:%d", host, localShootsPort)
|
||||
driver.serverPrefix = fmt.Sprintf("http://%s:%d", host, localServerPort)
|
||||
driver.timeout = timeout
|
||||
|
||||
return driver, nil
|
||||
}
|
||||
|
||||
type ShootsIOSDriver struct {
|
||||
*uixt.WDADriver
|
||||
|
||||
bightInsightPrefix string
|
||||
serverPrefix string
|
||||
timeout time.Duration
|
||||
device *uixt.IOSDevice
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) setUpWda() (err error) {
|
||||
if s.WDADriver == nil {
|
||||
capabilities := option.NewCapabilities()
|
||||
capabilities.WithDefaultAlertAction(option.AlertActionAccept)
|
||||
driver, err := s.device.NewHTTPDriver(capabilities)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to init WDA driver for shoots IOS")
|
||||
return err
|
||||
}
|
||||
s.WDADriver = driver.(*uixt.WDADriver)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitSession starts a new session and returns the DriverSession.
|
||||
func (s *ShootsIOSDriver) InitSession(capabilities option.Capabilities) error {
|
||||
return s.WDADriver.InitSession(capabilities)
|
||||
}
|
||||
|
||||
// DeleteSession Kills application associated with that session and removes session
|
||||
// 1. alertsMonitor disable
|
||||
// 2. testedApplicationBundleId terminate
|
||||
func (s *ShootsIOSDriver) DeleteSession() error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.DeleteSession()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Status() (types.DeviceStatus, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return types.DeviceStatus{}, err
|
||||
}
|
||||
return s.WDADriver.Status()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) DeviceInfo() (types.DeviceInfo, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return types.DeviceInfo{}, err
|
||||
}
|
||||
return s.WDADriver.DeviceInfo()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Location() (types.Location, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return types.Location{}, err
|
||||
}
|
||||
return s.WDADriver.Location()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) BatteryInfo() (types.BatteryInfo, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return types.BatteryInfo{}, err
|
||||
}
|
||||
return s.WDADriver.BatteryInfo()
|
||||
}
|
||||
|
||||
// WindowSize Return the width and height in portrait mode.
|
||||
// when getting the window size in wda/ui2/adb, if the device is in landscape mode,
|
||||
// the width and height will be reversed.
|
||||
func (s *ShootsIOSDriver) WindowSize() (types.Size, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return types.Size{}, err
|
||||
}
|
||||
return s.WDADriver.WindowSize()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Screen() (ai.Screen, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return ai.Screen{}, err
|
||||
}
|
||||
return s.WDADriver.Screen()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Scale() (float64, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return s.WDADriver.Scale()
|
||||
}
|
||||
|
||||
// Homescreen Forces the device under test to switch to the home screen
|
||||
func (s *ShootsIOSDriver) Homescreen() error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.Homescreen()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Unlock() (err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.Unlock()
|
||||
}
|
||||
|
||||
// AppLaunch Launch an application with given bundle identifier in scope of current session.
|
||||
// !This method is only available since Xcode9 SDK
|
||||
func (s *ShootsIOSDriver) AppLaunch(packageName string) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.AppLaunch(packageName)
|
||||
}
|
||||
|
||||
// AppTerminate Terminate an application with the given package name.
|
||||
// Either `true` if the app has been successfully terminated or `false` if it was not running
|
||||
func (s *ShootsIOSDriver) AppTerminate(packageName string) (bool, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return s.WDADriver.AppTerminate(packageName)
|
||||
}
|
||||
|
||||
// GetForegroundApp returns current foreground app package name and activity name
|
||||
func (s *ShootsIOSDriver) GetForegroundApp() (app types.AppInfo, err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return types.AppInfo{}, err
|
||||
}
|
||||
return s.WDADriver.GetForegroundApp()
|
||||
}
|
||||
|
||||
// StartCamera Starts a new camera for recording
|
||||
func (s *ShootsIOSDriver) StartCamera() error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.StartCamera()
|
||||
}
|
||||
|
||||
// StopCamera Stops the camera for recording
|
||||
func (s *ShootsIOSDriver) StopCamera() error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.StopCamera()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Orientation() (orientation types.Orientation, err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return types.OrientationPortrait, err
|
||||
}
|
||||
return s.WDADriver.Orientation()
|
||||
}
|
||||
|
||||
// Tap Sends a tap event at the coordinate.
|
||||
func (s *ShootsIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.Tap(x, y, opts...)
|
||||
}
|
||||
|
||||
// DoubleTap Sends a double tap event at the coordinate.
|
||||
func (s *ShootsIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.DoubleTap(x, y, opts...)
|
||||
}
|
||||
|
||||
// TouchAndHold Initiates a long-press gesture at the coordinate, holding for the specified duration.
|
||||
//
|
||||
// second: The default value is 1
|
||||
func (s *ShootsIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.TouchAndHold(x, y, opts...)
|
||||
}
|
||||
|
||||
// Drag Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
|
||||
// WithPressDurationOption option can be used to set pressForDuration (default to 1 second).
|
||||
func (s *ShootsIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.Drag(fromX, fromY, toX, toY, opts...)
|
||||
}
|
||||
|
||||
// SetPasteboard Sets data to the general pasteboard
|
||||
func (s *ShootsIOSDriver) SetPasteboard(contentType types.PasteboardType, content string) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.SetPasteboard(contentType, content)
|
||||
}
|
||||
|
||||
// GetPasteboard Gets the data contained in the general pasteboard.
|
||||
//
|
||||
// It worked when `WDA` was foreground. https://github.com/appium/WebDriverAgent/issues/330
|
||||
func (s *ShootsIOSDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WDADriver.GetPasteboard(contentType)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) SetIme(ime string) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.SetIme(ime)
|
||||
}
|
||||
|
||||
// SendKeys Types a string into active element. There must be element with keyboard focus,
|
||||
// otherwise an error is raised.
|
||||
// WithFrequency option can be used to set frequency of typing (letters per sec). The default value is 60
|
||||
func (s *ShootsIOSDriver) SendKeys(text string, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.SendKeys(text, opts...)
|
||||
}
|
||||
|
||||
// Input works like SendKeys
|
||||
func (s *ShootsIOSDriver) Input(text string, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.Input(text, opts...)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Clear(packageName string) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.Clear(packageName)
|
||||
}
|
||||
|
||||
// PressButton Presses the corresponding hardware button on the device
|
||||
func (s *ShootsIOSDriver) PressButton(devBtn types.DeviceButton) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.PressButton(devBtn)
|
||||
}
|
||||
|
||||
// PressBack Presses the back button
|
||||
func (s *ShootsIOSDriver) PressBack(opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.PressBack(opts...)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) PressKeyCode(keyCode uixt.KeyCode) (err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.PressKeyCode(keyCode)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Screenshot() (*bytes.Buffer, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WDADriver.Screenshot()
|
||||
//screenshotService, err := instruments.NewScreenshotService(s.device.d)
|
||||
//if err != nil {
|
||||
// log.Error().Err(err).Msg("Starting screenshot service failed")
|
||||
// return nil, err
|
||||
//}
|
||||
//defer screenshotService.Close()
|
||||
//
|
||||
//imageBytes, err := screenshotService.TakeScreenshot()
|
||||
//if err != nil {
|
||||
// log.Error().Err(err).Msg("failed to task screenshot")
|
||||
// return nil, err
|
||||
//}
|
||||
//return bytes.NewBuffer(imageBytes), nil
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) TapByText(text string, opts ...option.ActionOption) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.TapByText(text, opts...)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) TapByTexts(actions ...uixt.TapTextAction) error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.TapByTexts(actions...)
|
||||
}
|
||||
|
||||
// AccessibleSource Return application elements accessibility tree
|
||||
func (s *ShootsIOSDriver) AccessibleSource() (string, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return s.WDADriver.AccessibleSource()
|
||||
}
|
||||
|
||||
// HealthCheck Health check might modify simulator state so it should only be called in-between testing sessions
|
||||
//
|
||||
// Checks health of XCTest by:
|
||||
// 1) Querying application for some elements,
|
||||
// 2) Triggering some device events.
|
||||
func (s *ShootsIOSDriver) HealthCheck() error {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.HealthCheck()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) GetAppiumSettings() (map[string]interface{}, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WDADriver.GetAppiumSettings()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WDADriver.SetAppiumSettings(settings)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) IsHealthy() (bool, error) {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return s.WDADriver.IsHealthy()
|
||||
}
|
||||
|
||||
// triggers the log capture and returns the log entries
|
||||
func (s *ShootsIOSDriver) StartCaptureLog(identifier ...string) (err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.WDADriver.StartCaptureLog(identifier...)
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) StopCaptureLog() (result interface{}, err error) {
|
||||
err = s.setUpWda()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WDADriver.StopCaptureLog()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) GetDriverResults() []*uixt.DriverRequests {
|
||||
err := s.setUpWda()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return s.WDADriver.GetDriverResults()
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) {
|
||||
resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/source?format=json&onlyWeb=false", s.bightInsightPrefix), []byte{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(resp), nil
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) 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")
|
||||
}
|
||||
bsJSON, err := json.Marshal(params)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
resp, err := s.Request(http.MethodPost, fmt.Sprintf("%s/host/login/account/", s.serverPrefix), bsJSON)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
res, err := resp.ValueConvertToJsonObject()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
log.Info().Msgf("%v", res)
|
||||
// {'isSuccess': True, 'data': '登录成功', 'code': 0}
|
||||
if res["isSuccess"] != true {
|
||||
err = fmt.Errorf("falied to logout %s", res["data"])
|
||||
log.Err(err).Msgf("%v", res)
|
||||
return info, err
|
||||
}
|
||||
time.Sleep(20 * time.Second)
|
||||
info, err = s.getLoginAppInfo(packageName)
|
||||
if err != nil || !info.IsLogin {
|
||||
return info, fmt.Errorf("falied to login %v", info)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) LogoutNoneUI(packageName string) error {
|
||||
resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/host/loginout/", s.serverPrefix), []byte{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := resp.ValueConvertToJsonObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info().Msgf("%v", res)
|
||||
if res["isSuccess"] != true {
|
||||
err = fmt.Errorf("falied to logout %s", res["data"])
|
||||
log.Err(err).Msgf("%v", res)
|
||||
return err
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) TearDown() error {
|
||||
s.WDADriver.TearDown()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) {
|
||||
resp, err := s.Request(http.MethodGet, fmt.Sprintf("%s/host/app/info/", s.serverPrefix), []byte{})
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
res, err := resp.ValueConvertToJsonObject()
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
log.Info().Msgf("%v", res)
|
||||
if res["isSuccess"] != true {
|
||||
err = fmt.Errorf("falied to get is login %s", res["data"])
|
||||
log.Err(err).Msgf("%v", res)
|
||||
return info, err
|
||||
}
|
||||
err = json.Unmarshal([]byte(res["data"].(string)), &info)
|
||||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (s *ShootsIOSDriver) GetSession() *uixt.Session {
|
||||
return s.Session
|
||||
}
|
||||
109
pkg/uixt/driver_ext/shoots_ios_driver_test.go
Normal file
109
pkg/uixt/driver_ext/shoots_ios_driver_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package driver_ext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v5/internal/builtin"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt"
|
||||
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
|
||||
)
|
||||
|
||||
var (
|
||||
shootsIOSDriver uixt.IDriverExt
|
||||
iOSDevice *uixt.IOSDevice
|
||||
)
|
||||
|
||||
func setupShootsIOSDriver(t *testing.T) {
|
||||
var err error
|
||||
iOSDevice, err = uixt.NewIOSDevice(
|
||||
option.WithWDAPort(8700),
|
||||
option.WithWDAMjpegPort(8800))
|
||||
checkErr(t, err)
|
||||
shootsIOSDriver, err = iOSDevice.NewDriver()
|
||||
checkErr(t, err)
|
||||
}
|
||||
|
||||
func TestIOSLogin(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
info, err := shootsIOSDriver.(*ShootsIOSDriver).LoginNoneUI("", "12342316231", "8517", "")
|
||||
checkErr(t, err)
|
||||
t.Log(info)
|
||||
}
|
||||
|
||||
func TestIOSLogout(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
err := shootsIOSDriver.(*ShootsIOSDriver).LogoutNoneUI("")
|
||||
checkErr(t, err)
|
||||
}
|
||||
|
||||
func TestIOSIsLogin(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
err := shootsIOSDriver.(*ShootsIOSDriver).LogoutNoneUI("")
|
||||
checkErr(t, err)
|
||||
}
|
||||
|
||||
func TestIOSSource(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
source, err := shootsIOSDriver.GetDriver().Source()
|
||||
checkErr(t, err)
|
||||
t.Log(source)
|
||||
}
|
||||
|
||||
func TestIOSForeground(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
app, err := shootsIOSDriver.GetDriver().GetForegroundApp()
|
||||
checkErr(t, err)
|
||||
t.Log(app)
|
||||
}
|
||||
|
||||
func TestIOSSwipe(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
shootsIOSDriver.GetDriver().Swipe(540, 0, 540, 1000)
|
||||
}
|
||||
|
||||
func TestIOSSave(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
raw, err := shootsIOSDriver.GetDriver().Screenshot()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
source, err := shootsIOSDriver.GetDriver().Source()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
step := 7
|
||||
file, err := os.Create(fmt.Sprintf("/Users/bytedance/workcode/wings_algorithm/testcases/data/cases/ios/4159417_cvcn02okg4g0/%d.jpg", step))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Write(raw.Bytes())
|
||||
|
||||
file, err = os.Create(fmt.Sprintf("/Users/bytedance/workcode/wings_algorithm/testcases/data/cases/ios/4159417_cvcn02okg4g0/%d.json", step))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
file.Write([]byte(source))
|
||||
}
|
||||
|
||||
func TestListen(t *testing.T) {
|
||||
setupShootsIOSDriver(t)
|
||||
localPort, err := builtin.GetFreePort()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = iOSDevice.Forward(localPort, 8800)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
addr := fmt.Sprintf("0.0.0.0:%d", localPort)
|
||||
l, err := net.Listen("tcp", addr)
|
||||
if err == nil {
|
||||
l.Close() // 端口成功绑定后立即释放,返回该端口号
|
||||
} else {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user