Merge branch 'chore/huangbin/v5_browser' into 'wings'

Chore/huangbin/v5 browser

See merge request iesqa/httprunner!66
This commit is contained in:
黄彬
2025-02-20 04:27:15 +00:00
6 changed files with 56 additions and 373 deletions

View File

@@ -67,7 +67,7 @@ func (dev *BrowserDevice) GetPackageInfo(packageName string) (types.AppInfo, err
func (dev *BrowserDevice) NewDriver() (driver IDriver, err error) {
// var driver WebDriver
driver, err = newBrowserWebDriver(dev.UUID())
driver, err = NewBrowserWebDriver(dev.UUID())
if err != nil {
return nil, err
}

View File

@@ -90,8 +90,8 @@ func CreateBrowser(timeout int) (browserInfo *BrowserInfo, err error) {
return &result.Data, nil
}
func newBrowserWebDriver(browserId string) (driver *BrowserWebDriver, err error) {
log.Info().Msg("init newBrowserWebDriver driver")
func NewBrowserWebDriver(browserId string) (driver *BrowserWebDriver, err error) {
log.Info().Msg("init NewBrowserWebDriver driver")
driver = new(BrowserWebDriver)
driver.urlPrefix = &url.URL{}
driver.urlPrefix.Host = BROWSER_LOCAL_ADDRESS
@@ -121,7 +121,7 @@ func (wd *BrowserWebDriver) Drag(fromX, fromY, toX, toY float64, options ...opti
data["duration"] = actionOptions.Duration
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/drag")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/drag")
return
}
@@ -130,7 +130,7 @@ func (wd *BrowserWebDriver) AppLaunch(packageName string) (err error) {
"url": packageName,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/page_launch")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/page_launch")
return
}
@@ -172,7 +172,7 @@ func (wd *BrowserWebDriver) Scroll(delta int) (err error) {
data := map[string]interface{}{
"delta": delta,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/scroll")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/scroll")
return err
}
@@ -195,7 +195,7 @@ func (wd *BrowserWebDriver) ClosePage(pageIndex int) (err error) {
data := map[string]interface{}{
"page_index": pageIndex,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/page_close")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/page_close")
return err
}
@@ -207,7 +207,7 @@ func (wd *BrowserWebDriver) HoverBySelector(selector string, options ...option.A
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/hover")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/hover")
return err
}
@@ -219,7 +219,7 @@ func (wd *BrowserWebDriver) tapBySelector(selector string, options ...option.Act
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/tap")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/tap")
return err
}
@@ -228,7 +228,7 @@ func (wd *BrowserWebDriver) RightClick(x, y int) (err error) {
"x": x,
"y": y,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/right_click")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/right_click")
return err
}
@@ -240,7 +240,7 @@ func (wd *BrowserWebDriver) RightclickbySelector(selector string, options ...opt
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/right_click")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/right_click")
return err
}
@@ -250,7 +250,7 @@ func (wd *BrowserWebDriver) GetElementTextBySelector(selector string, options ..
if actionOptions.Index > 0 {
uri = uri + "&element_index=" + fmt.Sprintf("%v", actionOptions.Index)
}
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, uri)
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, uri)
if err != nil {
return "", err
}
@@ -264,7 +264,7 @@ func (wd *BrowserWebDriver) GetPageUrl(options ...option.ActionOption) (text str
if actionOptions.Index > 0 {
uri = uri + "?page_index=" + fmt.Sprintf("%v", actionOptions.Index)
}
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, uri)
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, uri)
if err != nil {
return "", err
}
@@ -273,7 +273,7 @@ func (wd *BrowserWebDriver) GetPageUrl(options ...option.ActionOption) (text str
}
func (wd *BrowserWebDriver) IsElementExistBySelector(selector string) (bool, error) {
resp, err := wd.httpGet(wd.sessionId, "ui/element_exist", "?selector=", selector)
resp, err := wd.HttpGet(wd.sessionId, "ui/element_exist", "?selector=", selector)
if err != nil {
return false, err
}
@@ -286,7 +286,7 @@ func (wd *BrowserWebDriver) Hover(x, y float64) (err error) {
"x": x,
"y": y,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/hover")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/hover")
return err
}
@@ -295,7 +295,7 @@ func (wd *BrowserWebDriver) DoubleTapXY(x, y float64, option ...option.ActionOpt
"x": x,
"y": y,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/double_tap")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/double_tap")
return err
}
@@ -303,13 +303,13 @@ func (wd *BrowserWebDriver) Input(text string, option ...option.ActionOption) (e
data := map[string]interface{}{
"text": text,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/input")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/input")
return err
}
// Source Return application elements tree
func (wd *BrowserWebDriver) Source(srcOpt ...option.SourceOption) (string, error) {
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, "stub/source")
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, "stub/source")
if err != nil {
return "", err
@@ -325,7 +325,7 @@ func (wd *BrowserWebDriver) Source(srcOpt ...option.SourceOption) (string, error
}
func (wd *BrowserWebDriver) ScreenShot(options ...option.ActionOption) (*bytes.Buffer, error) {
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, "screenshot")
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, "screenshot")
if err != nil {
return nil, err
}
@@ -337,7 +337,7 @@ func (wd *BrowserWebDriver) ScreenShot(options ...option.ActionOption) (*bytes.B
return res, err
}
func (wd *BrowserWebDriver) httpPOST(data interface{}, pathElem ...string) (response *WebAgentResponse, err error) {
func (wd *BrowserWebDriver) HttpPOST(data interface{}, pathElem ...string) (response *WebAgentResponse, err error) {
var bsJSON []byte = nil
if data != nil {
if bsJSON, err = json.Marshal(data); err != nil {
@@ -348,7 +348,7 @@ func (wd *BrowserWebDriver) httpPOST(data interface{}, pathElem ...string) (resp
return wd.httpRequest(http.MethodPost, wd.concatURL(pathElem...), bsJSON)
}
func (wd *BrowserWebDriver) httpGet(data interface{}, pathElem ...string) (response *WebAgentResponse, err error) {
func (wd *BrowserWebDriver) HttpGet(data interface{}, pathElem ...string) (response *WebAgentResponse, err error) {
return wd.httpRequest(http.MethodGet, wd.concatURL(pathElem...), nil)
}
@@ -417,7 +417,7 @@ func (wd *BrowserWebDriver) BatteryInfo() (batteryInfo types.BatteryInfo, err er
}
func (wd *BrowserWebDriver) WindowSize() (types.Size, error) {
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, "window_size")
resp, err := wd.HttpGet(http.MethodGet, wd.sessionId, "window_size")
if err != nil {
return types.Size{}, err
}
@@ -498,7 +498,7 @@ func (wd *BrowserWebDriver) TapFloat(x, y float64, options ...option.ActionOptio
"y": y,
"duration": duration,
}
_, err := wd.httpPOST(data, wd.sessionId, "ui/tap")
_, err := wd.HttpPOST(data, wd.sessionId, "ui/tap")
return err
}
@@ -508,7 +508,7 @@ func (wd *BrowserWebDriver) DoubleTap(x, y float64, options ...option.ActionOpti
"x": x,
"y": y,
}
_, err := wd.httpPOST(data, wd.sessionId, "ui/double_tap")
_, err := wd.HttpPOST(data, wd.sessionId, "ui/double_tap")
return err
}
func (wd *BrowserWebDriver) UploadFile(x, y float64, FileUrl, FileFormat string) (err error) {
@@ -518,7 +518,7 @@ func (wd *BrowserWebDriver) UploadFile(x, y float64, FileUrl, FileFormat string)
"file_url": FileUrl,
"file_format": FileFormat,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/upload")
_, err = wd.HttpPOST(data, wd.sessionId, "ui/upload")
return err
}
@@ -566,7 +566,7 @@ func (wd *BrowserWebDriver) ForegroundInfo() (app types.AppInfo, err error) {
// PressBack Presses the back button
func (wd *BrowserWebDriver) PressBack(options ...option.ActionOption) error {
_, err := wd.httpPOST(map[string]interface{}{}, wd.sessionId, "ui/back")
_, err := wd.HttpPOST(map[string]interface{}{}, wd.sessionId, "ui/back")
return err
}

View File

@@ -101,9 +101,22 @@ func (dExt *XTDriver) GetIDriver() IDriver {
return dExt.IDriver
}
func (dExt *XTDriver) GetWebDriver() IBrowserWebDriver {
return dExt.GetIDriver().(*BrowserWebDriver)
}
type IXTDriver interface {
IDriver
GetIDriver() IDriver
GetWebDriver() IBrowserWebDriver
GetScreenResult(opts ...option.ActionOption) (screenResult *ScreenResult, err error)
DoAction(action MobileAction) (err error)
}
type IBrowserWebDriver interface {
IDriver
Hover(x, y float64) (err error)
RightClick(x, y int) (err error)
Scroll(delta int) (err error)
UploadFile(x, y float64, FileUrl, FileFormat string) (err error)
}

View File

@@ -2,21 +2,16 @@ package driver_ext
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"path"
"time"
"github.com/gorilla/websocket"
"github.com/httprunner/httprunner/v5/pkg/uixt"
"github.com/httprunner/httprunner/v5/pkg/uixt/option"
"github.com/httprunner/httprunner/v5/pkg/uixt/types"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
const BROWSER_LOCAL_ADDRESS = "localhost:8093"
@@ -34,7 +29,7 @@ type CreateBrowserResponse struct {
Data BrowserInfo `json:"data"`
}
type BrowserWebDriver struct {
type StubBrowserDriver struct {
*uixt.BrowserWebDriver
urlPrefix *url.URL
sessionId string
@@ -92,16 +87,14 @@ func CreateBrowser(timeout int) (browserInfo *BrowserInfo, err error) {
return &result.Data, nil
}
func NewStubBrowserDriver(browserId string) (driver *BrowserWebDriver, err error) {
log.Info().Msg("init NewStubBrowserDriver driver")
driver = new(BrowserWebDriver)
driver.urlPrefix = &url.URL{}
driver.urlPrefix.Host = BROWSER_LOCAL_ADDRESS
driver.urlPrefix.Scheme = "http"
driver.scale = 1.0
func NewStubBrowserDriver(browserId string) (driver *StubBrowserDriver, err error) {
BrowserWebDriver, err := uixt.NewBrowserWebDriver(browserId)
if err != nil {
return nil, errors.Wrap(err, "create browser session failed")
}
driver = &StubBrowserDriver{
BrowserWebDriver: BrowserWebDriver,
}
driver.sessionId = browserId
if err != nil {
return nil, fmt.Errorf("adb forward: %w", err)
@@ -109,209 +102,9 @@ func NewStubBrowserDriver(browserId string) (driver *BrowserWebDriver, err error
return driver, nil
}
func (wd *BrowserWebDriver) Drag(fromX, fromY, toX, toY float64, options ...option.ActionOption) (err error) {
data := map[string]interface{}{
"from_x": fromX,
"from_y": fromY,
"to_x": toX,
"to_y": toY,
}
actionOptions := option.NewActionOptions(options...)
if actionOptions.Duration > 0 {
data["duration"] = actionOptions.Duration
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/drag")
return
}
func (wd *BrowserWebDriver) AppLaunch(packageName string) (err error) {
data := map[string]interface{}{
"url": packageName,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/page_launch")
return
}
func (wd *BrowserWebDriver) DeleteSession() (err error) {
url := wd.concatURL("context", wd.sessionId)
req, err := http.NewRequest("DELETE", url, nil)
if err != nil {
panic(err)
}
client := &http.Client{
Timeout: 60 * time.Second, // 设置超时时间为5秒
}
resp, err := client.Do(req)
if err != nil {
return err
}
rawResp, err := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
var result CreateBrowserResponse
if err = json.Unmarshal(rawResp, &result); err != nil {
return err
}
if result.Code != 0 {
return errors.New(result.Message)
}
return nil
}
func (wd *BrowserWebDriver) Scroll(delta int) (err error) {
data := map[string]interface{}{
"delta": delta,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/scroll")
return err
}
func (wd *BrowserWebDriver) CreateNetListener() (*websocket.Conn, error) {
webSocketUrl := "ws://localhost:8093/websocket_net_listen"
c, _, err := websocket.DefaultDialer.Dial(webSocketUrl, nil)
if err != nil {
return nil, err
}
// 发送消息
initMessage := fmt.Sprintf(`{
"type":"create_net_listener",
"context_id":"%v"
}`, wd.sessionId)
err = c.WriteMessage(websocket.TextMessage, []byte(initMessage))
return c, nil
}
func (wd *BrowserWebDriver) ClosePage(pageIndex int) (err error) {
data := map[string]interface{}{
"page_index": pageIndex,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/page_close")
return err
}
func (wd *BrowserWebDriver) HoverBySelector(selector string, options ...option.ActionOption) (err error) {
data := map[string]interface{}{
"selector": selector,
}
actionOptions := option.NewActionOptions(options...)
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/hover")
return err
}
func (wd *BrowserWebDriver) tapBySelector(selector string, options ...option.ActionOption) (err error) {
data := map[string]interface{}{
"selector": selector,
}
actionOptions := option.NewActionOptions(options...)
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/tap")
return err
}
func (wd *BrowserWebDriver) RightClick(x, y int) (err error) {
data := map[string]interface{}{
"x": x,
"y": y,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/right_click")
return err
}
func (wd *BrowserWebDriver) RightclickbySelector(selector string, options ...option.ActionOption) (err error) {
data := map[string]interface{}{
"selector": selector,
}
actionOptions := option.NewActionOptions(options...)
if actionOptions.Index > 0 {
data["element_index"] = actionOptions.Index
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/right_click")
return err
}
func (wd *BrowserWebDriver) GetElementTextBySelector(selector string, options ...option.ActionOption) (text string, err error) {
actionOptions := option.NewActionOptions(options...)
uri := "ui/element_text?selector=" + selector
if actionOptions.Index > 0 {
uri = uri + "&element_index=" + fmt.Sprintf("%v", actionOptions.Index)
}
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, uri)
if err != nil {
return "", err
}
data := resp.Data.(map[string]interface{})
return data["text"].(string), nil
}
func (wd *BrowserWebDriver) GetPageUrl(options ...option.ActionOption) (text string, err error) {
uri := "ui/page_url"
actionOptions := option.NewActionOptions(options...)
if actionOptions.Index > 0 {
uri = uri + "?page_index=" + fmt.Sprintf("%v", actionOptions.Index)
}
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, uri)
if err != nil {
return "", err
}
data := resp.Data.(map[string]interface{})
return data["url"].(string), nil
}
func (wd *BrowserWebDriver) IsElementExistBySelector(selector string) (bool, error) {
resp, err := wd.httpGet(wd.sessionId, "ui/element_exist", "?selector=", selector)
if err != nil {
return false, err
}
data := resp.Data.(map[string]interface{})
return data["exist"].(bool), nil
}
func (wd *BrowserWebDriver) Hover(x, y float64) (err error) {
data := map[string]interface{}{
"x": x,
"y": y,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/hover")
return err
}
func (wd *BrowserWebDriver) DoubleTapXY(x, y float64, option ...option.ActionOption) (err error) {
data := map[string]interface{}{
"x": x,
"y": y,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/double_tap")
return err
}
func (wd *BrowserWebDriver) Input(text string, option ...option.ActionOption) (err error) {
data := map[string]interface{}{
"text": text,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/input")
return err
}
// Source Return application elements tree
func (wd *BrowserWebDriver) Source(srcOpt ...option.SourceOption) (string, error) {
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, "stub/source")
func (wd *StubBrowserDriver) Source(srcOpt ...option.SourceOption) (string, error) {
resp, err := wd.BrowserWebDriver.HttpGet(http.MethodGet, wd.sessionId, "stub/source")
if err != nil {
return "", err
@@ -326,89 +119,12 @@ func (wd *BrowserWebDriver) Source(srcOpt ...option.SourceOption) (string, error
return string(jsonData), err
}
func (wd *BrowserWebDriver) ScreenShot(options ...option.ActionOption) (*bytes.Buffer, error) {
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, "screenshot")
if err != nil {
return nil, err
}
data := resp.Data.(map[string]interface{})
screenshotBase64 := data["screenshot"].(string)
screenRaw, err := base64.StdEncoding.DecodeString(screenshotBase64)
res := bytes.NewBuffer(screenRaw)
return res, err
}
func (wd *BrowserWebDriver) httpPOST(data interface{}, pathElem ...string) (response *WebAgentResponse, err error) {
var bsJSON []byte = nil
if data != nil {
if bsJSON, err = json.Marshal(data); err != nil {
return nil, err
}
}
return wd.httpRequest(http.MethodPost, wd.concatURL(pathElem...), bsJSON)
}
func (wd *BrowserWebDriver) httpGet(data interface{}, pathElem ...string) (response *WebAgentResponse, err error) {
return wd.httpRequest(http.MethodGet, wd.concatURL(pathElem...), nil)
}
func (wd *BrowserWebDriver) concatURL(elem ...string) string {
tmp, _ := url.Parse(wd.urlPrefix.String())
commonPath := path.Join(append([]string{wd.urlPrefix.Path}, "api/v1/")...)
tmp.Path = path.Join(append([]string{commonPath}, elem...)...)
return tmp.String()
}
func (wd *BrowserWebDriver) httpRequest(method string, rawURL string, rawBody []byte, disableRetry ...bool) (response *WebAgentResponse, err error) {
req, err := http.NewRequest(method, rawURL, bytes.NewBuffer(rawBody))
req.Header.Set("Content-Type", "application/json")
if err != nil {
return nil, err
}
// 新建http client
client := &http.Client{
Timeout: 60 * time.Second, // 设置超时时间为5秒
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
rawResp, err := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return nil, errors.New(resp.Status)
}
// 将结果解析为 JSON
var result WebAgentResponse
if err = json.Unmarshal(rawResp, &result); err != nil {
return nil, err
}
if result.Code != 0 {
log.Info().Msgf("%v", result.Message)
return nil, errors.New(result.Message)
}
if err != nil {
return nil, err
}
return &result, err
}
func (wd *BrowserWebDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
func (wd *StubBrowserDriver) LoginNoneUI(packageName, phoneNumber string, captcha, password string) (info AppLoginInfo, err error) {
data := map[string]interface{}{
"url": packageName,
"web_cookie": password,
}
_, err = wd.httpPOST(data, wd.sessionId, "stub/login")
_, err = wd.HttpPOST(data, wd.sessionId, "stub/login")
if err != nil {
return info, err
@@ -420,52 +136,3 @@ func (wd *BrowserWebDriver) LoginNoneUI(packageName, phoneNumber string, captcha
}
return loginSuccss, err
}
func (wd *BrowserWebDriver) WindowSize() (types.Size, error) {
resp, err := wd.httpGet(http.MethodGet, wd.sessionId, "window_size")
if err != nil {
return types.Size{}, err
}
data := resp.Data.(map[string]interface{})
width := data["width"]
height := data["height"]
return types.Size{
Width: int(width.(float64)),
Height: int(height.(float64)),
}, nil
}
func (wd *BrowserWebDriver) TapFloat(x, y float64, options ...option.ActionOption) error {
actionOptions := option.NewActionOptions(options...)
duration := 0.1
if actionOptions.Duration > 0 {
duration = actionOptions.Duration
}
data := map[string]interface{}{
"x": x,
"y": y,
"duration": duration,
}
_, err := wd.httpPOST(data, wd.sessionId, "ui/tap")
return err
}
// DoubleTap Sends a double tap event at the coordinate.
func (wd *BrowserWebDriver) DoubleTap(x, y float64, options ...option.ActionOption) error {
data := map[string]interface{}{
"x": x,
"y": y,
}
_, err := wd.httpPOST(data, wd.sessionId, "ui/double_tap")
return err
}
func (wd *BrowserWebDriver) UploadFile(x, y float64, FileUrl, FileFormat string) (err error) {
data := map[string]interface{}{
"x": x,
"y": y,
"file_url": FileUrl,
"file_format": FileFormat,
}
_, err = wd.httpPOST(data, wd.sessionId, "ui/upload")
return err
}

View File

@@ -68,3 +68,7 @@ func (dExt *XTDriver) Install(filePath string, opts ...option.InstallOption) err
return dExt.GetDevice().Install(filePath, opts...)
}
func (dExt *XTDriver) GetWebDriver() uixt.IBrowserWebDriver {
return dExt.GetIDriver().(*StubBrowserDriver)
}