fix: web ui test

This commit is contained in:
徐聪
2025-05-06 02:02:28 +08:00
parent 37fd2e900d
commit 6cce5e3c5b
14 changed files with 347 additions and 60 deletions

View File

@@ -35,6 +35,7 @@ type TConfig struct {
IOS []*option.IOSDeviceOptions `json:"ios,omitempty" yaml:"ios,omitempty"`
Android []*option.AndroidDeviceOptions `json:"android,omitempty" yaml:"android,omitempty"`
Harmony []*option.HarmonyDeviceOptions `json:"harmony,omitempty" yaml:"harmony,omitempty"`
Browser []*option.BrowserDeviceOptions `json:"browser,omitempty" yaml:"browser,omitempty"`
RequestTimeout float32 `json:"request_timeout,omitempty" yaml:"request_timeout,omitempty"` // request timeout in seconds
CaseTimeout float32 `json:"case_timeout,omitempty" yaml:"case_timeout,omitempty"` // testcase timeout in seconds
Export []string `json:"export,omitempty" yaml:"export,omitempty"`
@@ -185,6 +186,24 @@ func (c *TConfig) SetAndroid(opts ...option.AndroidDeviceOption) *TConfig {
return c
}
func (c *TConfig) SetBrowser(opts ...option.BrowserDeviceOption) *TConfig {
browserOptions := option.NewBrowserDeviceOptions(opts...)
// each device can have its own settings
if browserOptions.BrowserID != "" {
c.Browser = append(c.Browser, browserOptions)
return c
}
// device UDID is not specified, settings will be shared
if len(c.Browser) == 0 {
c.Browser = append(c.Browser, browserOptions)
} else {
c.Browser[0] = browserOptions
}
return c
}
// EnablePlugin enables plugin for current testcase.
// default to disable plugin
func (c *TConfig) EnablePlugin() *TConfig {

View File

@@ -498,6 +498,30 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
}
r.uixtDrivers[harmonyDeviceOptions.ConnectKey] = driverExt
}
// parse browser devices config
for _, browserDeviceOptions := range parsedConfig.Browser {
err := r.parseDeviceConfig(browserDeviceOptions, parsedConfig.Variables)
if err != nil {
return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse browser config failed: %v", err))
}
device, err := uixt.NewBrowserDevice(browserDeviceOptions.Options()...)
if err != nil {
return nil, errors.Wrap(err, "init browser device failed")
}
if err := device.Setup(); err != nil {
return nil, err
}
driver, err := device.NewDriver()
if err != nil {
return nil, err
}
if err := driver.Setup(); err != nil {
return nil, err
}
driverExt := uixt.NewXTDriver(driver, aiOpts...)
r.uixtDrivers[browserDeviceOptions.BrowserID] = driverExt
}
return parsedConfig, nil
}

View File

@@ -15,6 +15,7 @@ const (
StepTypeAndroid StepType = "android"
StepTypeHarmony StepType = "harmony"
StepTypeIOS StepType = "ios"
stepTypeBrowser StepType = "browser"
StepTypeShell StepType = "shell"
StepTypeFunction StepType = "function"
@@ -47,6 +48,7 @@ type TStep struct {
Android *MobileUI `json:"android,omitempty" yaml:"android,omitempty"`
Harmony *MobileUI `json:"harmony,omitempty" yaml:"harmony,omitempty"`
IOS *MobileUI `json:"ios,omitempty" yaml:"ios,omitempty"`
Browser *MobileUI `json:"browser,omitempty" yaml:"browser,omitempty"`
Shell *Shell `json:"shell,omitempty" yaml:"shell,omitempty"`
}

View File

@@ -791,6 +791,17 @@ func (s *StepRequest) Harmony(opts ...option.HarmonyDeviceOption) *StepMobile {
}
}
// Browser creates a new browser step session
func (s *StepRequest) Browser(opts ...option.BrowserDeviceOption) *StepMobile {
browserOptions := option.NewBrowserDeviceOptions(opts...)
return &StepMobile{
StepConfig: s.StepConfig,
Browser: &MobileUI{
Serial: browserOptions.BrowserID,
},
}
}
// Shell creates a new shell step session
func (s *StepRequest) Shell(content string) *StepShell {
return &StepShell{

View File

@@ -28,8 +28,8 @@ type StepMobile struct {
Android *MobileUI `json:"android,omitempty" yaml:"android,omitempty"`
Harmony *MobileUI `json:"harmony,omitempty" yaml:"harmony,omitempty"`
IOS *MobileUI `json:"ios,omitempty" yaml:"ios,omitempty"`
cache *MobileUI // used for caching
Browser *MobileUI `json:"browser,omitempty" yaml:"browser,omitempty"`
cache *MobileUI // used for caching
}
// uniform interface for all types of mobile systems
@@ -50,6 +50,10 @@ func (s *StepMobile) obj() *MobileUI {
s.cache = s.Android
s.cache.OSType = string(StepTypeAndroid)
return s.cache
} else if s.Browser != nil {
s.cache = s.Browser
s.cache.OSType = string(stepTypeBrowser)
return s.cache
} else if s.Mobile != nil {
s.cache = s.Mobile
return s.cache
@@ -79,6 +83,14 @@ func (s *StepMobile) InstallApp(path string) *StepMobile {
return s
}
func (s *StepMobile) LoginNoneUI(packageName, phoneNumber string, captcha, password string) *StepMobile {
s.obj().Actions = append(s.obj().Actions, uixt.MobileAction{
Method: uixt.ACTION_LoginNoneUI,
Params: []string{packageName, phoneNumber, captcha, password},
})
return s
}
func (s *StepMobile) AppLaunch(bundleId string) *StepMobile {
s.obj().Actions = append(s.obj().Actions, uixt.MobileAction{
Method: uixt.ACTION_AppLaunch,
@@ -286,6 +298,61 @@ func (s *StepMobile) SwipeToTapTexts(texts interface{}, opts ...option.ActionOpt
return s
}
func (s *StepMobile) RightClick(x, y float64, options ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_RightClick,
Params: []float64{x, y},
Options: option.NewActionOptions(options...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) RightClickBySelector(selector string, options ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_RightClickBySelector,
Params: selector,
Options: option.NewActionOptions(options...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) HoverBySelector(selector string, options ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_HoverBySelector,
Params: selector,
Options: option.NewActionOptions(options...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) TapBySelector(selector string, options ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_TapBySelector,
Params: selector,
Options: option.NewActionOptions(options...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) ClosePage(idx int, options ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_ClosePage,
Params: idx,
Options: option.NewActionOptions(options...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) GetElementTextBySelector(selector string, options ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_GetElementTextBySelector,
Params: selector,
Options: option.NewActionOptions(options...),
}
s.obj().Actions = append(s.obj().Actions, action)
return s
}
func (s *StepMobile) Input(text string, opts ...option.ActionOption) *StepMobile {
action := uixt.MobileAction{
Method: uixt.ACTION_Input,

View File

@@ -251,6 +251,12 @@ func (tc *TestCaseDef) loadISteps() (*TestCase, error) {
StepConfig: step.StepConfig,
Android: step.Android,
})
} else if step.Browser != nil {
testCase.TestSteps = append(testCase.TestSteps, &StepMobile{
StepConfig: step.StepConfig,
Browser: step.Browser,
})
} else if step.Shell != nil {
testCase.TestSteps = append(testCase.TestSteps, &StepShell{
StepConfig: step.StepConfig,

View File

@@ -37,6 +37,7 @@ type CreateBrowserResponse struct {
type BrowserDriver struct {
urlPrefix *url.URL
sessionId string
Session *DriverSession
}
type BrowserInfo struct {
@@ -100,39 +101,49 @@ func NewBrowserDriver(device *BrowserDevice) (driver *BrowserDriver, err error)
driver.urlPrefix.Host = BROWSER_LOCAL_ADDRESS
driver.urlPrefix.Scheme = "http"
driver.sessionId = device.UUID()
driver.Session = NewDriverSession()
driver.Session.ID = driver.sessionId
return driver, nil
}
func (wd *BrowserDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error {
var err error
fromX, fromY, toX, toY, err = handlerDrag(wd, fromX, fromY, toX, toY, opts...)
func (wd *BrowserDriver) Setup() error {
err := wd.Session.SetupPortForward(8093)
if err != nil {
return err
}
wd.Session.SetBaseURL(BROWSER_LOCAL_ADDRESS)
return nil
}
func (wd *BrowserDriver) Drag(fromX, fromY, toX, toY float64, options ...option.ActionOption) (err error) {
fromX, fromY, toX, toY, err = handlerDrag(wd, fromX, fromY, toX, toY, options...)
if err != nil {
return err
}
data := map[string]interface{}{
"from_x": fromX,
"from_y": fromY,
"to_x": toX,
"to_y": toY,
}
actionOptions := option.NewActionOptions(options...)
actionOptions := option.NewActionOptions(opts...)
if actionOptions.Duration > 0 {
data["duration"] = actionOptions.Duration
} else {
data["duration"] = 0.5
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/drag")
return err
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/drag"))
return
}
func (wd *BrowserDriver) AppLaunch(packageName string) (err error) {
data := map[string]interface{}{
"url": packageName,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/page_launch")
return
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/page_launch"))
return nil
}
func (wd *BrowserDriver) DeleteSession() (err error) {
@@ -193,7 +204,8 @@ func (wd *BrowserDriver) ClosePage(pageIndex int) (err error) {
data := map[string]interface{}{
"page_index": pageIndex,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/page_close")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/page_close"))
return err
}
@@ -205,7 +217,7 @@ func (wd *BrowserDriver) HoverBySelector(selector string, options ...option.Acti
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/hover")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/hover"))
return err
}
@@ -217,7 +229,7 @@ func (wd *BrowserDriver) TapBySelector(selector string, options ...option.Action
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/tap")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/tap"))
return err
}
@@ -226,7 +238,7 @@ func (wd *BrowserDriver) RightClick(x, y float64) (err error) {
"x": x,
"y": y,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/right_click")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/right_click"))
return err
}
@@ -238,7 +250,7 @@ func (wd *BrowserDriver) RightClickBySelector(selector string, options ...option
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/right_click")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/right_click"))
return err
}
@@ -294,29 +306,49 @@ func (wd *BrowserDriver) GetPageUrl(options ...option.ActionOption) (text string
if actionOptions.Index > 0 {
uri = uri + "?page_index=" + fmt.Sprintf("%v", actionOptions.Index)
}
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, uri)
resp, err := wd.Session.GET(wd.concatURL(wd.sessionId, uri))
if err != nil {
return "", err
}
data := resp.Data.(map[string]interface{})
data, err := resp.ValueConvertToJsonObject()
if err != nil {
return "", err
}
data = data["data"].(map[string]interface{})
return data["url"].(string), nil
}
func (wd *BrowserDriver) IsElementExistBySelector(selector string) (bool, error) {
resp, err := wd.HttpGet(wd.sessionId, "ui/element_exist", "?selector=", selector)
resp, err := wd.Session.GET(wd.concatURL("ui/element_exist", "?selector=", selector))
if err != nil {
return false, err
}
data := resp.Data.(map[string]interface{})
data, err := resp.ValueConvertToJsonObject()
if err != nil {
return false, err
}
data = data["data"].(map[string]interface{})
return data["exist"].(bool), nil
}
func (wd *BrowserDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (success bool, err error) {
data := map[string]interface{}{
"url": packageName,
"web_cookie": password,
}
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "stub/login"))
if err != nil {
return false, err
}
return true, err
}
func (wd *BrowserDriver) Hover(x, y float64) (err error) {
data := map[string]interface{}{
"x": x,
"y": y,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/hover")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/hover"))
return err
}
@@ -324,31 +356,38 @@ func (wd *BrowserDriver) Input(text string, option ...option.ActionOption) (err
data := map[string]interface{}{
"text": text,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/input")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/input"))
return err
}
// Source Return application elements tree
func (wd *BrowserDriver) Source(srcOpt ...option.SourceOption) (string, error) {
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, "stub/source")
resp, err := wd.Session.GET(wd.concatURL(wd.sessionId, "stub/source"))
if err != nil {
return "", err
}
jsonData, err := json.Marshal(resp.Data)
if err != nil {
return "", err
}
return string(jsonData), err
return resp.ValueConvertToString()
}
func (wd *BrowserDriver) ScreenShot(options ...option.ActionOption) (*bytes.Buffer, error) {
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, "screenshot")
resp, err := wd.Session.GET(wd.concatURL(wd.sessionId, "screenshot"))
if err != nil {
return nil, err
}
data := resp.Data.(map[string]interface{})
// 将结果解析为 JSON
var result WebAgentResponse
if err = json.Unmarshal(resp, &result); err != nil {
return nil, err
}
if result.Code != 0 {
log.Info().Msgf("%v", result.Message)
return nil, errors.New(result.Message)
}
data := result.Data.(map[string]interface{})
screenshotBase64 := data["screenshot"].(string)
screenRaw, err := base64.StdEncoding.DecodeString(screenshotBase64)
if err != nil {
@@ -434,11 +473,16 @@ func (wd *BrowserDriver) BatteryInfo() (batteryInfo types.BatteryInfo, err error
}
func (wd *BrowserDriver) WindowSize() (types.Size, error) {
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, "window_size")
resp, err := wd.Session.GET(wd.concatURL(wd.sessionId, "window_size"))
if err != nil {
return types.Size{}, err
}
data := resp.Data.(map[string]interface{})
data, err := resp.ValueConvertToJsonObject()
if err != nil {
return types.Size{}, err
}
data = data["data"].(map[string]interface{})
width := data["width"]
height := data["height"]
return types.Size{
@@ -540,7 +584,8 @@ func (wd *BrowserDriver) TapFloat(x, y float64, opts ...option.ActionOption) err
"y": y,
"duration": duration,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/tap")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/tap"))
return err
}
@@ -555,7 +600,8 @@ func (wd *BrowserDriver) DoubleTap(x, y float64, options ...option.ActionOption)
"x": x,
"y": y,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/double_tap")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/double_tap"))
return err
}
@@ -566,7 +612,7 @@ func (wd *BrowserDriver) UploadFile(x, y float64, FileUrl, FileFormat string) (e
"file_url": FileUrl,
"file_format": FileFormat,
}
_, err = wd.HttpPOST(data, wd.sessionId, "ui/upload")
_, err = wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/upload"))
return err
}
@@ -602,10 +648,6 @@ func (wd *BrowserDriver) Clear(packageName string) error {
return errors.New("not support")
}
func (wd *BrowserDriver) Setup() error {
return nil
}
func (wd *BrowserDriver) GetDevice() IDevice {
return nil
}
@@ -684,7 +726,7 @@ func (wd *BrowserDriver) InitSession(capabilities option.Capabilities) error {
}
func (wd *BrowserDriver) GetSession() *DriverSession {
return nil
return wd.Session
}
func (wd *BrowserDriver) ScreenRecord(opts ...option.ActionOption) (videoPath string, err error) {
@@ -708,7 +750,7 @@ func (wd *BrowserDriver) TapXY(x, y float64, opts ...option.ActionOption) error
"x": x,
"y": y,
}
_, err := wd.HttpPOST(data, wd.sessionId, "ui/double_tap")
_, err := wd.Session.POST(data, wd.concatURL(wd.sessionId, "ui/double_tap"))
return err
}

View File

@@ -20,6 +20,7 @@ const (
ACTION_LOG ActionMethod = "log"
ACTION_AppInstall ActionMethod = "install"
ACTION_AppUninstall ActionMethod = "uninstall"
ACTION_LoginNoneUI ActionMethod = "login_none_ui"
ACTION_AppClear ActionMethod = "app_clear"
ACTION_AppStart ActionMethod = "app_start"
ACTION_AppLaunch ActionMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
@@ -35,18 +36,24 @@ const (
ACTION_CallFunction ActionMethod = "call_function"
// UI handling
ACTION_Home ActionMethod = "home"
ACTION_TapXY ActionMethod = "tap_xy"
ACTION_TapAbsXY ActionMethod = "tap_abs_xy"
ACTION_TapByOCR ActionMethod = "tap_ocr"
ACTION_TapByCV ActionMethod = "tap_cv"
ACTION_DoubleTapXY ActionMethod = "double_tap_xy"
ACTION_Swipe ActionMethod = "swipe"
ACTION_Drag ActionMethod = "drag"
ACTION_Input ActionMethod = "input"
ACTION_Back ActionMethod = "back"
ACTION_KeyCode ActionMethod = "keycode"
ACTION_AIAction ActionMethod = "ai_action" // action with ai
ACTION_Home ActionMethod = "home"
ACTION_TapXY ActionMethod = "tap_xy"
ACTION_TapAbsXY ActionMethod = "tap_abs_xy"
ACTION_TapByOCR ActionMethod = "tap_ocr"
ACTION_TapByCV ActionMethod = "tap_cv"
ACTION_DoubleTapXY ActionMethod = "double_tap_xy"
ACTION_Swipe ActionMethod = "swipe"
ACTION_Drag ActionMethod = "drag"
ACTION_Input ActionMethod = "input"
ACTION_Back ActionMethod = "back"
ACTION_KeyCode ActionMethod = "keycode"
ACTION_AIAction ActionMethod = "ai_action" // action with ai
ACTION_TapBySelector ActionMethod = "tap_by_selector"
ACTION_HoverBySelector ActionMethod = "hover_by_selector"
ACTION_ClosePage ActionMethod = "close_page"
ACTION_RightClick ActionMethod = "right_click"
ACTION_RightClickBySelector ActionMethod = "right_click_by_selector"
ACTION_GetElementTextBySelector ActionMethod = "get_element_text_by_selector"
// custom actions
ACTION_SwipeToTapApp ActionMethod = "swipe_to_tap_app" // swipe left & right to find app and tap
@@ -111,6 +118,13 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
}()
switch action.Method {
case ACTION_LoginNoneUI:
if len(action.Params.([]interface{})) == 4 {
params := action.Params.([]interface{})
_, err = dExt.IDriver.(*BrowserDriver).LoginNoneUI(params[0].(string), params[1].(string), params[2].(string), params[3].(string))
return err
}
return fmt.Errorf("invalid %s params: %v", ACTION_LoginNoneUI, action.Params)
case ACTION_AppInstall:
if app, ok := action.Params.(string); ok {
if err = dExt.GetDevice().Install(app,
@@ -170,6 +184,40 @@ func (dExt *XTDriver) DoAction(action MobileAction) (err error) {
return fmt.Errorf("app_terminate params should be bundleId(string), got %v", action.Params)
case ACTION_Home:
return dExt.Home()
case ACTION_RightClick:
if params, err := builtin.ConvertToFloat64Slice(action.Params); err == nil {
if len(params) != 2 {
return fmt.Errorf("invalid tap location params: %v", params)
}
x, y := params[0], params[1]
return dExt.IDriver.(*BrowserDriver).RightClick(x, y)
}
return fmt.Errorf("invalid %s params: %v", ACTION_RightClick, action.Params)
case ACTION_HoverBySelector:
if selector, ok := action.Params.(string); ok {
return dExt.IDriver.(*BrowserDriver).HoverBySelector(selector, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_HoverBySelector, action.Params)
case ACTION_TapBySelector:
if selector, ok := action.Params.(string); ok {
return dExt.IDriver.(*BrowserDriver).TapBySelector(selector, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_TapBySelector, action.Params)
case ACTION_RightClickBySelector:
if selector, ok := action.Params.(string); ok {
return dExt.IDriver.(*BrowserDriver).RightClickBySelector(selector, action.GetOptions()...)
}
return fmt.Errorf("invalid %s params: %v", ACTION_RightClickBySelector, action.Params)
case ACTION_ClosePage:
if param, ok := action.Params.(json.Number); ok {
paramInt64, _ := param.Int64()
return dExt.IDriver.(*BrowserDriver).ClosePage(int(paramInt64))
} else if param, ok := action.Params.(int64); ok {
return dExt.IDriver.(*BrowserDriver).ClosePage(int(param))
} else {
return dExt.IDriver.(*BrowserDriver).ClosePage(action.Params.(int))
}
// return fmt.Errorf("invalid %s params: %v", ACTION_ClosePage, action.Params)
case ACTION_SetIme:
if ime, ok := action.Params.(string); ok {
err = dExt.SetIme(ime)

View File

@@ -56,3 +56,15 @@ func (dExt *XTDriver) TapByCV(opts ...option.ActionOption) error {
return dExt.TapAbsXY(point.X, point.Y, opts...)
}
func (dExt *XTDriver) RightClickByOCR(ocrText string, opts ...option.ActionOption) error {
actionOptions := option.NewActionOptions(opts...)
point, err := dExt.FindScreenText(ocrText, opts...)
if err != nil {
if actionOptions.IgnoreNotFoundError {
return nil
}
return err
}
return dExt.IDriver.(*BrowserDriver).RightClick(point.Center().X, point.Center().Y)
}

View File

@@ -270,14 +270,18 @@ func (s *DriverSession) Request(method string, urlStr string, rawBody []byte) (
}
func (s *DriverSession) SetupPortForward(localPort int) error {
conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort))
if err != nil {
return fmt.Errorf("create tcp connection error %v", err)
}
// conn, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", localPort))
// if err != nil {
// return fmt.Errorf("create tcp connection error %v", err)
// }
s.client.Transport = &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return conn, nil
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return net.Dial(network, fmt.Sprintf("127.0.0.1:%d", localPort))
},
MaxIdleConns: 10,
IdleConnTimeout: 30 * time.Second,
DisableKeepAlives: false,
TLSHandshakeTimeout: 10 * time.Second,
}
return nil
}

View File

@@ -180,6 +180,24 @@ func (dExt *XTDriver) assertForegroundApp(appName, assert string) error {
return nil
}
func (dExt *XTDriver) assertSelector(selector, assert string) error {
switch assert {
case AssertionExists:
_, err := dExt.IDriver.(*BrowserDriver).IsElementExistBySelector(selector)
if err != nil {
return errors.Wrap(err, "assert ocr exists failed")
}
case AssertionNotExists:
_, err := dExt.IDriver.(*BrowserDriver).IsElementExistBySelector(selector)
if err == nil {
return errors.New("assert ocr not exists failed")
}
default:
return fmt.Errorf("unexpected assert method %s", assert)
}
return nil
}
func (dExt *XTDriver) DoValidation(check, assert, expected string, message ...string) (err error) {
switch check {
case SelectorOCR:

View File

@@ -7,6 +7,7 @@ import (
"testing"
"time"
"github.com/danielpaulus/go-ios/ios"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -50,6 +51,16 @@ func TestDriver_WDA_LazySetup(t *testing.T) {
assert.Nil(t, err)
}
func TestIOSDeviceList(t *testing.T) {
t.Logf("start test")
// get all attached ios devices
devices, err := ios.ListDevices()
if err != nil {
t.Fatal(err)
}
t.Logf("%+v", devices)
}
func TestDevice_IOS_New(t *testing.T) {
device, err := NewIOSDevice(
option.WithWDAPort(8700),

View File

@@ -15,6 +15,19 @@ type BrowserDeviceOptions struct {
Height int `json:"height,omitempty" yaml:"height,omitempty"`
}
func (dev *BrowserDeviceOptions) Options() (deviceOptions []BrowserDeviceOption) {
if dev.BrowserID != "" {
deviceOptions = append(deviceOptions, WithBrowserID(dev.BrowserID))
}
if dev.LogOn {
deviceOptions = append(deviceOptions, WithBrowserLogOn(true))
}
if dev.Width > 0 && dev.Height > 0 {
deviceOptions = append(deviceOptions, WithBrowserPageSize(dev.Width, dev.Height))
}
return
}
type BrowserDeviceOption func(*BrowserDeviceOptions)
func WithBrowserID(serial string) BrowserDeviceOption {

View File

@@ -2,6 +2,7 @@ package option
type IOSDeviceOptions struct {
UDID string `json:"udid,omitempty" yaml:"udid,omitempty"`
Wireless bool `json:"wireless,omitempty" yaml:"wireless,omitempty"`
WDAPort int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
WDAMjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
@@ -20,6 +21,9 @@ func (dev *IOSDeviceOptions) Options() (deviceOptions []IOSDeviceOption) {
if dev.UDID != "" {
deviceOptions = append(deviceOptions, WithUDID(dev.UDID))
}
if dev.Wireless {
deviceOptions = append(deviceOptions, WithWireless(true))
}
if dev.WDAPort != 0 {
deviceOptions = append(deviceOptions, WithWDAPort(dev.WDAPort))
}
@@ -101,6 +105,12 @@ func WithUDID(udid string) IOSDeviceOption {
}
}
func WithWireless(on bool) IOSDeviceOption {
return func(device *IOSDeviceOptions) {
device.Wireless = on
}
}
func WithWDAPort(port int) IOSDeviceOption {
return func(device *IOSDeviceOptions) {
device.WDAPort = port