From 1ec383180bd830c28783898488d6ae5052c48cb6 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 10 Feb 2025 13:35:22 +0800 Subject: [PATCH] refactor: move shoots driver to driver_ext --- internal/version/VERSION | 2 +- pkg/uixt/android_device.go | 2 - pkg/uixt/android_driver_adb.go | 63 +++--- pkg/uixt/android_driver_stub_test.go | 144 -------------- pkg/uixt/android_driver_uia2.go | 128 ++++++------ pkg/uixt/driver.go | 25 ++- .../shoots_android_dirver.go} | 143 +++++--------- .../driver_ext/shoots_android_driver_test.go | 154 +++++++++++++++ .../shoots_ios_driver.go} | 140 +++++++------ .../shoots_ios_driver_test.go} | 50 ++--- pkg/uixt/driver_session.go | 40 ++-- pkg/uixt/harmony_driver_hdc.go | 76 ++++--- pkg/uixt/ios_device.go | 50 ++--- pkg/uixt/ios_driver_wda.go | 186 +++++++++--------- pkg/uixt/option/android.go | 16 -- pkg/uixt/option/ios.go | 10 - pkg/uixt/types/driver.go | 5 + server/context.go | 3 +- server/main.go | 8 +- 19 files changed, 564 insertions(+), 681 deletions(-) delete mode 100644 pkg/uixt/android_driver_stub_test.go rename pkg/uixt/{android_driver_stub.go => driver_ext/shoots_android_dirver.go} (59%) create mode 100644 pkg/uixt/driver_ext/shoots_android_driver_test.go rename pkg/uixt/{ios_driver_stub.go => driver_ext/shoots_ios_driver.go} (70%) rename pkg/uixt/{ios_driver_stub_test.go => driver_ext/shoots_ios_driver_test.go} (59%) create mode 100644 pkg/uixt/types/driver.go diff --git a/internal/version/VERSION b/internal/version/VERSION index ccd3ed99..78addcb1 100644 --- a/internal/version/VERSION +++ b/internal/version/VERSION @@ -1 +1 @@ -v5.0.0+2502091051 +v5.0.0+2502101335 diff --git a/pkg/uixt/android_device.go b/pkg/uixt/android_device.go index 6565163f..13e80f12 100644 --- a/pkg/uixt/android_device.go +++ b/pkg/uixt/android_device.go @@ -153,8 +153,6 @@ func (dev *AndroidDevice) NewDriver() (driverExt IDriverExt, err error) { var driver IDriver if dev.UIA2 || dev.LogOn { driver, err = NewUIA2Driver(dev) - } else if dev.STUB { - driver, err = NewStubAndroidDriver(dev) } else { driver, err = NewADBDriver(dev) } diff --git a/pkg/uixt/android_driver_adb.go b/pkg/uixt/android_driver_adb.go index 5c5e1648..69f0ee24 100644 --- a/pkg/uixt/android_driver_adb.go +++ b/pkg/uixt/android_driver_adb.go @@ -34,14 +34,13 @@ func NewADBDriver(device *AndroidDevice) (*ADBDriver, error) { AndroidDevice: device, Session: &Session{}, } - driver.NewSession(nil) + driver.InitSession(nil) return driver, nil } type ADBDriver struct { *AndroidDevice - *Session - // DriverExt + Session *Session } func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, err error) { @@ -59,7 +58,7 @@ func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, } else { driverResult.Success = true } - ad.addRequestResult(driverResult) + ad.Session.addRequestResult(driverResult) }() // adb shell screencap -p @@ -77,18 +76,18 @@ func (ad *ADBDriver) runShellCommand(cmd string, args ...string) (output string, return output, err } -func (ad *ADBDriver) NewSession(capabilities option.Capabilities) (sessionInfo Session, err error) { - ad.Reset() - err = errDriverNotImplemented - return +func (ad *ADBDriver) InitSession(capabilities option.Capabilities) error { + ad.Session = &Session{} + ad.Session.Reset() + return nil } func (ad *ADBDriver) DeleteSession() (err error) { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (ad *ADBDriver) Status() (deviceStatus types.DeviceStatus, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } @@ -97,17 +96,17 @@ func (ad *ADBDriver) GetDevice() IDevice { } func (ad *ADBDriver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) Location() (location types.Location, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } @@ -145,9 +144,9 @@ func (ad *ADBDriver) getWindowSize() (size types.Size, err error) { } func (ad *ADBDriver) WindowSize() (size types.Size, err error) { - if !ad.windowSize.IsNil() { + if !ad.Session.windowSize.IsNil() { // use cached window size - return ad.windowSize, nil + return ad.Session.windowSize, nil } size, err = ad.getWindowSize() @@ -166,12 +165,12 @@ func (ad *ADBDriver) WindowSize() (size types.Size, err error) { size.Width, size.Height = size.Height, size.Width } - ad.windowSize = size // cache window size + ad.Session.windowSize = size // cache window size return size, nil } func (ad *ADBDriver) Screen() (screen ai.Screen, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } @@ -487,17 +486,17 @@ func (ad *ADBDriver) ForceTouch(x, y int, pressure float64, second ...float64) e } func (ad *ADBDriver) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) SetPasteboard(contentType types.PasteboardType, content string) (err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } @@ -620,17 +619,17 @@ func (ad *ADBDriver) Clear(packageName string) error { } func (ad *ADBDriver) PressButton(devBtn types.DeviceButton) (err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) Rotation() (rotation types.Rotation, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) SetRotation(rotation types.Rotation) (err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } @@ -660,14 +659,6 @@ func (ad *ADBDriver) Source(srcOpt ...option.SourceOption) (source string, err e return } -func (ad *ADBDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { - return info, errDriverNotImplemented -} - -func (ad *ADBDriver) LogoutNoneUI(packageName string) error { - return errDriverNotImplemented -} - func (ad *ADBDriver) sourceTree(srcOpt ...option.SourceOption) (sourceTree *Hierarchy, err error) { source, err := ad.Source() if err != nil { @@ -750,27 +741,27 @@ func (ad *ADBDriver) searchNodes(nodes []Layout, text string, opts ...option.Act } func (ad *ADBDriver) AccessibleSource() (source string, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) HealthCheck() (err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) GetAppiumSettings() (settings map[string]interface{}, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) SetAppiumSettings(settings map[string]interface{}) (ret map[string]interface{}, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (ad *ADBDriver) IsHealthy() (healthy bool, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } diff --git a/pkg/uixt/android_driver_stub_test.go b/pkg/uixt/android_driver_stub_test.go deleted file mode 100644 index c7600252..00000000 --- a/pkg/uixt/android_driver_stub_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package uixt - -import ( - "fmt" - "os" - "testing" - - "github.com/httprunner/httprunner/v5/pkg/uixt/option" -) - -var androidStubDriver *StubAndroidDriver - -func setupStubDriver(t *testing.T) { - device, err := NewAndroidDevice() - checkErr(t, err) - device.STUB = true - androidStubDriver, err = NewStubAndroidDriver(device) - checkErr(t, err) -} - -func TestHello(t *testing.T) { - setupStubDriver(t) - status, err := androidStubDriver.Status() - if err != nil { - t.Fatal(err) - } - t.Log(status) -} - -func TestSource(t *testing.T) { - setupStubDriver(t) - source, err := androidStubDriver.Source() - if err != nil { - t.Fatal(err) - } - t.Log(source) -} - -func TestLogin(t *testing.T) { - setupStubDriver(t) - info, err := androidStubDriver.LoginNoneUI("com.ss.android.ugc.aweme", "12342316231", "8517", "") - if err != nil { - t.Fatal(err) - } - t.Log(info) -} - -func TestLogout(t *testing.T) { - setupStubDriver(t) - err := androidStubDriver.LogoutNoneUI("com.ss.android.ugc.aweme") - if err != nil { - t.Fatal(err) - } -} - -func TestSwipe(t *testing.T) { - setupStubDriver(t) - err := androidStubDriver.Swipe(878, 2375, 672, 2375) - if err != nil { - t.Fatal(err) - } -} - -func TestTap(t *testing.T) { - setupStubDriver(t) - err := androidStubDriver.Tap(900, 400) - if err != nil { - t.Fatal(err) - } -} - -func TestDoubleTap(t *testing.T) { - setupStubDriver(t) - err := androidStubDriver.DoubleTap(500, 500) - if err != nil { - t.Fatal(err) - } -} - -func TestLongPress(t *testing.T) { - setupStubDriver(t) - err := androidStubDriver.Swipe(1036, 1076, 1036, 1076, - option.WithDuration(3)) - if err != nil { - t.Fatal(err) - } -} - -func TestInput(t *testing.T) { - setupStubDriver(t) - err := androidStubDriver.Input("\"哈哈\"") - if err != nil { - t.Fatal(err) - } -} - -func TestSave(t *testing.T) { - setupStubDriver(t) - raw, err := androidStubDriver.Screenshot() - if err != nil { - t.Fatal(err) - } - source, err := androidStubDriver.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) { - setupStubDriver(t) - err := androidStubDriver.AppLaunch("com.ss.android.ugc.aweme") - if err != nil { - t.Fatal(err) - } -} - -func TestAppTerminal(t *testing.T) { - setupStubDriver(t) - _, err := androidStubDriver.AppTerminate("com.ss.android.ugc.aweme") - if err != nil { - t.Fatal(err) - } -} - -func TestAppInfo(t *testing.T) { - setupStubDriver(t) - info, err := androidStubDriver.getLoginAppInfo("com.ss.android.ugc.aweme") - if err != nil { - t.Fatal(err) - } - t.Log(info) -} diff --git a/pkg/uixt/android_driver_uia2.go b/pkg/uixt/android_driver_uia2.go index b9f6e6f1..1ac4e966 100644 --- a/pkg/uixt/android_driver_uia2.go +++ b/pkg/uixt/android_driver_uia2.go @@ -6,7 +6,6 @@ import ( "encoding/json" "encoding/xml" "fmt" - "net" "net/http" "strings" "time" @@ -20,10 +19,6 @@ import ( "github.com/httprunner/httprunner/v5/pkg/uixt/types" ) -var errDriverNotImplemented = errors.New("driver method not implemented") - -const forwardToPrefix = "forward-to-" - func NewUIA2Driver(device *AndroidDevice) (*UIA2Driver, error) { log.Info().Interface("device", device).Msg("init android UIA2 driver") localPort, err := device.Forward(device.UIA2Port) @@ -32,16 +27,15 @@ func NewUIA2Driver(device *AndroidDevice) (*UIA2Driver, error) { fmt.Sprintf("forward port %d->%d failed: %v", localPort, device.UIA2Port, err)) } - conn, err := net.Dial("tcp", fmt.Sprintf(":%d", localPort)) - if err != nil { - return nil, fmt.Errorf("adb forward: %w", err) - } driver := new(UIA2Driver) - driver.client = convertToHTTPClient(conn) + err = driver.Session.InitConnection(localPort) + if err != nil { + return nil, err + } driver.Device = device.Device driver.Logcat = device.Logcat - _, err = driver.NewSession(nil) + err = driver.InitSession(nil) if err != nil { return nil, errors.Wrap(err, "create UIA2 session failed") } @@ -53,81 +47,75 @@ type UIA2Driver struct { } func (ud *UIA2Driver) resetDriver() error { - session, err := ud.NewSession(option.NewCapabilities()) - if err != nil { - return err - } - ud.sessionID = session.sessionID - return nil + return ud.InitSession(option.NewCapabilities()) } -func (ud *UIA2Driver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) { +func (ud *UIA2Driver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp DriverRawResponse, err error) { for retryCount := 1; retryCount <= 5; retryCount++ { - rawResp, err = ud.Request(method, rawURL, rawBody) + rawResp, err = ud.Session.Request(method, rawURL, rawBody) if err == nil { return } // wait for UIA2 server to resume automatically time.Sleep(3 * time.Second) - oldSessionID := ud.sessionID + oldSessionID := ud.Session.ID if err2 := ud.resetDriver(); err2 != nil { log.Err(err2).Msgf("failed to reset uia2 driver, retry count: %v", retryCount) continue } - log.Debug().Str("new session", ud.sessionID).Str("old session", oldSessionID).Msgf("successful to reset uia2 driver, retry count: %v", retryCount) + log.Debug().Str("new session", ud.Session.ID).Str("old session", oldSessionID).Msgf("successful to reset uia2 driver, retry count: %v", retryCount) if oldSessionID != "" { - rawURL = strings.Replace(rawURL, oldSessionID, ud.sessionID, 1) + rawURL = strings.Replace(rawURL, oldSessionID, ud.Session.ID, 1) } } return } -func (ud *UIA2Driver) httpGET(pathElem ...string) (rawResp rawResponse, err error) { - return ud.httpRequest(http.MethodGet, ud.concatURL(nil, pathElem...), nil) +func (ud *UIA2Driver) httpGET(pathElem ...string) (rawResp DriverRawResponse, err error) { + return ud.httpRequest(http.MethodGet, ud.Session.concatURL(nil, pathElem...), nil) } -func (ud *UIA2Driver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) { +func (ud *UIA2Driver) httpPOST(data interface{}, pathElem ...string) (rawResp DriverRawResponse, err error) { var bsJSON []byte = nil if data != nil { if bsJSON, err = json.Marshal(data); err != nil { return nil, err } } - return ud.httpRequest(http.MethodPost, ud.concatURL(nil, pathElem...), bsJSON) + return ud.httpRequest(http.MethodPost, ud.Session.concatURL(nil, pathElem...), bsJSON) } -func (ud *UIA2Driver) httpDELETE(pathElem ...string) (rawResp rawResponse, err error) { - return ud.httpRequest(http.MethodDelete, ud.concatURL(nil, pathElem...), nil) +func (ud *UIA2Driver) httpDELETE(pathElem ...string) (rawResp DriverRawResponse, err error) { + return ud.httpRequest(http.MethodDelete, ud.Session.concatURL(nil, pathElem...), nil) } -func (ud *UIA2Driver) NewSession(capabilities option.Capabilities) (sessionInfo Session, err error) { - // register(postHandler, new NewSession("/wd/hub/session")) - var rawResp rawResponse +func (ud *UIA2Driver) InitSession(capabilities option.Capabilities) (err error) { + // register(postHandler, new InitSession("/wd/hub/session")) + var rawResp DriverRawResponse data := make(map[string]interface{}) if len(capabilities) == 0 { data["capabilities"] = make(map[string]interface{}) } else { data["capabilities"] = map[string]interface{}{"alwaysMatch": capabilities} } - if rawResp, err = ud.POST(data, "/session"); err != nil { - return Session{sessionID: ""}, err + if rawResp, err = ud.Session.POST(data, "/session"); err != nil { + return err } reply := new(struct{ Value struct{ SessionId string } }) if err = json.Unmarshal(rawResp, reply); err != nil { - return Session{sessionID: ""}, err + return err } - sessionID := reply.Value.SessionId - ud.Reset() - ud.sessionID = sessionID - return Session{sessionID: sessionID}, nil + ud.Session = &Session{ID: reply.Value.SessionId} + ud.Session.Reset() + return nil } func (ud *UIA2Driver) DeleteSession() (err error) { - if ud.sessionID == "" { + if ud.Session.ID == "" { return nil } - if _, err = ud.httpDELETE("/session", ud.sessionID); err == nil { - ud.sessionID = "" + if _, err = ud.httpDELETE("/session", ud.Session.ID); err == nil { + ud.Session.ID = "" } return err @@ -135,9 +123,9 @@ func (ud *UIA2Driver) DeleteSession() (err error) { func (ud *UIA2Driver) Status() (deviceStatus types.DeviceStatus, err error) { // register(getHandler, new Status("/wd/hub/status")) - var rawResp rawResponse + var rawResp DriverRawResponse // Notice: use Driver.GET instead of httpGET to avoid loop calling - if rawResp, err = ud.GET("/status"); err != nil { + if rawResp, err = ud.Session.GET("/status"); err != nil { return types.DeviceStatus{Ready: false}, err } reply := new(struct { @@ -154,8 +142,8 @@ func (ud *UIA2Driver) Status() (deviceStatus types.DeviceStatus, err error) { func (ud *UIA2Driver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { // register(getHandler, new GetDeviceInfo("/wd/hub/session/:sessionId/appium/device/info")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionID, "appium/device/info"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpGET("/session", ud.Session.ID, "appium/device/info"); err != nil { return types.DeviceInfo{}, err } reply := new(struct{ Value struct{ types.DeviceInfo } }) @@ -168,8 +156,8 @@ func (ud *UIA2Driver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { func (ud *UIA2Driver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { // register(getHandler, new GetBatteryInfo("/wd/hub/session/:sessionId/appium/device/battery_info")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionID, "appium/device/battery_info"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpGET("/session", ud.Session.ID, "appium/device/battery_info"); err != nil { return types.BatteryInfo{}, err } reply := new(struct{ Value struct{ types.BatteryInfo } }) @@ -185,13 +173,13 @@ func (ud *UIA2Driver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { func (ud *UIA2Driver) WindowSize() (size types.Size, err error) { // register(getHandler, new GetDeviceSize("/wd/hub/session/:sessionId/window/:windowHandle/size")) - if !ud.windowSize.IsNil() { + if !ud.Session.windowSize.IsNil() { // use cached window size - return ud.windowSize, nil + return ud.Session.windowSize, nil } - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionID, "window/:windowHandle/size"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpGET("/session", ud.Session.ID, "window/:windowHandle/size"); err != nil { return types.Size{}, errors.Wrap(err, "get window size failed by UIA2 request") } reply := new(struct{ Value struct{ types.Size } }) @@ -210,14 +198,14 @@ func (ud *UIA2Driver) WindowSize() (size types.Size, err error) { size.Width, size.Height = size.Height, size.Width } - ud.windowSize = size // cache window size + ud.Session.windowSize = size // cache window size return size, nil } // PressBack simulates a short press on the BACK button. func (ud *UIA2Driver) PressBack(opts ...option.ActionOption) (err error) { // register(postHandler, new PressBack("/wd/hub/session/:sessionId/back")) - _, err = ud.httpPOST(nil, "/session", ud.sessionID, "back") + _, err = ud.httpPOST(nil, "/session", ud.Session.ID, "back") return } @@ -240,14 +228,14 @@ func (ud *UIA2Driver) PressKeyCodes(keyCode KeyCode, metaState KeyMeta, flags .. if len(flags) != 0 { data["flags"] = flags[0] } - _, err = ud.httpPOST(data, "/session", ud.sessionID, "appium/device/press_keycode") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "appium/device/press_keycode") return } func (ud *UIA2Driver) Orientation() (orientation types.Orientation, err error) { // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)] - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionID, "/orientation"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpGET("/session", ud.Session.ID, "/orientation"); err != nil { return "", err } reply := new(struct{ Value types.Orientation }) @@ -280,7 +268,7 @@ func (ud *UIA2Driver) DoubleFloatTap(x, y float64) error { }, } - _, err := ud.httpPOST(data, "/session", ud.sessionID, "actions/tap") + _, err := ud.httpPOST(data, "/session", ud.Session.ID, "actions/tap") return err } @@ -318,7 +306,7 @@ func (ud *UIA2Driver) Tap(x, y float64, opts ...option.ActionOption) (err error) // update data options in post data for extra uiautomator configurations actionOptions.UpdateData(data) - _, err = ud.httpPOST(data, "/session", ud.sessionID, "actions/tap") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "actions/tap") return err } @@ -336,7 +324,7 @@ func (ud *UIA2Driver) TouchAndHold(x, y float64, opts ...option.ActionOption) (e "duration": int(duration * 1000), }, } - _, err = ud.httpPOST(data, "/session", ud.sessionID, "touch/longclick") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "touch/longclick") return } @@ -368,7 +356,7 @@ func (ud *UIA2Driver) Drag(fromX, fromY, toX, toY float64, opts ...option.Action actionOptions.UpdateData(data) // register(postHandler, new Drag("/wd/hub/session/:sessionId/touch/drag")) - _, err = ud.httpPOST(data, "/session", ud.sessionID, "touch/drag") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "touch/drag") return } @@ -414,7 +402,7 @@ func (ud *UIA2Driver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Actio // update data options in post data for extra uiautomator configurations actionOptions.UpdateData(data) - _, err := ud.httpPOST(data, "/session", ud.sessionID, "actions/swipe") + _, err := ud.httpPOST(data, "/session", ud.Session.ID, "actions/swipe") return err } @@ -432,7 +420,7 @@ func (ud *UIA2Driver) SetPasteboard(contentType types.PasteboardType, content st "content": base64.StdEncoding.EncodeToString([]byte(content)), } // register(postHandler, new SetClipboard("/wd/hub/session/:sessionId/appium/device/set_clipboard")) - _, err = ud.httpPOST(data, "/session", ud.sessionID, "appium/device/set_clipboard") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "appium/device/set_clipboard") return } @@ -444,8 +432,8 @@ func (ud *UIA2Driver) GetPasteboard(contentType types.PasteboardType) (raw *byte data := map[string]interface{}{ "contentType": contentType[0], } - var rawResp rawResponse - if rawResp, err = ud.httpPOST(data, "/session", ud.sessionID, "appium/device/get_clipboard"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpPOST(data, "/session", ud.Session.ID, "appium/device/get_clipboard"); err != nil { return } reply := new(struct{ Value string }) @@ -475,7 +463,7 @@ func (ud *UIA2Driver) SendKeys(text string, opts ...option.ActionOption) (err er // new data options in post data for extra uiautomator configurations actionOptions.UpdateData(data) - _, err = ud.httpPOST(data, "/session", ud.sessionID, "/keys") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "/keys") } return } @@ -533,7 +521,7 @@ func (ud *UIA2Driver) SendActionKey(text string, opts ...option.ActionOption) (e // new data options in post data for extra uiautomator configurations actionOptions.UpdateData(data) - _, err = ud.httpPOST(data, "/session", ud.sessionID, "/actions/keys") + _, err = ud.httpPOST(data, "/session", ud.Session.ID, "/actions/keys") return } @@ -543,8 +531,8 @@ func (ud *UIA2Driver) Input(text string, opts ...option.ActionOption) (err error func (ud *UIA2Driver) Rotation() (rotation types.Rotation, err error) { // register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionID, "rotation"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpGET("/session", ud.Session.ID, "rotation"); err != nil { return types.Rotation{}, err } reply := new(struct{ Value types.Rotation }) @@ -564,8 +552,8 @@ func (ud *UIA2Driver) Screenshot() (raw *bytes.Buffer, err error) { func (ud *UIA2Driver) Source(srcOpt ...option.SourceOption) (source string, err error) { // register(getHandler, new Source("/wd/hub/session/:sessionId/source")) - var rawResp rawResponse - if rawResp, err = ud.httpGET("/session", ud.sessionID, "source"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = ud.httpGET("/session", ud.Session.ID, "source"); err != nil { return "", err } reply := new(struct{ Value string }) diff --git a/pkg/uixt/driver.go b/pkg/uixt/driver.go index b848aa1e..f63c75f8 100644 --- a/pkg/uixt/driver.go +++ b/pkg/uixt/driver.go @@ -30,8 +30,8 @@ var ( // current implemeted driver: ADBDriver, UIA2Driver, WDADriver, HDCDriver type IDriver interface { - // NewSession starts a new session and returns the DriverSession. - NewSession(capabilities option.Capabilities) (Session, error) + // InitSession starts a new session + InitSession(capabilities option.Capabilities) error // DeleteSession Kills application associated with that session and removes session // 1) alertsMonitor disable @@ -143,9 +143,6 @@ type IDriver interface { // Source Return application elements tree Source(srcOpt ...option.SourceOption) (string, error) - LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) - LogoutNoneUI(packageName string) error - TapByText(text string, opts ...option.ActionOption) error TapByTexts(actions ...TapTextAction) error @@ -312,9 +309,9 @@ func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...s return nil } -type rawResponse []byte +type DriverRawResponse []byte -func (r rawResponse) checkErr() (err error) { +func (r DriverRawResponse) CheckErr() (err error) { reply := new(struct { Value struct { Err string `json:"error"` @@ -338,7 +335,7 @@ func (r rawResponse) checkErr() (err error) { return } -func (r rawResponse) valueConvertToString() (s string, err error) { +func (r DriverRawResponse) ValueConvertToString() (s string, err error) { reply := new(struct{ Value string }) if err = json.Unmarshal(r, reply); err != nil { return "", errors.Wrapf(err, "json.Unmarshal failed, rawResponse: %s", string(r)) @@ -347,7 +344,7 @@ func (r rawResponse) valueConvertToString() (s string, err error) { return } -func (r rawResponse) valueConvertToBool() (b bool, err error) { +func (r DriverRawResponse) ValueConvertToBool() (b bool, err error) { reply := new(struct{ Value bool }) if err = json.Unmarshal(r, reply); err != nil { return false, err @@ -356,7 +353,7 @@ func (r rawResponse) valueConvertToBool() (b bool, err error) { return } -func (r rawResponse) valueConvertToSessionInfo() (sessionInfo Session, err error) { +func (r DriverRawResponse) ValueConvertToSessionInfo() (sessionInfo Session, err error) { reply := new(struct{ Value struct{ Session } }) if err = json.Unmarshal(r, reply); err != nil { return Session{}, err @@ -365,7 +362,7 @@ func (r rawResponse) valueConvertToSessionInfo() (sessionInfo Session, err error return } -func (r rawResponse) valueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) { +func (r DriverRawResponse) ValueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) { reply := new(struct{ Value builtinJSON.RawMessage }) if err = json.Unmarshal(r, reply); err != nil { return nil, err @@ -374,15 +371,15 @@ func (r rawResponse) valueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, return } -func (r rawResponse) valueConvertToJsonObject() (obj map[string]interface{}, err error) { +func (r DriverRawResponse) ValueConvertToJsonObject() (obj map[string]interface{}, err error) { if err = json.Unmarshal(r, &obj); err != nil { return nil, err } return } -func (r rawResponse) valueDecodeAsBase64() (raw *bytes.Buffer, err error) { - str, err := r.valueConvertToString() +func (r DriverRawResponse) ValueDecodeAsBase64() (raw *bytes.Buffer, err error) { + str, err := r.ValueConvertToString() if err != nil { return nil, errors.Wrap(err, "failed to convert value to string") } diff --git a/pkg/uixt/android_driver_stub.go b/pkg/uixt/driver_ext/shoots_android_dirver.go similarity index 59% rename from pkg/uixt/android_driver_stub.go rename to pkg/uixt/driver_ext/shoots_android_dirver.go index 51cd09ca..164e91db 100644 --- a/pkg/uixt/android_driver_stub.go +++ b/pkg/uixt/driver_ext/shoots_android_dirver.go @@ -1,12 +1,8 @@ -package uixt +package driver_ext import ( "fmt" "net" - "net/http" - "net/url" - "strconv" - "strings" "time" "github.com/pkg/errors" @@ -14,116 +10,59 @@ import ( "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 ( - StubSocketName = "com.bytest.device" - DouyinServerPort = 32316 + shootsSocketName = "com.bytest.device" + douyinServerPort = 32316 + forwardToPrefix = "forward-to-" ) -func NewStubAndroidDriver(device *AndroidDevice) (driver *StubAndroidDriver, err error) { - socketLocalPort, err := device.Forward(StubSocketName) +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, StubSocketName, err)) + socketLocalPort, shootsSocketName, 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{ + driver = &ShootsAndroidDriver{ socket: conn, timeout: 10 * time.Second, } - rawURL := fmt.Sprintf("http://forward-to-%d:%d", - serverLocalPort, DouyinServerPort) - if driver.baseURL, err = url.Parse(rawURL); err != nil { - return nil, err + 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.NewSession(nil) driver.Device = device.Device driver.Logcat = device.Logcat return driver, nil } -type StubAndroidDriver struct { - *ADBDriver +type ShootsAndroidDriver 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.baseURL.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.baseURL.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) NewSession(capabilities option.Capabilities) (Session, error) { - sad.Reset() - return Session{}, errDriverNotImplemented -} - -func (sad *StubAndroidDriver) sendCommand(packageName string, cmdType string, params map[string]interface{}, readTimeout ...time.Duration) (interface{}, error) { +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, @@ -150,24 +89,24 @@ func (sad *StubAndroidDriver) sendCommand(packageName string, cmdType string, pa return nil, err } if resultMap["Error"] != nil { - return nil, fmt.Errorf("failed to call stub command: %s", resultMap["Error"].(string)) + return nil, fmt.Errorf("failed to call shoots command: %s", resultMap["Error"].(string)) } return resultMap["Result"], nil } -func (sad *StubAndroidDriver) DeleteSession() error { +func (sad *ShootsAndroidDriver) DeleteSession() error { return sad.close() } -func (sad *StubAndroidDriver) close() error { +func (sad *ShootsAndroidDriver) close() error { if sad.socket != nil { return sad.socket.Close() } return nil } -func (sad *StubAndroidDriver) Status() (types.DeviceStatus, error) { +func (sad *ShootsAndroidDriver) Status() (types.DeviceStatus, error) { app, err := sad.GetForegroundApp() if err != nil { return types.DeviceStatus{}, err @@ -176,11 +115,11 @@ func (sad *StubAndroidDriver) Status() (types.DeviceStatus, error) { if err != nil { return types.DeviceStatus{}, err } - log.Info().Msg(fmt.Sprintf("ping stub result :%v", res)) + log.Info().Msg(fmt.Sprintf("ping shoots result :%v", res)) return types.DeviceStatus{}, nil } -func (sad *StubAndroidDriver) Source(srcOpt ...option.SourceOption) (source string, err error) { +func (sad *ShootsAndroidDriver) Source(srcOpt ...option.SourceOption) (source string, err error) { app, err := sad.GetForegroundApp() if err != nil { return "", err @@ -198,7 +137,7 @@ func (sad *StubAndroidDriver) Source(srcOpt ...option.SourceOption) (source stri return res.(string), nil } -func (sad *StubAndroidDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { +func (sad *ShootsAndroidDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { params := map[string]interface{}{ "phone": phoneNumber, } @@ -209,11 +148,11 @@ func (sad *StubAndroidDriver) LoginNoneUI(packageName, phoneNumber string, captc } else { return info, fmt.Errorf("password and capcha is empty") } - resp, err := sad.httpPOST(params, "/host", "/login", "account") + resp, err := sad.Session.POST(params, "/host", "/login", "account") if err != nil { return info, err } - res, err := resp.valueConvertToJsonObject() + res, err := resp.ValueConvertToJsonObject() if err != nil { return info, err } @@ -231,12 +170,12 @@ func (sad *StubAndroidDriver) LoginNoneUI(packageName, phoneNumber string, captc return info, nil } -func (sad *StubAndroidDriver) LogoutNoneUI(packageName string) error { - resp, err := sad.httpGET("/host", "/logout") +func (sad *ShootsAndroidDriver) LogoutNoneUI(packageName string) error { + resp, err := sad.Session.GET("/host", "/logout") if err != nil { return err } - res, err := resp.valueConvertToJsonObject() + res, err := resp.ValueConvertToJsonObject() if err != nil { return err } @@ -254,7 +193,7 @@ func (sad *StubAndroidDriver) LogoutNoneUI(packageName string) error { return nil } -func (sad *StubAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string, captcha string) error { +func (sad *ShootsAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string, captcha string) error { params := map[string]interface{}{ "ClassName": "qe.python.test.LoginUtil", "Method": "loginSync", @@ -269,7 +208,7 @@ func (sad *StubAndroidDriver) LoginNoneUIDynamic(packageName, phoneNumber string return nil } -func (sad *StubAndroidDriver) SetHDTStatus(status bool) error { +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)) @@ -288,12 +227,12 @@ func (sad *StubAndroidDriver) SetHDTStatus(status bool) error { return nil } -func (sad *StubAndroidDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) { - resp, err := sad.httpGET("/host", "/app", "/info") +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() + res, err := resp.ValueConvertToJsonObject() if err != nil { return info, err } @@ -310,3 +249,9 @@ func (sad *StubAndroidDriver) getLoginAppInfo(packageName string) (info AppLogin } 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"` +} diff --git a/pkg/uixt/driver_ext/shoots_android_driver_test.go b/pkg/uixt/driver_ext/shoots_android_driver_test.go new file mode 100644 index 00000000..8295d8d8 --- /dev/null +++ b/pkg/uixt/driver_ext/shoots_android_driver_test.go @@ -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) +} diff --git a/pkg/uixt/ios_driver_stub.go b/pkg/uixt/driver_ext/shoots_ios_driver.go similarity index 70% rename from pkg/uixt/ios_driver_stub.go rename to pkg/uixt/driver_ext/shoots_ios_driver.go index b3383288..21baa4de 100644 --- a/pkg/uixt/ios_driver_stub.go +++ b/pkg/uixt/driver_ext/shoots_ios_driver.go @@ -1,4 +1,4 @@ -package uixt +package driver_ext import ( "bytes" @@ -12,6 +12,7 @@ import ( "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" @@ -22,14 +23,14 @@ const ( defaultDouyinServerPort = 32921 ) -func NewStubIOSDriver(device *IOSDevice) (driver *StubIOSDriver, err error) { - localStubPort, err := builtin.GetFreePort() +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(localStubPort, defaultBightInsightPort); err != nil { + if err = device.Forward(localShootsPort, defaultBightInsightPort); err != nil { return nil, errors.Wrap(code.DeviceHTTPDriverError, fmt.Sprintf("forward tcp port failed: %v", err)) } @@ -39,61 +40,54 @@ func NewStubIOSDriver(device *IOSDevice) (driver *StubIOSDriver, err error) { return nil, errors.Wrap(code.DeviceHTTPDriverError, fmt.Sprintf("get free port failed: %v", err)) } - if err = device.forward(localServerPort, defaultDouyinServerPort); err != nil { + 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 = &StubIOSDriver{} + driver = &ShootsIOSDriver{} driver.device = device - driver.bightInsightPrefix = fmt.Sprintf("http://%s:%d", host, localStubPort) + driver.bightInsightPrefix = fmt.Sprintf("http://%s:%d", host, localShootsPort) driver.serverPrefix = fmt.Sprintf("http://%s:%d", host, localServerPort) driver.timeout = timeout - driver.client = &http.Client{ - Timeout: time.Second * 10, // 设置超时时间为 10 秒 - } return driver, nil } -type StubIOSDriver struct { - *WDADriver +type ShootsIOSDriver struct { + *uixt.WDADriver bightInsightPrefix string serverPrefix string timeout time.Duration - device *IOSDevice + device *uixt.IOSDevice } -func (s *StubIOSDriver) setUpWda() (err error) { +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("stub driver failed to init wda driver") + log.Error().Err(err).Msg("failed to init WDA driver for shoots IOS") return err } - s.WDADriver = driver.(*WDADriver) + s.WDADriver = driver.(*uixt.WDADriver) } return nil } -// NewSession starts a new session and returns the DriverSession. -func (s *StubIOSDriver) NewSession(capabilities option.Capabilities) (Session, error) { - err := s.setUpWda() - if err != nil { - return Session{}, err - } - return s.WDADriver.NewSession(capabilities) +// 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 *StubIOSDriver) DeleteSession() error { +func (s *ShootsIOSDriver) DeleteSession() error { err := s.setUpWda() if err != nil { return err @@ -101,7 +95,7 @@ func (s *StubIOSDriver) DeleteSession() error { return s.WDADriver.DeleteSession() } -func (s *StubIOSDriver) Status() (types.DeviceStatus, error) { +func (s *ShootsIOSDriver) Status() (types.DeviceStatus, error) { err := s.setUpWda() if err != nil { return types.DeviceStatus{}, err @@ -109,7 +103,7 @@ func (s *StubIOSDriver) Status() (types.DeviceStatus, error) { return s.WDADriver.Status() } -func (s *StubIOSDriver) DeviceInfo() (types.DeviceInfo, error) { +func (s *ShootsIOSDriver) DeviceInfo() (types.DeviceInfo, error) { err := s.setUpWda() if err != nil { return types.DeviceInfo{}, err @@ -117,7 +111,7 @@ func (s *StubIOSDriver) DeviceInfo() (types.DeviceInfo, error) { return s.WDADriver.DeviceInfo() } -func (s *StubIOSDriver) Location() (types.Location, error) { +func (s *ShootsIOSDriver) Location() (types.Location, error) { err := s.setUpWda() if err != nil { return types.Location{}, err @@ -125,7 +119,7 @@ func (s *StubIOSDriver) Location() (types.Location, error) { return s.WDADriver.Location() } -func (s *StubIOSDriver) BatteryInfo() (types.BatteryInfo, error) { +func (s *ShootsIOSDriver) BatteryInfo() (types.BatteryInfo, error) { err := s.setUpWda() if err != nil { return types.BatteryInfo{}, err @@ -136,7 +130,7 @@ func (s *StubIOSDriver) BatteryInfo() (types.BatteryInfo, error) { // 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 *StubIOSDriver) WindowSize() (types.Size, error) { +func (s *ShootsIOSDriver) WindowSize() (types.Size, error) { err := s.setUpWda() if err != nil { return types.Size{}, err @@ -144,7 +138,7 @@ func (s *StubIOSDriver) WindowSize() (types.Size, error) { return s.WDADriver.WindowSize() } -func (s *StubIOSDriver) Screen() (ai.Screen, error) { +func (s *ShootsIOSDriver) Screen() (ai.Screen, error) { err := s.setUpWda() if err != nil { return ai.Screen{}, err @@ -152,7 +146,7 @@ func (s *StubIOSDriver) Screen() (ai.Screen, error) { return s.WDADriver.Screen() } -func (s *StubIOSDriver) Scale() (float64, error) { +func (s *ShootsIOSDriver) Scale() (float64, error) { err := s.setUpWda() if err != nil { return 0, err @@ -161,7 +155,7 @@ func (s *StubIOSDriver) Scale() (float64, error) { } // Homescreen Forces the device under test to switch to the home screen -func (s *StubIOSDriver) Homescreen() error { +func (s *ShootsIOSDriver) Homescreen() error { err := s.setUpWda() if err != nil { return err @@ -169,7 +163,7 @@ func (s *StubIOSDriver) Homescreen() error { return s.WDADriver.Homescreen() } -func (s *StubIOSDriver) Unlock() (err error) { +func (s *ShootsIOSDriver) Unlock() (err error) { err = s.setUpWda() if err != nil { return err @@ -179,7 +173,7 @@ func (s *StubIOSDriver) Unlock() (err error) { // AppLaunch Launch an application with given bundle identifier in scope of current session. // !This method is only available since Xcode9 SDK -func (s *StubIOSDriver) AppLaunch(packageName string) error { +func (s *ShootsIOSDriver) AppLaunch(packageName string) error { err := s.setUpWda() if err != nil { return err @@ -189,7 +183,7 @@ func (s *StubIOSDriver) AppLaunch(packageName string) error { // 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 *StubIOSDriver) AppTerminate(packageName string) (bool, error) { +func (s *ShootsIOSDriver) AppTerminate(packageName string) (bool, error) { err := s.setUpWda() if err != nil { return false, err @@ -198,7 +192,7 @@ func (s *StubIOSDriver) AppTerminate(packageName string) (bool, error) { } // GetForegroundApp returns current foreground app package name and activity name -func (s *StubIOSDriver) GetForegroundApp() (app types.AppInfo, err error) { +func (s *ShootsIOSDriver) GetForegroundApp() (app types.AppInfo, err error) { err = s.setUpWda() if err != nil { return types.AppInfo{}, err @@ -207,7 +201,7 @@ func (s *StubIOSDriver) GetForegroundApp() (app types.AppInfo, err error) { } // StartCamera Starts a new camera for recording -func (s *StubIOSDriver) StartCamera() error { +func (s *ShootsIOSDriver) StartCamera() error { err := s.setUpWda() if err != nil { return err @@ -216,7 +210,7 @@ func (s *StubIOSDriver) StartCamera() error { } // StopCamera Stops the camera for recording -func (s *StubIOSDriver) StopCamera() error { +func (s *ShootsIOSDriver) StopCamera() error { err := s.setUpWda() if err != nil { return err @@ -224,7 +218,7 @@ func (s *StubIOSDriver) StopCamera() error { return s.WDADriver.StopCamera() } -func (s *StubIOSDriver) Orientation() (orientation types.Orientation, err error) { +func (s *ShootsIOSDriver) Orientation() (orientation types.Orientation, err error) { err = s.setUpWda() if err != nil { return types.OrientationPortrait, err @@ -233,7 +227,7 @@ func (s *StubIOSDriver) Orientation() (orientation types.Orientation, err error) } // Tap Sends a tap event at the coordinate. -func (s *StubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -242,7 +236,7 @@ func (s *StubIOSDriver) Tap(x, y float64, opts ...option.ActionOption) error { } // DoubleTap Sends a double tap event at the coordinate. -func (s *StubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -253,7 +247,7 @@ func (s *StubIOSDriver) DoubleTap(x, y float64, opts ...option.ActionOption) err // TouchAndHold Initiates a long-press gesture at the coordinate, holding for the specified duration. // // second: The default value is 1 -func (s *StubIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -263,7 +257,7 @@ func (s *StubIOSDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) // 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 *StubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -272,7 +266,7 @@ func (s *StubIOSDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.Acti } // SetPasteboard Sets data to the general pasteboard -func (s *StubIOSDriver) SetPasteboard(contentType types.PasteboardType, content string) error { +func (s *ShootsIOSDriver) SetPasteboard(contentType types.PasteboardType, content string) error { err := s.setUpWda() if err != nil { return err @@ -283,7 +277,7 @@ func (s *StubIOSDriver) SetPasteboard(contentType types.PasteboardType, 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 *StubIOSDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { +func (s *ShootsIOSDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { err = s.setUpWda() if err != nil { return nil, err @@ -291,7 +285,7 @@ func (s *StubIOSDriver) GetPasteboard(contentType types.PasteboardType) (raw *by return s.WDADriver.GetPasteboard(contentType) } -func (s *StubIOSDriver) SetIme(ime string) error { +func (s *ShootsIOSDriver) SetIme(ime string) error { err := s.setUpWda() if err != nil { return err @@ -302,7 +296,7 @@ func (s *StubIOSDriver) SetIme(ime string) error { // 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 *StubIOSDriver) SendKeys(text string, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) SendKeys(text string, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -311,7 +305,7 @@ func (s *StubIOSDriver) SendKeys(text string, opts ...option.ActionOption) error } // Input works like SendKeys -func (s *StubIOSDriver) Input(text string, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) Input(text string, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -319,7 +313,7 @@ func (s *StubIOSDriver) Input(text string, opts ...option.ActionOption) error { return s.WDADriver.Input(text, opts...) } -func (s *StubIOSDriver) Clear(packageName string) error { +func (s *ShootsIOSDriver) Clear(packageName string) error { err := s.setUpWda() if err != nil { return err @@ -328,7 +322,7 @@ func (s *StubIOSDriver) Clear(packageName string) error { } // PressButton Presses the corresponding hardware button on the device -func (s *StubIOSDriver) PressButton(devBtn types.DeviceButton) error { +func (s *ShootsIOSDriver) PressButton(devBtn types.DeviceButton) error { err := s.setUpWda() if err != nil { return err @@ -337,7 +331,7 @@ func (s *StubIOSDriver) PressButton(devBtn types.DeviceButton) error { } // PressBack Presses the back button -func (s *StubIOSDriver) PressBack(opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) PressBack(opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -345,7 +339,7 @@ func (s *StubIOSDriver) PressBack(opts ...option.ActionOption) error { return s.WDADriver.PressBack(opts...) } -func (s *StubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { +func (s *ShootsIOSDriver) PressKeyCode(keyCode uixt.KeyCode) (err error) { err = s.setUpWda() if err != nil { return err @@ -353,7 +347,7 @@ func (s *StubIOSDriver) PressKeyCode(keyCode KeyCode) (err error) { return s.WDADriver.PressKeyCode(keyCode) } -func (s *StubIOSDriver) Screenshot() (*bytes.Buffer, error) { +func (s *ShootsIOSDriver) Screenshot() (*bytes.Buffer, error) { err := s.setUpWda() if err != nil { return nil, err @@ -374,7 +368,7 @@ func (s *StubIOSDriver) Screenshot() (*bytes.Buffer, error) { //return bytes.NewBuffer(imageBytes), nil } -func (s *StubIOSDriver) TapByText(text string, opts ...option.ActionOption) error { +func (s *ShootsIOSDriver) TapByText(text string, opts ...option.ActionOption) error { err := s.setUpWda() if err != nil { return err @@ -382,7 +376,7 @@ func (s *StubIOSDriver) TapByText(text string, opts ...option.ActionOption) erro return s.WDADriver.TapByText(text, opts...) } -func (s *StubIOSDriver) TapByTexts(actions ...TapTextAction) error { +func (s *ShootsIOSDriver) TapByTexts(actions ...uixt.TapTextAction) error { err := s.setUpWda() if err != nil { return err @@ -391,7 +385,7 @@ func (s *StubIOSDriver) TapByTexts(actions ...TapTextAction) error { } // AccessibleSource Return application elements accessibility tree -func (s *StubIOSDriver) AccessibleSource() (string, error) { +func (s *ShootsIOSDriver) AccessibleSource() (string, error) { err := s.setUpWda() if err != nil { return "", err @@ -404,7 +398,7 @@ func (s *StubIOSDriver) AccessibleSource() (string, error) { // Checks health of XCTest by: // 1) Querying application for some elements, // 2) Triggering some device events. -func (s *StubIOSDriver) HealthCheck() error { +func (s *ShootsIOSDriver) HealthCheck() error { err := s.setUpWda() if err != nil { return err @@ -412,7 +406,7 @@ func (s *StubIOSDriver) HealthCheck() error { return s.WDADriver.HealthCheck() } -func (s *StubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { +func (s *ShootsIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { err := s.setUpWda() if err != nil { return nil, err @@ -420,7 +414,7 @@ func (s *StubIOSDriver) GetAppiumSettings() (map[string]interface{}, error) { return s.WDADriver.GetAppiumSettings() } -func (s *StubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) { +func (s *ShootsIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) { err := s.setUpWda() if err != nil { return nil, err @@ -428,7 +422,7 @@ func (s *StubIOSDriver) SetAppiumSettings(settings map[string]interface{}) (map[ return s.WDADriver.SetAppiumSettings(settings) } -func (s *StubIOSDriver) IsHealthy() (bool, error) { +func (s *ShootsIOSDriver) IsHealthy() (bool, error) { err := s.setUpWda() if err != nil { return false, err @@ -437,7 +431,7 @@ func (s *StubIOSDriver) IsHealthy() (bool, error) { } // triggers the log capture and returns the log entries -func (s *StubIOSDriver) StartCaptureLog(identifier ...string) (err error) { +func (s *ShootsIOSDriver) StartCaptureLog(identifier ...string) (err error) { err = s.setUpWda() if err != nil { return err @@ -445,7 +439,7 @@ func (s *StubIOSDriver) StartCaptureLog(identifier ...string) (err error) { return s.WDADriver.StartCaptureLog(identifier...) } -func (s *StubIOSDriver) StopCaptureLog() (result interface{}, err error) { +func (s *ShootsIOSDriver) StopCaptureLog() (result interface{}, err error) { err = s.setUpWda() if err != nil { return nil, err @@ -453,7 +447,7 @@ func (s *StubIOSDriver) StopCaptureLog() (result interface{}, err error) { return s.WDADriver.StopCaptureLog() } -func (s *StubIOSDriver) GetDriverResults() []*DriverRequests { +func (s *ShootsIOSDriver) GetDriverResults() []*uixt.DriverRequests { err := s.setUpWda() if err != nil { return nil @@ -461,7 +455,7 @@ func (s *StubIOSDriver) GetDriverResults() []*DriverRequests { return s.WDADriver.GetDriverResults() } -func (s *StubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) { +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 @@ -469,7 +463,7 @@ func (s *StubIOSDriver) Source(srcOpt ...option.SourceOption) (string, error) { return string(resp), nil } -func (s *StubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { +func (s *ShootsIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { params := map[string]interface{}{ "phone": phoneNumber, } @@ -488,7 +482,7 @@ func (s *StubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, pa if err != nil { return info, err } - res, err := resp.valueConvertToJsonObject() + res, err := resp.ValueConvertToJsonObject() if err != nil { return info, err } @@ -507,12 +501,12 @@ func (s *StubIOSDriver) LoginNoneUI(packageName, phoneNumber string, captcha, pa return info, nil } -func (s *StubIOSDriver) LogoutNoneUI(packageName string) error { +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() + res, err := resp.ValueConvertToJsonObject() if err != nil { return err } @@ -526,17 +520,17 @@ func (s *StubIOSDriver) LogoutNoneUI(packageName string) error { return nil } -func (s *StubIOSDriver) TearDown() error { - s.client.CloseIdleConnections() +func (s *ShootsIOSDriver) TearDown() error { + s.WDADriver.TearDown() return nil } -func (s *StubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, err error) { +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() + res, err := resp.ValueConvertToJsonObject() if err != nil { return info, err } @@ -553,6 +547,6 @@ func (s *StubIOSDriver) getLoginAppInfo(packageName string) (info AppLoginInfo, return info, nil } -func (s *StubIOSDriver) GetSession() *Session { +func (s *ShootsIOSDriver) GetSession() *uixt.Session { return s.Session } diff --git a/pkg/uixt/ios_driver_stub_test.go b/pkg/uixt/driver_ext/shoots_ios_driver_test.go similarity index 59% rename from pkg/uixt/ios_driver_stub_test.go rename to pkg/uixt/driver_ext/shoots_ios_driver_test.go index 9451db59..71bbefff 100644 --- a/pkg/uixt/ios_driver_stub_test.go +++ b/pkg/uixt/driver_ext/shoots_ios_driver_test.go @@ -1,4 +1,4 @@ -package uixt +package driver_ext import ( "fmt" @@ -7,71 +7,71 @@ import ( "testing" "github.com/httprunner/httprunner/v5/internal/builtin" + "github.com/httprunner/httprunner/v5/pkg/uixt" "github.com/httprunner/httprunner/v5/pkg/uixt/option" ) var ( - iOSStubDriver IDriverExt - iOSDevice *IOSDevice + shootsIOSDriver uixt.IDriverExt + iOSDevice *uixt.IOSDevice ) -func setupiOSStubDriver(t *testing.T) { +func setupShootsIOSDriver(t *testing.T) { var err error - iOSDevice, err = NewIOSDevice( + iOSDevice, err = uixt.NewIOSDevice( option.WithWDAPort(8700), - option.WithWDAMjpegPort(8800), - option.WithIOSStub(false)) + option.WithWDAMjpegPort(8800)) checkErr(t, err) - iOSStubDriver, err = iOSDevice.NewDriver() + shootsIOSDriver, err = iOSDevice.NewDriver() checkErr(t, err) } func TestIOSLogin(t *testing.T) { - setupiOSStubDriver(t) - info, err := iOSStubDriver.GetDriver().LoginNoneUI("", "12342316231", "8517", "") + setupShootsIOSDriver(t) + info, err := shootsIOSDriver.(*ShootsIOSDriver).LoginNoneUI("", "12342316231", "8517", "") checkErr(t, err) t.Log(info) } func TestIOSLogout(t *testing.T) { - setupiOSStubDriver(t) - err := iOSStubDriver.GetDriver().LogoutNoneUI("") + setupShootsIOSDriver(t) + err := shootsIOSDriver.(*ShootsIOSDriver).LogoutNoneUI("") checkErr(t, err) } func TestIOSIsLogin(t *testing.T) { - setupiOSStubDriver(t) - err := iOSStubDriver.GetDriver().LogoutNoneUI("") + setupShootsIOSDriver(t) + err := shootsIOSDriver.(*ShootsIOSDriver).LogoutNoneUI("") checkErr(t, err) } func TestIOSSource(t *testing.T) { - setupiOSStubDriver(t) - source, err := iOSStubDriver.GetDriver().Source() + setupShootsIOSDriver(t) + source, err := shootsIOSDriver.GetDriver().Source() checkErr(t, err) t.Log(source) } func TestIOSForeground(t *testing.T) { - setupiOSStubDriver(t) - app, err := iOSStubDriver.GetDriver().GetForegroundApp() + setupShootsIOSDriver(t) + app, err := shootsIOSDriver.GetDriver().GetForegroundApp() checkErr(t, err) t.Log(app) } func TestIOSSwipe(t *testing.T) { - setupiOSStubDriver(t) - iOSStubDriver.GetDriver().Swipe(540, 0, 540, 1000) + setupShootsIOSDriver(t) + shootsIOSDriver.GetDriver().Swipe(540, 0, 540, 1000) } func TestIOSSave(t *testing.T) { - setupiOSStubDriver(t) - raw, err := iOSStubDriver.GetDriver().Screenshot() + setupShootsIOSDriver(t) + raw, err := shootsIOSDriver.GetDriver().Screenshot() if err != nil { t.Fatal(err) } - source, err := iOSStubDriver.GetDriver().Source() + source, err := shootsIOSDriver.GetDriver().Source() if err != nil { t.Fatal(err) } @@ -90,12 +90,12 @@ func TestIOSSave(t *testing.T) { } func TestListen(t *testing.T) { - setupiOSStubDriver(t) + setupShootsIOSDriver(t) localPort, err := builtin.GetFreePort() if err != nil { t.Fatal(err) } - err = iOSDevice.forward(localPort, 8800) + err = iOSDevice.Forward(localPort, 8800) if err != nil { t.Fatal(err) } diff --git a/pkg/uixt/driver_session.go b/pkg/uixt/driver_session.go index 1735291e..4d4a7a98 100644 --- a/pkg/uixt/driver_session.go +++ b/pkg/uixt/driver_session.go @@ -3,6 +3,7 @@ package uixt import ( "bytes" "context" + "fmt" "io" "net" "net/http" @@ -19,9 +20,9 @@ import ( type Session struct { // ctx context.Context - sessionID string - baseURL *url.URL - client *http.Client + ID string + baseURL *url.URL + client *http.Client // cache to avoid repeated query scale float64 @@ -43,14 +44,18 @@ func (d *Session) addRequestResult(driverResult *DriverRequests) { d.requests = append(d.requests, driverResult) } +func (d *Session) Init(baseUrl string) error { + var err error + d.baseURL, err = url.Parse(baseUrl) + return err +} + func (d *Session) Reset() { d.screenResults = make([]*ScreenResult, 0) d.requests = make([]*DriverRequests, 0) d.e2eDelay = nil } -type Attachments map[string]interface{} - func (d *Session) GetData(withReset bool) Attachments { data := Attachments{ "screen_results": d.screenResults, @@ -67,6 +72,8 @@ func (d *Session) GetData(withReset bool) Attachments { return data } +type Attachments map[string]interface{} + type DriverRequests struct { RequestMethod string `json:"request_method"` RequestUrl string `json:"request_url"` @@ -91,11 +98,11 @@ func (wd *Session) concatURL(u *url.URL, elem ...string) string { return tmp.String() } -func (wd *Session) GET(pathElem ...string) (rawResp rawResponse, err error) { +func (wd *Session) GET(pathElem ...string) (rawResp DriverRawResponse, err error) { return wd.Request(http.MethodGet, wd.concatURL(nil, pathElem...), nil) } -func (wd *Session) POST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) { +func (wd *Session) POST(data interface{}, pathElem ...string) (rawResp DriverRawResponse, err error) { var bsJSON []byte = nil if data != nil { if bsJSON, err = json.Marshal(data); err != nil { @@ -105,11 +112,11 @@ func (wd *Session) POST(data interface{}, pathElem ...string) (rawResp rawRespon return wd.Request(http.MethodPost, wd.concatURL(nil, pathElem...), bsJSON) } -func (wd *Session) DELETE(pathElem ...string) (rawResp rawResponse, err error) { +func (wd *Session) DELETE(pathElem ...string) (rawResp DriverRawResponse, err error) { return wd.Request(http.MethodDelete, wd.concatURL(nil, pathElem...), nil) } -func (wd *Session) Request(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) { +func (wd *Session) Request(method string, rawURL string, rawBody []byte) (rawResp DriverRawResponse, err error) { driverResult := &DriverRequests{ RequestMethod: method, RequestUrl: rawURL, @@ -179,7 +186,7 @@ func (wd *Session) Request(method string, rawURL string, rawBody []byte) (rawRes return nil, err } - if err = rawResp.checkErr(); err != nil { + if err = rawResp.CheckErr(); err != nil { if resp.StatusCode == http.StatusOK { return rawResp, nil } @@ -189,13 +196,22 @@ func (wd *Session) Request(method string, rawURL string, rawBody []byte) (rawRes return } -func convertToHTTPClient(conn net.Conn) *http.Client { +func (wd *Session) InitConnection(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) + } + wd.client = NewHTTPClientWithConnection(conn, 30*time.Second) + return nil +} + +func NewHTTPClientWithConnection(conn net.Conn, timeout time.Duration) *http.Client { return &http.Client{ Transport: &http.Transport{ DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { return conn, nil }, }, - Timeout: 30 * time.Second, + Timeout: timeout, } } diff --git a/pkg/uixt/harmony_driver_hdc.go b/pkg/uixt/harmony_driver_hdc.go index 3acacc2f..cae70cb3 100644 --- a/pkg/uixt/harmony_driver_hdc.go +++ b/pkg/uixt/harmony_driver_hdc.go @@ -41,18 +41,19 @@ func NewHDCDriver(device *HarmonyDevice) (driver *HDCDriver, err error) { return nil, err } driver.uiDriver = uiDriver - driver.NewSession(nil) + driver.InitSession(nil) return } -func (hd *HDCDriver) NewSession(capabilities option.Capabilities) (Session, error) { - hd.Reset() +func (hd *HDCDriver) InitSession(capabilities option.Capabilities) error { + hd.Session = &Session{} + hd.Session.Reset() hd.Unlock() - return Session{}, errDriverNotImplemented + return nil } func (hd *HDCDriver) DeleteSession() error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) GetSession() *Session { @@ -60,7 +61,7 @@ func (hd *HDCDriver) GetSession() *Session { } func (hd *HDCDriver) Status() (types.DeviceStatus, error) { - return types.DeviceStatus{}, errDriverNotImplemented + return types.DeviceStatus{}, types.ErrDriverNotImplemented } func (hd *HDCDriver) GetDevice() IDevice { @@ -68,15 +69,15 @@ func (hd *HDCDriver) GetDevice() IDevice { } func (hd *HDCDriver) DeviceInfo() (types.DeviceInfo, error) { - return types.DeviceInfo{}, errDriverNotImplemented + return types.DeviceInfo{}, types.ErrDriverNotImplemented } func (hd *HDCDriver) Location() (types.Location, error) { - return types.Location{}, errDriverNotImplemented + return types.Location{}, types.ErrDriverNotImplemented } func (hd *HDCDriver) BatteryInfo() (types.BatteryInfo, error) { - return types.BatteryInfo{}, errDriverNotImplemented + return types.BatteryInfo{}, types.ErrDriverNotImplemented } func (hd *HDCDriver) WindowSize() (size types.Size, err error) { @@ -91,7 +92,7 @@ func (hd *HDCDriver) WindowSize() (size types.Size, err error) { } func (hd *HDCDriver) Screen() (ai.Screen, error) { - return ai.Screen{}, errDriverNotImplemented + return ai.Screen{}, types.ErrDriverNotImplemented } func (hd *HDCDriver) Scale() (float64, error) { @@ -126,7 +127,7 @@ func (hd *HDCDriver) Unlock() (err error) { func (hd *HDCDriver) AppLaunch(packageName string) error { // Todo - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) AppTerminate(packageName string) (bool, error) { @@ -140,7 +141,7 @@ func (hd *HDCDriver) AppTerminate(packageName string) (bool, error) { func (hd *HDCDriver) GetForegroundApp() (app types.AppInfo, err error) { // Todo - return types.AppInfo{}, errDriverNotImplemented + return types.AppInfo{}, types.ErrDriverNotImplemented } func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...string) error { @@ -149,11 +150,11 @@ func (hd *HDCDriver) AssertForegroundApp(packageName string, activityType ...str } func (hd *HDCDriver) StartCamera() error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) StopCamera() error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) Orientation() (orientation types.Orientation, err error) { @@ -178,15 +179,15 @@ func (hd *HDCDriver) Tap(x, y float64, opts ...option.ActionOption) error { } func (hd *HDCDriver) DoubleTap(x, y float64, opts ...option.ActionOption) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) TouchAndHold(x, y float64, opts ...option.ActionOption) (err error) { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionOption) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } // Swipe works like Drag, but `pressForDuration` value is 0 @@ -215,15 +216,15 @@ func (hd *HDCDriver) Swipe(fromX, fromY, toX, toY float64, opts ...option.Action } func (hd *HDCDriver) SetPasteboard(contentType types.PasteboardType, content string) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { - return nil, errDriverNotImplemented + return nil, types.ErrDriverNotImplemented } func (hd *HDCDriver) SetIme(ime string) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) SendKeys(text string, opts ...option.ActionOption) error { @@ -235,11 +236,11 @@ func (hd *HDCDriver) Input(text string, opts ...option.ActionOption) error { } func (hd *HDCDriver) Clear(packageName string) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) PressButton(devBtn types.DeviceButton) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) PressBack(opts ...option.ActionOption) error { @@ -251,7 +252,7 @@ func (hd *HDCDriver) Backspace(count int, opts ...option.ActionOption) (err erro } func (hd *HDCDriver) PressKeyCode(keyCode KeyCode) (err error) { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) PressHarmonyKeyCode(keyCode ghdc.KeyCode) (err error) { @@ -282,45 +283,36 @@ func (hd *HDCDriver) Source(srcOpt ...option.SourceOption) (string, error) { return "", nil } -func (hd *HDCDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { - err = errDriverNotImplemented - return -} - -func (hd *HDCDriver) LogoutNoneUI(packageName string) error { - return errDriverNotImplemented -} - func (hd *HDCDriver) TapByText(text string, opts ...option.ActionOption) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) TapByTexts(actions ...TapTextAction) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) AccessibleSource() (string, error) { - return "", errDriverNotImplemented + return "", types.ErrDriverNotImplemented } func (hd *HDCDriver) HealthCheck() error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) GetAppiumSettings() (map[string]interface{}, error) { - return nil, errDriverNotImplemented + return nil, types.ErrDriverNotImplemented } func (hd *HDCDriver) SetAppiumSettings(settings map[string]interface{}) (map[string]interface{}, error) { - return nil, errDriverNotImplemented + return nil, types.ErrDriverNotImplemented } func (hd *HDCDriver) IsHealthy() (bool, error) { - return false, errDriverNotImplemented + return false, types.ErrDriverNotImplemented } func (hd *HDCDriver) StartCaptureLog(identifier ...string) (err error) { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (hd *HDCDriver) StopCaptureLog() (result interface{}, err error) { @@ -345,11 +337,11 @@ func (hd *HDCDriver) TearDown() error { } func (hd *HDCDriver) Rotation() (rotation types.Rotation, err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } func (hd *HDCDriver) SetRotation(rotation types.Rotation) (err error) { - err = errDriverNotImplemented + err = types.ErrDriverNotImplemented return } diff --git a/pkg/uixt/ios_device.go b/pkg/uixt/ios_device.go index 1447c764..4c93c9a2 100644 --- a/pkg/uixt/ios_device.go +++ b/pkg/uixt/ios_device.go @@ -228,35 +228,10 @@ func (dev *IOSDevice) getAppInfo(packageName string) (appInfo types.AppInfo, err } func (dev *IOSDevice) NewDriver() (driverExt IDriverExt, err error) { - var driver IDriver - if dev.STUB { - driver, err = NewStubIOSDriver(dev) - if err != nil { - return nil, errors.Wrap(err, "failed to init Stub driver") - } - } else { - driver, err := NewWDADriver(dev) - if err != nil { - return nil, errors.Wrap(err, "failed to init WDA driver") - } - settings, err := driver.SetAppiumSettings(map[string]interface{}{ - "snapshotMaxDepth": dev.SnapshotMaxDepth, - "acceptAlertButtonSelector": dev.AcceptAlertButtonSelector, - }) - if err != nil { - return nil, errors.Wrap(err, "failed to set appium WDA settings") - } - log.Info().Interface("appiumWDASettings", settings).Msg("set appium WDA settings") + driver, err := NewWDADriver(dev) + if err != nil { + return nil, errors.Wrap(err, "failed to init WDA driver") } - - if dev.ResetHomeOnStartup { - log.Info().Msg("go back to home screen") - if err = driver.Homescreen(); err != nil { - return nil, errors.Wrap(code.MobileUIDriverError, - fmt.Sprintf("go back to home screen failed: %v", err)) - } - } - settings, err := driver.SetAppiumSettings(map[string]interface{}{ "snapshotMaxDepth": dev.SnapshotMaxDepth, "acceptAlertButtonSelector": dev.AcceptAlertButtonSelector, @@ -266,6 +241,13 @@ func (dev *IOSDevice) NewDriver() (driverExt IDriverExt, err error) { } log.Info().Interface("appiumWDASettings", settings).Msg("set appium WDA settings") + if dev.ResetHomeOnStartup { + log.Info().Msg("go back to home screen") + if err = driver.Homescreen(); err != nil { + return nil, errors.Wrap(code.MobileUIDriverError, + fmt.Sprintf("go back to home screen failed: %v", err)) + } + } if dev.LogOn { err = driver.StartCaptureLog("hrp_wda_log") if err != nil { @@ -319,7 +301,7 @@ func (dev *IOSDevice) Uninstall(bundleId string) error { return nil } -func (dev *IOSDevice) forward(localPort, remotePort int) error { +func (dev *IOSDevice) Forward(localPort, remotePort int) error { if dev.listeners[localPort] != nil { log.Warn().Msg(fmt.Sprintf("local port :%d is already in use", localPort)) _ = dev.listeners[localPort].Close() @@ -521,7 +503,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID return nil, errors.Wrap(code.DeviceHTTPDriverError, fmt.Sprintf("get free port failed: %v", err)) } - if err = dev.forward(localPort, dev.WDAPort); err != nil { + if err = dev.Forward(localPort, dev.WDAPort); err != nil { return nil, errors.Wrap(code.DeviceHTTPDriverError, fmt.Sprintf("forward tcp port failed: %v", err)) } @@ -537,7 +519,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID return nil, errors.Wrap(code.DeviceHTTPDriverError, fmt.Sprintf("get free port failed: %v", err)) } - if err = dev.forward(localMjpegPort, dev.WDAMjpegPort); err != nil { + if err = dev.Forward(localMjpegPort, dev.WDAMjpegPort); err != nil { return nil, errors.Wrap(code.DeviceHTTPDriverError, fmt.Sprintf("forward tcp port failed: %v", err)) } @@ -563,11 +545,9 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID } // create new session - var sessionInfo Session - if sessionInfo, err = wd.NewSession(capabilities); err != nil { + if err = wd.InitSession(capabilities); err != nil { return nil, errors.Wrap(code.DeviceHTTPDriverError, err.Error()) } - wd.sessionID = sessionInfo.sessionID if wd.mjpegHTTPConn, err = net.Dial( "tcp", @@ -575,7 +555,7 @@ func (dev *IOSDevice) NewHTTPDriver(capabilities option.Capabilities) (driver ID ); err != nil { return nil, errors.Wrap(code.DeviceHTTPDriverError, err.Error()) } - wd.mjpegClient = convertToHTTPClient(wd.mjpegHTTPConn) + wd.mjpegClient = NewHTTPClientWithConnection(wd.mjpegHTTPConn, 30*time.Second) wd.mjpegUrl = fmt.Sprintf("%s:%d", host, localMjpegPort) // init WDA scale if wd.scale, err = wd.Scale(); err != nil { diff --git a/pkg/uixt/ios_driver_wda.go b/pkg/uixt/ios_driver_wda.go index a0025aa6..f3c48bc1 100644 --- a/pkg/uixt/ios_driver_wda.go +++ b/pkg/uixt/ios_driver_wda.go @@ -34,7 +34,7 @@ func NewWDADriver(device *IOSDevice) (*WDADriver, error) { IOSDevice: device, Session: &Session{}, } - driver.NewSession(nil) + driver.InitSession(nil) return driver, nil } @@ -63,16 +63,16 @@ func (wd *WDADriver) resetSession() error { if err != nil { return err } - sessionInfo, err := rawResp.valueConvertToSessionInfo() + sessionInfo, err := rawResp.ValueConvertToSessionInfo() if err != nil { return err } // update session ID - wd.sessionID = sessionInfo.sessionID + wd.ID = sessionInfo.ID return nil } -func (wd *WDADriver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp rawResponse, err error) { +func (wd *WDADriver) httpRequest(method string, rawURL string, rawBody []byte) (rawResp DriverRawResponse, err error) { retryInterval := 3 * time.Second for retryCount := 1; retryCount <= 3; retryCount++ { rawResp, err = wd.Request(method, rawURL, rawBody) @@ -92,25 +92,25 @@ func (wd *WDADriver) httpRequest(method string, rawURL string, rawBody []byte) ( retryInterval = retryInterval * 2 time.Sleep(retryInterval) - oldSessionID := wd.sessionID + oldSessionID := wd.ID if err2 := wd.resetSession(); err2 != nil { log.Err(err2).Msgf("failed to reset wda driver session, retry count: %v", retryCount) continue } - log.Debug().Str("new session", wd.sessionID).Str("old session", oldSessionID). + log.Debug().Str("new session", wd.ID).Str("old session", oldSessionID). Msgf("reset wda driver session successfully, retry count: %v", retryCount) if oldSessionID != "" { - rawURL = strings.Replace(rawURL, oldSessionID, wd.sessionID, 1) + rawURL = strings.Replace(rawURL, oldSessionID, wd.ID, 1) } } return } -func (wd *WDADriver) httpGET(pathElem ...string) (rawResp rawResponse, err error) { +func (wd *WDADriver) httpGET(pathElem ...string) (rawResp DriverRawResponse, err error) { return wd.httpRequest(http.MethodGet, wd.concatURL(nil, pathElem...), nil) } -func (wd *WDADriver) httpPOST(data interface{}, pathElem ...string) (rawResp rawResponse, err error) { +func (wd *WDADriver) httpPOST(data interface{}, pathElem ...string) (rawResp DriverRawResponse, err error) { var bsJSON []byte = nil if data != nil { if bsJSON, err = json.Marshal(data); err != nil { @@ -120,7 +120,7 @@ func (wd *WDADriver) httpPOST(data interface{}, pathElem ...string) (rawResp raw return wd.httpRequest(http.MethodPost, wd.concatURL(nil, pathElem...), bsJSON) } -func (wd *WDADriver) httpDELETE(pathElem ...string) (rawResp rawResponse, err error) { +func (wd *WDADriver) httpDELETE(pathElem ...string) (rawResp DriverRawResponse, err error) { return wd.httpRequest(http.MethodDelete, wd.concatURL(nil, pathElem...), nil) } @@ -128,7 +128,7 @@ func (wd *WDADriver) GetMjpegClient() *http.Client { return wd.mjpegClient } -func (wd *WDADriver) NewSession(capabilities option.Capabilities) (sessionInfo Session, err error) { +func (wd *WDADriver) InitSession(capabilities option.Capabilities) error { // [[FBRoute POST:@"/session"].withoutSession respondWithTarget:self action:@selector(handleCreateSession:)] data := make(map[string]interface{}) if len(capabilities) == 0 { @@ -139,14 +139,16 @@ func (wd *WDADriver) NewSession(capabilities option.Capabilities) (sessionInfo S } } - var rawResp rawResponse - if rawResp, err = wd.httpPOST(data, "/session"); err != nil { - return Session{}, err + rawResp, err := wd.httpPOST(data, "/session") + if err != nil { + return err } - if sessionInfo, err = rawResp.valueConvertToSessionInfo(); err != nil { - return Session{}, err + sessionInfo, err := rawResp.ValueConvertToSessionInfo() + if err != nil { + return err } - return + wd.Session = &sessionInfo + return nil } func (wd *WDADriver) DeleteSession() (err error) { @@ -158,13 +160,13 @@ func (wd *WDADriver) DeleteSession() (err error) { } // [[FBRoute DELETE:@""] respondWithTarget:self action:@selector(handleDeleteSession:)] - _, err = wd.httpDELETE("/session", wd.sessionID) + _, err = wd.httpDELETE("/session", wd.ID) return } func (wd *WDADriver) Status() (deviceStatus types.DeviceStatus, err error) { // [[FBRoute GET:@"/status"].withoutSession respondWithTarget:self action:@selector(handleGetStatus:)] - var rawResp rawResponse + var rawResp DriverRawResponse // Notice: use Driver.GET instead of httpGET to avoid loop calling if rawResp, err = wd.GET("/status"); err != nil { return types.DeviceStatus{}, err @@ -184,8 +186,8 @@ func (wd *WDADriver) GetDevice() IDevice { func (wd *WDADriver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { // [[FBRoute GET:@"/wda/device/info"] respondWithTarget:self action:@selector(handleGetDeviceInfo:)] // [[FBRoute GET:@"/wda/device/info"].withoutSession - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/device/info"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/device/info"); err != nil { return types.DeviceInfo{}, err } reply := new(struct{ Value struct{ types.DeviceInfo } }) @@ -199,8 +201,8 @@ func (wd *WDADriver) DeviceInfo() (deviceInfo types.DeviceInfo, err error) { func (wd *WDADriver) Location() (location types.Location, err error) { // [[FBRoute GET:@"/wda/device/location"] respondWithTarget:self action:@selector(handleGetLocation:)] // [[FBRoute GET:@"/wda/device/location"].withoutSession - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/device/location"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/device/location"); err != nil { return types.Location{}, err } reply := new(struct{ Value struct{ types.Location } }) @@ -213,8 +215,8 @@ func (wd *WDADriver) Location() (location types.Location, err error) { func (wd *WDADriver) BatteryInfo() (batteryInfo types.BatteryInfo, err error) { // [[FBRoute GET:@"/wda/batteryInfo"] respondWithTarget:self action:@selector(handleGetBatteryInfo:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/batteryInfo"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/batteryInfo"); err != nil { return types.BatteryInfo{}, err } reply := new(struct{ Value struct{ types.BatteryInfo } }) @@ -232,8 +234,8 @@ func (wd *WDADriver) WindowSize() (size types.Size, err error) { return wd.windowSize, nil } - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/window/size"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/window/size"); err != nil { return types.Size{}, errors.Wrap(err, "get window size failed by WDA request") } reply := new(struct{ Value struct{ types.Size } }) @@ -254,8 +256,8 @@ func (wd *WDADriver) WindowSize() (size types.Size, err error) { func (wd *WDADriver) Screen() (screen ai.Screen, err error) { // [[FBRoute GET:@"/wda/screen"] respondWithTarget:self action:@selector(handleGetScreen:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/screen"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/screen"); err != nil { return ai.Screen{}, err } reply := new(struct{ Value struct{ ai.Screen } }) @@ -285,8 +287,8 @@ func (wd *WDADriver) toScale(x float64) float64 { func (wd *WDADriver) ActiveAppInfo() (info types.AppInfo, err error) { // [[FBRoute GET:@"/wda/activeAppInfo"] respondWithTarget:self action:@selector(handleActiveAppInfo:)] // [[FBRoute GET:@"/wda/activeAppInfo"].withoutSession - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/activeAppInfo"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/activeAppInfo"); err != nil { return types.AppInfo{}, err } reply := new(struct{ Value struct{ types.AppInfo } }) @@ -299,8 +301,8 @@ func (wd *WDADriver) ActiveAppInfo() (info types.AppInfo, err error) { func (wd *WDADriver) ActiveAppsList() (appsList []types.AppBaseInfo, err error) { // [[FBRoute GET:@"/wda/apps/list"] respondWithTarget:self action:@selector(handleGetActiveAppsList:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/apps/list"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/apps/list"); err != nil { return nil, err } reply := new(struct{ Value []types.AppBaseInfo }) @@ -314,8 +316,8 @@ func (wd *WDADriver) ActiveAppsList() (appsList []types.AppBaseInfo, err error) func (wd *WDADriver) AppState(bundleId string) (runState types.AppState, err error) { // [[FBRoute POST:@"/wda/apps/state"] respondWithTarget:self action:@selector(handleSessionAppState:)] data := map[string]interface{}{"bundleId": bundleId} - var rawResp rawResponse - if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/state"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpPOST(data, "/session", wd.ID, "/wda/apps/state"); err != nil { return 0, err } reply := new(struct{ Value types.AppState }) @@ -330,11 +332,11 @@ func (wd *WDADriver) AppState(bundleId string) (runState types.AppState, err err func (wd *WDADriver) IsLocked() (locked bool, err error) { // [[FBRoute GET:@"/wda/locked"] respondWithTarget:self action:@selector(handleIsLocked:)] // [[FBRoute GET:@"/wda/locked"].withoutSession - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/locked"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/locked"); err != nil { return false, err } - if locked, err = rawResp.valueConvertToBool(); err != nil { + if locked, err = rawResp.ValueConvertToBool(); err != nil { return false, err } return @@ -343,14 +345,14 @@ func (wd *WDADriver) IsLocked() (locked bool, err error) { func (wd *WDADriver) Unlock() (err error) { // [[FBRoute POST:@"/wda/unlock"] respondWithTarget:self action:@selector(handleUnlock:)] // [[FBRoute POST:@"/wda/unlock"].withoutSession - _, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/unlock") + _, err = wd.httpPOST(nil, "/session", wd.ID, "/wda/unlock") return } func (wd *WDADriver) Lock() (err error) { // [[FBRoute POST:@"/wda/lock"] respondWithTarget:self action:@selector(handleLock:)] // [[FBRoute POST:@"/wda/lock"].withoutSession - _, err = wd.httpPOST(nil, "/session", wd.sessionID, "/wda/lock") + _, err = wd.httpPOST(nil, "/session", wd.ID, "/wda/lock") return } @@ -363,11 +365,11 @@ func (wd *WDADriver) Homescreen() (err error) { func (wd *WDADriver) AlertText() (text string, err error) { // [[FBRoute GET:@"/alert/text"] respondWithTarget:self action:@selector(handleAlertGetTextCommand:)] // [[FBRoute GET:@"/alert/text"].withoutSession - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/alert/text"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/alert/text"); err != nil { return "", err } - if text, err = rawResp.valueConvertToString(); err != nil { + if text, err = rawResp.ValueConvertToString(); err != nil { return "", err } return @@ -375,8 +377,8 @@ func (wd *WDADriver) AlertText() (text string, err error) { func (wd *WDADriver) AlertButtons() (btnLabels []string, err error) { // [[FBRoute GET:@"/wda/alert/buttons"] respondWithTarget:self action:@selector(handleGetAlertButtonsCommand:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/alert/buttons"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/alert/buttons"); err != nil { return nil, err } reply := new(struct{ Value []string }) @@ -412,7 +414,7 @@ func (wd *WDADriver) AlertDismiss(label ...string) (err error) { func (wd *WDADriver) AlertSendKeys(text string) (err error) { // [[FBRoute POST:@"/alert/text"] respondWithTarget:self action:@selector(handleAlertSetTextCommand:)] data := map[string]interface{}{"value": strings.Split(text, "")} - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/alert/text") + _, err = wd.httpPOST(data, "/session", wd.ID, "/alert/text") return } @@ -423,7 +425,7 @@ func (wd *WDADriver) AppLaunch(bundleId string) (err error) { data["environment"] = map[string]interface{}{ "SHOW_EXPLORER": "NO", } - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/launch") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/apps/launch") if err != nil { return errors.Wrap(code.MobileUILaunchAppError, fmt.Sprintf("wda launch failed: %v", err)) @@ -445,11 +447,11 @@ func (wd *WDADriver) AppLaunchUnattached(bundleId string) (err error) { func (wd *WDADriver) AppTerminate(bundleId string) (successful bool, err error) { // [[FBRoute POST:@"/wda/apps/terminate"] respondWithTarget:self action:@selector(handleSessionAppTerminate:)] data := map[string]interface{}{"bundleId": bundleId} - var rawResp rawResponse - if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/terminate"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpPOST(data, "/session", wd.ID, "/wda/apps/terminate"); err != nil { return false, err } - if successful, err = rawResp.valueConvertToBool(); err != nil { + if successful, err = rawResp.ValueConvertToBool(); err != nil { return false, err } return @@ -458,7 +460,7 @@ func (wd *WDADriver) AppTerminate(bundleId string) (successful bool, err error) func (wd *WDADriver) AppActivate(bundleId string) (err error) { // [[FBRoute POST:@"/wda/apps/activate"] respondWithTarget:self action:@selector(handleSessionAppActivate:)] data := map[string]interface{}{"bundleId": bundleId} - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/apps/activate") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/apps/activate") return } @@ -468,7 +470,7 @@ func (wd *WDADriver) AppDeactivate(second float64) (err error) { second = 3.0 } data := map[string]interface{}{"duration": second} - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/deactivateApp") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/deactivateApp") return } @@ -539,7 +541,7 @@ func (wd *WDADriver) Tap(x, y float64, opts ...option.ActionOption) (err error) // update data options in post data for extra WDA configurations actionOptions.UpdateData(data) - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/tap/0") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/tap/0") return } @@ -559,7 +561,7 @@ func (wd *WDADriver) DoubleTap(x, y float64, opts ...option.ActionOption) (err e "x": x, "y": y, } - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/doubleTap") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/doubleTap") return } @@ -603,7 +605,7 @@ func (wd *WDADriver) Drag(fromX, fromY, toX, toY float64, opts ...option.ActionO // update data options in post data for extra WDA configurations actionOptions.UpdateData(data) // wda 43 version - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/dragfromtoforduration") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/dragfromtoforduration") // _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/drag") return } @@ -618,29 +620,29 @@ func (wd *WDADriver) SetPasteboard(contentType types.PasteboardType, content str "contentType": contentType, "content": base64.StdEncoding.EncodeToString([]byte(content)), } - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/setPasteboard") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/setPasteboard") return } func (wd *WDADriver) GetPasteboard(contentType types.PasteboardType) (raw *bytes.Buffer, err error) { // [[FBRoute POST:@"/wda/getPasteboard"] respondWithTarget:self action:@selector(handleGetPasteboard:)] data := map[string]interface{}{"contentType": contentType} - var rawResp rawResponse - if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/getPasteboard"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpPOST(data, "/session", wd.ID, "/wda/getPasteboard"); err != nil { return nil, err } - if raw, err = rawResp.valueDecodeAsBase64(); err != nil { + if raw, err = rawResp.ValueDecodeAsBase64(); err != nil { return nil, err } return } func (wd *WDADriver) SetIme(ime string) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (wd *WDADriver) PressKeyCode(keyCode KeyCode) (err error) { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (wd *WDADriver) SendKeys(text string, opts ...option.ActionOption) (err error) { @@ -651,7 +653,7 @@ func (wd *WDADriver) SendKeys(text string, opts ...option.ActionOption) (err err // new data options in post data for extra WDA configurations actionOptions.UpdateData(data) - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/keys") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/keys") return } @@ -674,7 +676,7 @@ func (wd *WDADriver) Input(text string, opts ...option.ActionOption) (err error) } func (wd *WDADriver) Clear(packageName string) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } // PressBack simulates a short press on the BACK button. @@ -710,25 +712,17 @@ func (wd *WDADriver) PressBack(opts ...option.ActionOption) (err error) { // update data options in post data for extra WDA configurations actionOptions.UpdateData(data) - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/dragfromtoforduration") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/dragfromtoforduration") return } func (wd *WDADriver) PressButton(devBtn types.DeviceButton) (err error) { // [[FBRoute POST:@"/wda/pressButton"] respondWithTarget:self action:@selector(handlePressButtonCommand:)] data := map[string]interface{}{"name": devBtn} - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/wda/pressButton") + _, err = wd.httpPOST(data, "/session", wd.ID, "/wda/pressButton") return } -func (wd *WDADriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) { - return info, errDriverNotImplemented -} - -func (wd *WDADriver) LogoutNoneUI(packageName string) error { - return errDriverNotImplemented -} - func (wd *WDADriver) StartCamera() (err error) { // start camera, alias for app_launch com.apple.camera return wd.AppLaunch("com.apple.camera") @@ -748,8 +742,8 @@ func (wd *WDADriver) StopCamera() (err error) { func (wd *WDADriver) Orientation() (orientation types.Orientation, err error) { // [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/orientation"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/orientation"); err != nil { return "", err } reply := new(struct{ Value types.Orientation }) @@ -763,14 +757,14 @@ func (wd *WDADriver) Orientation() (orientation types.Orientation, err error) { func (wd *WDADriver) SetOrientation(orientation types.Orientation) (err error) { // [[FBRoute POST:@"/orientation"] respondWithTarget:self action:@selector(handleSetOrientation:)] data := map[string]interface{}{"orientation": orientation} - _, err = wd.httpPOST(data, "/session", wd.sessionID, "/orientation") + _, err = wd.httpPOST(data, "/session", wd.ID, "/orientation") return } func (wd *WDADriver) Rotation() (rotation types.Rotation, err error) { // [[FBRoute GET:@"/rotation"] respondWithTarget:self action:@selector(handleGetRotation:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/rotation"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/rotation"); err != nil { return types.Rotation{}, err } reply := new(struct{ Value types.Rotation }) @@ -783,20 +777,20 @@ func (wd *WDADriver) Rotation() (rotation types.Rotation, err error) { func (wd *WDADriver) SetRotation(rotation types.Rotation) (err error) { // [[FBRoute POST:@"/rotation"] respondWithTarget:self action:@selector(handleSetRotation:)] - _, err = wd.httpPOST(rotation, "/session", wd.sessionID, "/rotation") + _, err = wd.httpPOST(rotation, "/session", wd.ID, "/rotation") return } func (wd *WDADriver) Screenshot() (raw *bytes.Buffer, err error) { // [[FBRoute GET:@"/screenshot"] respondWithTarget:self action:@selector(handleGetScreenshot:)] // [[FBRoute GET:@"/screenshot"].withoutSession respondWithTarget:self action:@selector(handleGetScreenshot:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/screenshot"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/screenshot"); err != nil { return nil, errors.Wrap(code.DeviceScreenShotError, fmt.Sprintf("get WDA screenshot data failed: %v", err)) } - if raw, err = rawResp.valueDecodeAsBase64(); err != nil { + if raw, err = rawResp.ValueDecodeAsBase64(); err != nil { return nil, errors.Wrap(code.DeviceScreenShotError, fmt.Sprintf("decode WDA screenshot data failed: %v", err)) } @@ -806,7 +800,7 @@ func (wd *WDADriver) Screenshot() (raw *bytes.Buffer, err error) { func (wd *WDADriver) Source(srcOpt ...option.SourceOption) (source string, err error) { // [[FBRoute GET:@"/source"] respondWithTarget:self action:@selector(handleGetSourceCommand:)] // [[FBRoute GET:@"/source"].withoutSession - tmp, _ := url.Parse(wd.concatURL(nil, "/session", wd.sessionID)) + tmp, _ := url.Parse(wd.concatURL(nil, "/session", wd.ID)) toJsonRaw := false if len(srcOpt) != 0 { q := tmp.Query() @@ -820,40 +814,40 @@ func (wd *WDADriver) Source(srcOpt ...option.SourceOption) (source string, err e tmp.RawQuery = q.Encode() } - var rawResp rawResponse + var rawResp DriverRawResponse if rawResp, err = wd.httpRequest(http.MethodGet, wd.concatURL(tmp, "/source"), nil); err != nil { return "", nil } if toJsonRaw { var jr builtinJSON.RawMessage - if jr, err = rawResp.valueConvertToJsonRawMessage(); err != nil { + if jr, err = rawResp.ValueConvertToJsonRawMessage(); err != nil { return "", err } return string(jr), nil } - if source, err = rawResp.valueConvertToString(); err != nil { + if source, err = rawResp.ValueConvertToString(); err != nil { return "", err } return } func (wd *WDADriver) TapByText(text string, opts ...option.ActionOption) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (wd *WDADriver) TapByTexts(actions ...TapTextAction) error { - return errDriverNotImplemented + return types.ErrDriverNotImplemented } func (wd *WDADriver) AccessibleSource() (source string, err error) { // [[FBRoute GET:@"/wda/accessibleSource"] respondWithTarget:self action:@selector(handleGetAccessibleSourceCommand:)] // [[FBRoute GET:@"/wda/accessibleSource"].withoutSession - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/wda/accessibleSource"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/wda/accessibleSource"); err != nil { return "", err } var jr builtinJSON.RawMessage - if jr, err = rawResp.valueConvertToJsonRawMessage(); err != nil { + if jr, err = rawResp.ValueConvertToJsonRawMessage(); err != nil { return "", err } source = string(jr) @@ -868,8 +862,8 @@ func (wd *WDADriver) HealthCheck() (err error) { func (wd *WDADriver) GetAppiumSettings() (settings map[string]interface{}, err error) { // [[FBRoute GET:@"/appium/settings"] respondWithTarget:self action:@selector(handleGetSettings:)] - var rawResp rawResponse - if rawResp, err = wd.httpGET("/session", wd.sessionID, "/appium/settings"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpGET("/session", wd.ID, "/appium/settings"); err != nil { return nil, err } reply := new(struct{ Value map[string]interface{} }) @@ -883,8 +877,8 @@ func (wd *WDADriver) GetAppiumSettings() (settings map[string]interface{}, err e func (wd *WDADriver) SetAppiumSettings(settings map[string]interface{}) (ret map[string]interface{}, err error) { // [[FBRoute POST:@"/appium/settings"] respondWithTarget:self action:@selector(handleSetSettings:)] data := map[string]interface{}{"settings": settings} - var rawResp rawResponse - if rawResp, err = wd.httpPOST(data, "/session", wd.sessionID, "/appium/settings"); err != nil { + var rawResp DriverRawResponse + if rawResp, err = wd.httpPOST(data, "/session", wd.ID, "/appium/settings"); err != nil { return nil, err } reply := new(struct{ Value map[string]interface{} }) @@ -896,7 +890,7 @@ func (wd *WDADriver) SetAppiumSettings(settings map[string]interface{}) (ret map } func (wd *WDADriver) IsHealthy() (healthy bool, err error) { - var rawResp rawResponse + var rawResp DriverRawResponse if rawResp, err = wd.httpGET("/health"); err != nil { return false, err } diff --git a/pkg/uixt/option/android.go b/pkg/uixt/option/android.go index edf7022e..0404bca7 100644 --- a/pkg/uixt/option/android.go +++ b/pkg/uixt/option/android.go @@ -4,7 +4,6 @@ import "github.com/httprunner/httprunner/v5/pkg/gadb" type AndroidDeviceOptions struct { SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"` - STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"` // adb @@ -23,9 +22,6 @@ func (dev *AndroidDeviceOptions) Options() (deviceOptions []AndroidDeviceOption) if dev.SerialNumber != "" { deviceOptions = append(deviceOptions, WithSerialNumber(dev.SerialNumber)) } - if dev.STUB { - deviceOptions = append(deviceOptions, WithStub(true)) - } if dev.UIA2 { deviceOptions = append(deviceOptions, WithUIA2(true)) } @@ -87,12 +83,6 @@ func NewAndroidDeviceOptions(opts ...AndroidDeviceOption) *AndroidDeviceOptions type AndroidDeviceOption func(*AndroidDeviceOptions) -func WithDriverTypeADB() AndroidDeviceOption { - return func(device *AndroidDeviceOptions) { - device.STUB = false - } -} - func WithSerialNumber(serial string) AndroidDeviceOption { return func(device *AndroidDeviceOptions) { device.SerialNumber = serial @@ -105,12 +95,6 @@ func WithUIA2(uia2On bool) AndroidDeviceOption { } } -func WithStub(stubOn bool) AndroidDeviceOption { - return func(device *AndroidDeviceOptions) { - device.STUB = stubOn - } -} - func WithUIA2IP(ip string) AndroidDeviceOption { return func(device *AndroidDeviceOptions) { device.UIA2IP = ip diff --git a/pkg/uixt/option/ios.go b/pkg/uixt/option/ios.go index 9ba7f546..34a36244 100644 --- a/pkg/uixt/option/ios.go +++ b/pkg/uixt/option/ios.go @@ -4,7 +4,6 @@ type IOSDeviceOptions struct { UDID string `json:"udid,omitempty" yaml:"udid,omitempty"` WDAPort int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port WDAMjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port - STUB bool `json:"stub,omitempty" yaml:"stub,omitempty"` // use stub LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"` // switch to iOS springboard before init WDA session @@ -26,9 +25,6 @@ func (dev *IOSDeviceOptions) Options() (deviceOptions []IOSDeviceOption) { if dev.WDAMjpegPort != 0 { deviceOptions = append(deviceOptions, WithWDAMjpegPort(dev.WDAMjpegPort)) } - if dev.STUB { - deviceOptions = append(deviceOptions, WithIOSStub(true)) - } if dev.LogOn { deviceOptions = append(deviceOptions, WithWDALogOn(true)) } @@ -123,12 +119,6 @@ func WithWDALogOn(logOn bool) IOSDeviceOption { } } -func WithIOSStub(stub bool) IOSDeviceOption { - return func(device *IOSDeviceOptions) { - device.STUB = stub - } -} - func WithResetHomeOnStartup(reset bool) IOSDeviceOption { return func(device *IOSDeviceOptions) { device.ResetHomeOnStartup = reset diff --git a/pkg/uixt/types/driver.go b/pkg/uixt/types/driver.go new file mode 100644 index 00000000..859138b5 --- /dev/null +++ b/pkg/uixt/types/driver.go @@ -0,0 +1,5 @@ +package types + +import "errors" + +var ErrDriverNotImplemented = errors.New("driver method not implemented") diff --git a/server/context.go b/server/context.go index 1be2df86..21d88022 100644 --- a/server/context.go +++ b/server/context.go @@ -38,8 +38,7 @@ func handleDeviceContext() gin.HandlerFunc { switch strings.ToLower(platform) { case "android": device, err := uixt.NewAndroidDevice( - option.WithSerialNumber(serial), - option.WithStub(true)) + option.WithSerialNumber(serial)) if err != nil { log.Error().Err(err).Str("platform", platform).Str("serial", serial). Msg("device not found") diff --git a/server/main.go b/server/main.go index 26cf52e4..027e2f87 100644 --- a/server/main.go +++ b/server/main.go @@ -32,11 +32,11 @@ func NewServer(port int) error { // get screen info apiV1PlatformSerial.GET("/screenshot", handleDeviceContext(), screenshotHandler) apiV1PlatformSerial.POST("/screenresult", handleDeviceContext(), screenResultHandler) - apiV1PlatformSerial.GET("/stub/source", handleDeviceContext(), sourceHandler) + apiV1PlatformSerial.GET("/shoots/source", handleDeviceContext(), sourceHandler) apiV1PlatformSerial.GET("/adb/source", handleDeviceContext(), adbSourceHandler) - // Stub operations - apiV1PlatformSerial.POST("/stub/login", handleDeviceContext(), loginHandler) - apiV1PlatformSerial.POST("/stub/logout", handleDeviceContext(), logoutHandler) + // shoots operations + apiV1PlatformSerial.POST("/shoots/login", handleDeviceContext(), loginHandler) + apiV1PlatformSerial.POST("/shoots/logout", handleDeviceContext(), logoutHandler) // run uixt actions apiV1PlatformSerial.POST("/uixt/action", handleDeviceContext(), uixtActionHandler)