refactor: improve ActionMethod type safety and eliminate type conversions

This commit is contained in:
lilong.129
2025-05-27 11:49:30 +08:00
parent 466fe39cb9
commit 7fb966b7ba
36 changed files with 1087 additions and 963 deletions

View File

@@ -22,17 +22,27 @@ func (r *Router) foregroundAppHandler(c *gin.Context) {
}
func (r *Router) appInfoHandler(c *gin.Context) {
var appInfoReq option.AppInfoRequest
if err := c.ShouldBindQuery(&appInfoReq); err != nil {
var req option.UnifiedActionRequest
if err := c.ShouldBindQuery(&req); err != nil {
RenderErrorValidateRequest(c, err)
return
}
// Set platform and serial from URL parameters
setRequestContextFromURL(c, &req)
// Validate for HTTP API usage
if err := req.ValidateForHTTPAPI(option.ACTION_AppInfo); err != nil {
RenderErrorValidateRequest(c, err)
return
}
device, err := r.GetDevice(c)
if err != nil {
return
}
if androidDevice, ok := device.(*uixt.AndroidDevice); ok {
appInfo, err := androidDevice.GetAppInfo(appInfoReq.PackageName)
appInfo, err := androidDevice.GetAppInfo(req.PackageName)
if err != nil {
RenderError(c, err)
return
@@ -40,7 +50,7 @@ func (r *Router) appInfoHandler(c *gin.Context) {
RenderSuccess(c, appInfo)
return
} else if iOSDevice, ok := device.(*uixt.IOSDevice); ok {
appInfo, err := iOSDevice.GetAppInfo(appInfoReq.PackageName)
appInfo, err := iOSDevice.GetAppInfo(req.PackageName)
if err != nil {
RenderError(c, err)
return
@@ -51,9 +61,8 @@ func (r *Router) appInfoHandler(c *gin.Context) {
}
func (r *Router) clearAppHandler(c *gin.Context) {
var appClearReq option.AppClearRequest
if err := c.ShouldBindJSON(&appClearReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_AppClear)
if err != nil {
return
}
@@ -61,7 +70,7 @@ func (r *Router) clearAppHandler(c *gin.Context) {
if err != nil {
return
}
err = driver.AppClear(appClearReq.PackageName)
err = driver.AppClear(req.PackageName)
if err != nil {
RenderError(c, err)
return
@@ -70,16 +79,16 @@ func (r *Router) clearAppHandler(c *gin.Context) {
}
func (r *Router) launchAppHandler(c *gin.Context) {
var appLaunchReq option.AppLaunchRequest
if err := c.ShouldBindJSON(&appLaunchReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_AppLaunch)
if err != nil {
return
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
err = driver.AppLaunch(appLaunchReq.PackageName)
err = driver.AppLaunch(req.PackageName)
if err != nil {
RenderError(c, err)
return
@@ -88,16 +97,16 @@ func (r *Router) launchAppHandler(c *gin.Context) {
}
func (r *Router) terminalAppHandler(c *gin.Context) {
var appTerminateReq option.AppTerminateRequest
if err := c.ShouldBindJSON(&appTerminateReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_AppTerminate)
if err != nil {
return
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
_, err = driver.AppTerminate(appTerminateReq.PackageName)
_, err = driver.AppTerminate(req.PackageName)
if err != nil {
RenderError(c, err)
return
@@ -106,16 +115,16 @@ func (r *Router) terminalAppHandler(c *gin.Context) {
}
func (r *Router) uninstallAppHandler(c *gin.Context) {
var appUninstallReq option.AppUninstallRequest
if err := c.ShouldBindJSON(&appUninstallReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_AppUninstall)
if err != nil {
return
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
err = driver.GetDevice().Uninstall(appUninstallReq.PackageName)
err = driver.GetDevice().Uninstall(req.PackageName)
if err != nil {
log.Err(err).Msg("failed to uninstall app")
}

View File

@@ -34,19 +34,20 @@ func (r *Router) homeHandler(c *gin.Context) {
}
func (r *Router) backspaceHandler(c *gin.Context) {
var deleteReq option.DeleteRequest
if err := c.ShouldBindJSON(&deleteReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_Backspace)
if err != nil {
return
}
if deleteReq.Count == 0 {
deleteReq.Count = 20
count := req.GetCount()
if count == 0 {
count = 20
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
err = driver.Backspace(deleteReq.Count)
err = driver.Backspace(count)
if err != nil {
RenderError(c, err)
return
@@ -55,18 +56,18 @@ func (r *Router) backspaceHandler(c *gin.Context) {
}
func (r *Router) keycodeHandler(c *gin.Context) {
var keycodeReq option.KeycodeRequest
if err := c.ShouldBindJSON(&keycodeReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_KeyCode)
if err != nil {
return
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
// TODO FIXME
err = driver.IDriver.(*uixt.ADBDriver).
PressKeyCode(uixt.KeyCode(keycodeReq.Keycode), uixt.KMEmpty)
PressKeyCode(uixt.KeyCode(req.GetKeycode()), uixt.KMEmpty)
if err != nil {
RenderError(c, err)
return

View File

@@ -33,15 +33,6 @@ type UploadRequest struct {
FileFormat string `json:"file_format"`
}
type HoverRequest struct {
X float64 `json:"x"`
Y float64 `json:"y"`
}
type ScrollRequest struct {
Delta int `json:"delta"`
}
type CreateBrowserRequest struct {
Timeout int `json:"timeout"`
Width int `json:"width"`

View File

@@ -6,21 +6,55 @@ import (
"github.com/httprunner/httprunner/v5/uixt/option"
)
func (r *Router) tapHandler(c *gin.Context) {
var tapReq option.TapRequest
if err := c.ShouldBindJSON(&tapReq); err != nil {
// processUnifiedRequest is a helper function to handle common request processing
func (r *Router) processUnifiedRequest(c *gin.Context, actionType option.ActionMethod) (*option.UnifiedActionRequest, error) {
var req option.UnifiedActionRequest
// Bind JSON request
if err := c.ShouldBindJSON(&req); err != nil {
RenderErrorValidateRequest(c, err)
return
return nil, err
}
// Set platform and serial from URL parameters
setRequestContextFromURL(c, &req)
// Validate for HTTP API usage
if err := req.ValidateForHTTPAPI(actionType); err != nil {
RenderErrorValidateRequest(c, err)
return nil, err
}
return &req, nil
}
// setRequestContextFromURL sets platform and serial from URL parameters
func setRequestContextFromURL(c *gin.Context, req *option.UnifiedActionRequest) {
if req.Platform == "" {
req.Platform = c.Param("platform")
}
if req.Serial == "" {
req.Serial = c.Param("serial")
}
}
func (r *Router) tapHandler(c *gin.Context) {
req, err := r.processUnifiedRequest(c, option.ACTION_Tap)
if err != nil {
return // Error already handled in processUnifiedRequest
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
if tapReq.Duration > 0 {
err = driver.Drag(tapReq.X, tapReq.Y, tapReq.X, tapReq.Y,
option.WithDuration(tapReq.Duration))
// Use UnifiedActionRequest directly
if req.GetDuration() > 0 {
err = driver.Drag(req.GetX(), req.GetY(), req.GetX(), req.GetY(),
option.WithDuration(req.GetDuration()))
} else {
err = driver.TapXY(tapReq.X, tapReq.Y)
err = driver.TapXY(req.GetX(), req.GetY())
}
if err != nil {
RenderError(c, err)
@@ -30,17 +64,17 @@ func (r *Router) tapHandler(c *gin.Context) {
}
func (r *Router) rightClickHandler(c *gin.Context) {
var rightClickReq option.TapRequest
if err := c.ShouldBindJSON(&rightClickReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_RightClick)
if err != nil {
return
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
err = driver.IDriver.(*uixt.BrowserDriver).
SecondaryClick(rightClickReq.X, rightClickReq.Y)
SecondaryClick(req.GetX(), req.GetY())
if err != nil {
RenderError(c, err)
return
@@ -71,9 +105,8 @@ func (r *Router) uploadHandler(c *gin.Context) {
}
func (r *Router) hoverHandler(c *gin.Context) {
var hoverReq HoverRequest
if err := c.ShouldBindJSON(&hoverReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_Hover)
if err != nil {
return
}
@@ -84,7 +117,7 @@ func (r *Router) hoverHandler(c *gin.Context) {
}
err = driver.IDriver.(*uixt.BrowserDriver).
Hover(hoverReq.X, hoverReq.Y)
Hover(req.GetX(), req.GetY())
if err != nil {
RenderError(c, err)
@@ -94,9 +127,8 @@ func (r *Router) hoverHandler(c *gin.Context) {
}
func (r *Router) scrollHandler(c *gin.Context) {
var scrollReq ScrollRequest
if err := c.ShouldBindJSON(&scrollReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_Scroll)
if err != nil {
return
}
@@ -107,7 +139,7 @@ func (r *Router) scrollHandler(c *gin.Context) {
}
err = driver.IDriver.(*uixt.BrowserDriver).
Scroll(scrollReq.Delta)
Scroll(req.GetDelta())
if err != nil {
RenderError(c, err)
@@ -117,9 +149,8 @@ func (r *Router) scrollHandler(c *gin.Context) {
}
func (r *Router) doubleTapHandler(c *gin.Context) {
var tapReq option.TapRequest
if err := c.ShouldBindJSON(&tapReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_DoubleTap)
if err != nil {
return
}
@@ -128,7 +159,7 @@ func (r *Router) doubleTapHandler(c *gin.Context) {
return
}
err = driver.DoubleTap(tapReq.X, tapReq.Y)
err = driver.DoubleTap(req.GetX(), req.GetY())
if err != nil {
RenderError(c, err)
return
@@ -137,22 +168,23 @@ func (r *Router) doubleTapHandler(c *gin.Context) {
}
func (r *Router) dragHandler(c *gin.Context) {
var dragReq option.DragRequest
if err := c.ShouldBindJSON(&dragReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_Drag)
if err != nil {
return
}
if dragReq.Duration == 0 {
dragReq.Duration = 1
duration := req.GetDuration()
if duration == 0 {
duration = 1
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
err = driver.Drag(dragReq.FromX, dragReq.FromY, dragReq.ToX, dragReq.ToY,
option.WithDuration(dragReq.Duration),
option.WithPressDuration(dragReq.PressDuration))
err = driver.Drag(req.GetFromX(), req.GetFromY(), req.GetToX(), req.GetToY(),
option.WithDuration(duration),
option.WithPressDuration(req.GetPressDuration()))
if err != nil {
RenderError(c, err)
return
@@ -161,16 +193,16 @@ func (r *Router) dragHandler(c *gin.Context) {
}
func (r *Router) inputHandler(c *gin.Context) {
var inputReq option.InputRequest
if err := c.ShouldBindJSON(&inputReq); err != nil {
RenderErrorValidateRequest(c, err)
req, err := r.processUnifiedRequest(c, option.ACTION_Input)
if err != nil {
return
}
driver, err := r.GetDriver(c)
if err != nil {
return
}
err = driver.Input(inputReq.Text, option.WithFrequency(inputReq.Frequency))
err = driver.Input(req.Text, option.WithFrequency(req.GetFrequency()))
if err != nil {
RenderError(c, err)
return

View File

@@ -18,17 +18,17 @@ func TestTapHandler(t *testing.T) {
tests := []struct {
name string
path string
tapReq option.TapRequest
req option.UnifiedActionRequest
wantStatus int
wantResp HttpResponse
}{
{
name: "tap abs xy",
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
tapReq: option.TapRequest{
X: 500,
Y: 800,
Duration: 0,
req: option.UnifiedActionRequest{
X: &[]float64{500}[0],
Y: &[]float64{800}[0],
Duration: &[]float64{0}[0],
},
wantStatus: http.StatusOK,
wantResp: HttpResponse{
@@ -40,10 +40,10 @@ func TestTapHandler(t *testing.T) {
{
name: "tap relative xy",
path: fmt.Sprintf("/api/v1/android/%s/ui/tap", "4622ca24"),
tapReq: option.TapRequest{
X: 0.5,
Y: 0.6,
Duration: 0,
req: option.UnifiedActionRequest{
X: &[]float64{0.5}[0],
Y: &[]float64{0.6}[0],
Duration: &[]float64{0}[0],
},
wantStatus: http.StatusOK,
wantResp: HttpResponse{
@@ -56,7 +56,7 @@ func TestTapHandler(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reqBody, _ := json.Marshal(tt.tapReq)
reqBody, _ := json.Marshal(tt.req)
req := httptest.NewRequest(http.MethodPost, tt.path, bytes.NewBuffer(reqBody))
req.Header.Set("Content-Type", "application/json")