diff --git a/hrp/internal/uixt/android_device.go b/hrp/internal/uixt/android_device.go index cfaf09a0..227e0784 100644 --- a/hrp/internal/uixt/android_device.go +++ b/hrp/internal/uixt/android_device.go @@ -7,8 +7,6 @@ import ( "net" "os/exec" "reflect" - "regexp" - "strconv" "strings" "syscall" @@ -17,6 +15,7 @@ import ( "github.com/rs/zerolog/log" "github.com/httprunner/httprunner/v4/hrp/internal/builtin" + "github.com/httprunner/httprunner/v4/hrp/internal/json" ) var ( @@ -28,15 +27,6 @@ var ( const forwardToPrefix = "forward-to-" -const ( - regexFloat = `[0-9\.]*` -) - -var ( - regexCompileSwipe = regexp.MustCompile(fmt.Sprintf(`timesec=(%s)\s*startX=(%s)\s*startY=(%s)\s*endX=(%s)\s*endY=(%s)`, regexFloat, regexFloat, regexFloat, regexFloat, regexFloat)) // parse ${var} or $var - regexCompileTap = regexp.MustCompile(fmt.Sprintf(`timesec=(%s)\s*x=(%s)\s*y=(%s)`, regexFloat, regexFloat, regexFloat)) // parse ${func1($a, $b)} // parse number -) - func InitUIAClient(device *AndroidDevice) (*DriverExt, error) { var deviceOptions []AndroidDeviceOption if device.SerialNumber != "" { @@ -75,6 +65,7 @@ func InitUIAClient(device *AndroidDevice) (*DriverExt, error) { } } + driverExt.UUID = androidDevice.UUID() return driverExt, err } @@ -308,43 +299,15 @@ type ExportPoint struct { func ConvertPoints(data string) (eps []ExportPoint) { lines := strings.Split(data, "\n") for _, line := range lines { - if strings.Contains(line, "startX") { - matched := regexCompileSwipe.FindStringSubmatch(line) - if len(matched) != 6 { + if strings.Contains(line, "ext") { + idx := strings.Index(line, "{") + line = line[idx:] + p := ExportPoint{} + err := json.Unmarshal([]byte(line), &p) + if err != nil { log.Error().Msg("failed to parse point data") continue } - start, _ := strconv.Atoi(matched[1]) - fromX, _ := strconv.ParseFloat(matched[2], 64) - fromY, _ := strconv.ParseFloat(matched[3], 64) - toX, _ := strconv.ParseFloat(matched[4], 64) - toY, _ := strconv.ParseFloat(matched[5], 64) - p := ExportPoint{ - Start: start, - End: start, - From: []float64{fromX, fromY}, - To: []float64{toX, toY}, - Operation: "Gtf-Drag", - Ext: "", - } - eps = append(eps, p) - } else if strings.Contains(line, "x=") { - matched := regexCompileTap.FindStringSubmatch(line) - if len(matched) != 4 { - log.Error().Msg("failed to parse point data") - continue - } - start, _ := strconv.Atoi(matched[1]) - x, _ := strconv.ParseFloat(matched[2], 64) - y, _ := strconv.ParseFloat(matched[3], 64) - p := ExportPoint{ - Start: start, - End: start, - From: []float64{x, y}, - To: []float64{x, y}, - Operation: "Gtf-Tap", - Ext: "", - } eps = append(eps, p) } } diff --git a/hrp/internal/uixt/android_device_test.go b/hrp/internal/uixt/android_device_test.go index 4195a5ef..2167d2f5 100644 --- a/hrp/internal/uixt/android_device_test.go +++ b/hrp/internal/uixt/android_device_test.go @@ -8,9 +8,9 @@ import ( ) func TestConvertPoints(t *testing.T) { - data := "09-29 15:02:08.379 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434928378\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:09.433 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434929432\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:10.452 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434930452\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:11.451 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434931450\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:12.491 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434932489\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:16.028 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434936027\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:21.424 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434941423\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:27.923 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434947922\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:33.628 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434953628\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:39.347 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434959347\tx=1259.5y=1868.5" + data := "10-09 20:16:48.216 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317808206,\"ext\":\"输入\",\"from\":{\"x\":0.0,\"y\":0.0},\"operation\":\"Gtf-SendKeys\",\"run_time\":627,\"start\":1665317807579,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":0.0,\"y\":0.0}}\n10-09 20:18:22.899 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317902898,\"ext\":\"进入直播间\",\"from\":{\"x\":717.0,\"y\":2117.5},\"operation\":\"Gtf-Tap\",\"run_time\":121,\"start\":1665317902777,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":717.0,\"y\":2117.5}}\n10-09 20:18:32.063 I/iesqaMonitor(17845): {\"duration\":0,\"end\":1665317912062,\"ext\":\"第一次上划\",\"from\":{\"x\":1437.0,\"y\":2409.9},\"operation\":\"Gtf-Swipe\",\"run_time\":32,\"start\":1665317912030,\"start_first\":0,\"start_last\":0,\"to\":{\"x\":1437.0,\"y\":2409.9}}" eps := ConvertPoints(data) - if len(eps) != 10 { + if len(eps) != 3 { t.Fatal() } jsons, _ := json.Marshal(eps) diff --git a/hrp/internal/uixt/android_driver.go b/hrp/internal/uixt/android_driver.go index ad3ce2a9..2c80a6a1 100644 --- a/hrp/internal/uixt/android_driver.go +++ b/hrp/internal/uixt/android_driver.go @@ -482,7 +482,7 @@ func (ud *uiaDriver) AppAuthReset(resource ProtectedResource) (err error) { } func (ud *uiaDriver) Tap(x, y int, options ...DataOption) error { - return ud.TapFloat(float64(x), float64(y)) + return ud.TapFloat(float64(x), float64(y), options...) } func (ud *uiaDriver) TapFloat(x, y float64, options ...DataOption) (err error) { @@ -491,6 +491,11 @@ func (ud *uiaDriver) TapFloat(x, y float64, options ...DataOption) (err error) { "x": x, "y": y, } + // append options in post data for extra uiautomator configurations + for _, option := range options { + option(data) + } + _, err = ud.httpPOST(data, "/session", ud.sessionId, "appium/tap") return } @@ -546,8 +551,7 @@ func (ud *uiaDriver) DragFloat(fromX, fromY, toX, toY float64, options ...DataOp "endY": toY, } - // append options in post data for extra WDA configurations - // e.g. use WithPressDuration to set pressForDuration + // append options in post data for extra uiautomator configurations for _, option := range options { option(data) } @@ -559,17 +563,23 @@ func (ud *uiaDriver) DragFloat(fromX, fromY, toX, toY float64, options ...DataOp return ud._drag(data) } -func (ud *uiaDriver) _swipe(startX, startY, endX, endY interface{}, steps int, elementID ...string) (err error) { +func (ud *uiaDriver) _swipe(startX, startY, endX, endY interface{}, options ...DataOption) (err error) { // register(postHandler, new Swipe("/wd/hub/session/:sessionId/touch/perform")) data := map[string]interface{}{ "startX": startX, "startY": startY, "endX": endX, "endY": endY, - "steps": steps, } - if len(elementID) != 0 { - data["elementId"] = elementID[0] + + // append options in post data for extra uiautomator configurations + // e.g. use WithPressDuration to set pressForDuration + for _, option := range options { + option(data) + } + + if _, ok := data["steps"]; !ok { + data["steps"] = 12 // default steps } _, err = ud.httpPOST(data, "/session", ud.sessionId, "touch/perform") return @@ -580,23 +590,12 @@ func (ud *uiaDriver) _swipe(startX, startY, endX, endY interface{}, steps int, e // per step. So for a 100 steps, the swipe will take about 1/2 second to complete. // `steps` is the number of move steps sent to the system func (ud *uiaDriver) Swipe(fromX, fromY, toX, toY int, options ...DataOption) error { - options = append(options, WithPressDuration(0)) + options = append(options, WithSteps(12)) return ud.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), options...) } func (ud *uiaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error { - data := map[string]interface{}{} - // append options in post data for extra WDA configurations - // e.g. use WithPressDuration to set pressForDuration - for _, option := range options { - option(data) - } - - if _, ok := data["steps"]; !ok { - data["steps"] = 12 // default steps - } - - return ud._swipe(fromX, fromY, toX, toY, data["steps"].(int)) + return ud._swipe(fromX, fromY, toX, toY, options...) } func (ud *uiaDriver) ForceTouch(x, y int, pressure float64, second ...float64) error { @@ -671,8 +670,7 @@ func (ud *uiaDriver) SendKeys(text string, options ...DataOption) (err error) { data := map[string]interface{}{ "text": text, } - // append options in post data for extra WDA configurations - // e.g. use WithPressDuration to set pressForDuration + // append options in post data for extra uiautomator configurations for _, option := range options { option(data) } @@ -690,7 +688,7 @@ func (ud *uiaDriver) Input(text string, options ...DataOption) (err error) { if err != nil { return err } - return element.SendKeys(text) + return element.SendKeys(text, options...) } func (ud *uiaDriver) KeyboardDismiss(keyNames ...string) (err error) { diff --git a/hrp/internal/uixt/android_elment.go b/hrp/internal/uixt/android_elment.go index f14fac40..27c503df 100644 --- a/hrp/internal/uixt/android_elment.go +++ b/hrp/internal/uixt/android_elment.go @@ -21,16 +21,22 @@ func (ue uiaElement) Click() (err error) { return } -func (ue uiaElement) SendKeys(text string, isReplace ...int) (err error) { - if len(isReplace) == 0 { - isReplace = []int{1} - } +func (ue uiaElement) SendKeys(text string, options ...DataOption) (err error) { // register(postHandler, new SendKeysToElement("/wd/hub/session/:sessionId/element/:id/value")) // https://github.com/appium/appium-uiutomator2-server/blob/master/app/src/main/java/io/appium/uiutomator2/handler/SendKeysToElement.java#L76-L85 data := map[string]interface{}{ - "text": text, - "replace": isReplace[0] == 1, + "text": text, } + + // append options in post data for extra uiautomator configurations + for _, option := range options { + option(data) + } + + if _, ok := data["isReplace"]; !ok { + data["isReplace"] = true // default true + } + _, err = ue.parent.httpPOST(data, "/session", ue.parent.sessionId, "/element", ue.id, "/value") return } @@ -105,7 +111,11 @@ func (ue uiaElement) Swipe(fromX, fromY, toX, toY int) error { } func (ue uiaElement) SwipeFloat(fromX, fromY, toX, toY float64) error { - return ue.parent._swipe(fromX, fromY, toX, toY, 12, ue.id) + options := []DataOption{ + WithSteps(12), + WithCustomOption("elementId", ue.id), + } + return ue.parent._swipe(fromX, fromY, toX, toY, options...) } func (ue uiaElement) SwipeDirection(direction Direction, velocity ...float64) (err error) { diff --git a/hrp/internal/uixt/interface.go b/hrp/internal/uixt/interface.go index 4922ba37..3ca14086 100644 --- a/hrp/internal/uixt/interface.go +++ b/hrp/internal/uixt/interface.go @@ -788,6 +788,12 @@ func WithPressDuration(duraion float64) DataOption { } } +func WithSteps(steps int) DataOption { + return func(data map[string]interface{}) { + data["steps"] = steps + } +} + func WithFrequency(frequency int) DataOption { return func(data map[string]interface{}) { data["frequency"] = frequency @@ -1003,7 +1009,7 @@ type WebElement interface { // SendKeys Types a text into element. It will try to activate keyboard on element, // if element has no keyboard focus. // frequency: Frequency of typing (letters per sec). The default value is 60 - SendKeys(text string, frequency ...int) error + SendKeys(text string, options ...DataOption) error // Clear Clears text on element. It will try to activate keyboard on element, // if element has no keyboard focus. Clear() error diff --git a/hrp/internal/uixt/ios_element.go b/hrp/internal/uixt/ios_element.go index 16479efe..74006479 100644 --- a/hrp/internal/uixt/ios_element.go +++ b/hrp/internal/uixt/ios_element.go @@ -25,13 +25,20 @@ func (we wdaElement) Click() (err error) { return } -func (we wdaElement) SendKeys(text string, frequency ...int) (err error) { +func (we wdaElement) SendKeys(text string, options ...DataOption) (err error) { // [[FBRoute POST:@"/element/:uuid/value"] respondWithTarget:self action:@selector(handleSetValue:)] - data := map[string]interface{}{"value": strings.Split(text, "")} - if len(frequency) == 0 || frequency[0] <= 0 { - frequency = []int{60} + data := map[string]interface{}{ + "value": strings.Split(text, ""), } - data["frequency"] = frequency[0] + // append options in post data for extra uiautomator configurations + for _, option := range options { + option(data) + } + + if _, ok := data["frequency"]; !ok { + data["frequency"] = 60 + } + _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/element", we.id, "/value") return }