diff --git a/README.en.md b/README.en.md
index 7a6a14ab..0a30ce22 100644
--- a/README.en.md
+++ b/README.en.md
@@ -87,11 +87,15 @@ Usage:
hrp [command]
Available Commands:
+ adb simple utils for android device management
boom run load test with boomer
build build plugin for testing
- completion generate the autocompletion script for the specified shell
- convert convert to JSON/YAML/gotest/pytest testcases
+ completion Generate the autocompletion script for the specified shell
+ convert convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases
+ dns DNS resolution for different source and record types
help Help about any command
+ ios simple utils for ios device management
+ ping run integrated ping command
pytest run API test with pytest
run run API test with go engine
startproject create a scaffold project
@@ -120,8 +124,6 @@ Use "hrp [command] --help" for more information about a command.
-如果你期望加入 HttpRunner 用户群,请看这里:[HttpRunner v4 用户交流群,它来啦!](https://httprunner.com/blog/join-chat-group)
-
[HttpRunner]: https://github.com/httprunner/httprunner
[boomer]: https://github.com/myzhan/boomer
[locust]: https://github.com/locustio/locust
diff --git a/README.md b/README.md
index 4debd21a..2ca5dc81 100644
--- a/README.md
+++ b/README.md
@@ -80,11 +80,15 @@ Usage:
hrp [command]
Available Commands:
+ adb simple utils for android device management
boom run load test with boomer
build build plugin for testing
- completion generate the autocompletion script for the specified shell
- convert convert to JSON/YAML/gotest/pytest testcases
+ completion Generate the autocompletion script for the specified shell
+ convert convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases
+ dns DNS resolution for different source and record types
help Help about any command
+ ios simple utils for ios device management
+ ping run integrated ping command
pytest run API test with pytest
run run API test with go engine
startproject create a scaffold project
@@ -107,21 +111,6 @@ Use "hrp [command] --help" for more information about a command.
-## 赞助商
-
-### 金牌赞助商
-
-[
](https://ceshiren.com/)
-
-> [霍格沃兹测试开发学社](http://qrcode.testing-studio.com/f?from=httprunner&url=https://ceshiren.com)是业界领先的测试开发技术高端教育品牌,隶属于[测吧(北京)科技有限公司](http://qrcode.testing-studio.com/f?from=httprunner&url=https://www.testing-studio.com) 。学院课程由一线大厂测试经理与资深测试开发专家参与研发,实战驱动。课程涵盖 web/app 自动化测试、接口测试、性能测试、安全测试、持续集成/持续交付/DevOps,测试左移&右移、精准测试、测试平台开发、测试管理等内容,帮助测试工程师实现测试开发技术转型。通过优秀的学社制度(奖学金、内推返学费、行业竞赛等多种方式)来实现学员、学社及用人企业的三方共赢。
-
-> [进入测试开发技术能力测评!](http://qrcode.testing-studio.com/f?from=httprunner&url=https://ceshiren.com/t/topic/14940)
-
-### 开源服务赞助商
-
-[
](https://sentry.io/_/open-source/)
-
-HttpRunner is in Sentry Sponsored plan.
## Subscribe
@@ -129,8 +118,6 @@ HttpRunner is in Sentry Sponsored plan.
-如果你期望加入 HttpRunner 用户群,请看这里:[HttpRunner v4 用户交流群,它来啦!](https://httprunner.com/blog/join-chat-group)
-
[HttpRunner]: https://github.com/httprunner/httprunner
[boomer]: https://github.com/myzhan/boomer
[locust]: https://github.com/locustio/locust
diff --git a/examples/data/curl/curl_examples.txt b/examples/data/curl/curl_examples.txt
index d6aa3d9f..6d8e6a7e 100644
--- a/examples/data/curl/curl_examples.txt
+++ b/examples/data/curl/curl_examples.txt
@@ -2,20 +2,10 @@ curl httpbin.org
curl https://httpbin.org/get?key1=value1&key2=value2
-curl -H "Content-Type: application/json" \
- -H "Authorization: Bearer b7d03a6947b217efb6f3ec3bd3504582" \
- -d '{"type":"A","name":"www","data":"162.10.66.0","priority":null,"port":null,"weight":null}' \
- "https://httpbin.org/post"
+curl -H "Content-Type: application/json" -H "Authorization: Bearer b7d03a6947b217efb6f3ec3bd3504582" -d '{"type":"A","name":"www","data":"162.10.66.0","priority":null,"port":null,"weight":null}' "https://httpbin.org/post"
curl -F "dummyName=dummyFile" -F file1=@file1.txt -F file2=@file2.txt https://httpbin.org/post
-curl https://httpbin.org/post \
- -d 'shipment[to_address][id]=adr_HrBKVA85' \
- -d 'shipment[from_address][id]=adr_VtuTOj7o' \
- -d 'shipment[parcel][id]=prcl_WDv2VzHp' \
- -d 'shipment[is_return]=true' \
- -d 'shipment[customs_info][id]=cstinfo_bl5sE20Y'
-
-curl https://httpbing.org/post -H "Content-Type: application/x-www-form-urlencoded" \
- --data "key1=value+1&key2=value%3A2"
+curl https://httpbin.org/post -d 'shipment[to_address][id]=adr_HrBKVA85' -d 'shipment[from_address][id]=adr_VtuTOj7o' -d 'shipment[parcel][id]=prcl_WDv2VzHp' -d 'shipment[is_return]=true' -d 'shipment[customs_info][id]=cstinfo_bl5sE20Y'
+curl https://httpbing.org/post -H "Content-Type: application/x-www-form-urlencoded" --data "key1=value+1&key2=value%3A2"
diff --git a/examples/uitest/demo_android_douyin_test.go b/examples/uitest/demo_android_douyin_test.go
index bbfa6391..8ef5d2c4 100644
--- a/examples/uitest/demo_android_douyin_test.go
+++ b/examples/uitest/demo_android_douyin_test.go
@@ -12,7 +12,7 @@ import (
func TestAndroidDouYinLive(t *testing.T) {
testCase := &hrp.TestCase{
Config: hrp.NewConfig("通过 feed 头像进入抖音直播间").
- SetAndroid(uixt.WithAdbLogOn(true), uixt.WithSerialNumber("2d06bf70")),
+ SetAndroid(uixt.WithAdbLogOn(true)),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").
Android().
@@ -36,13 +36,6 @@ func TestAndroidDouYinLive(t *testing.T) {
},
}
- if err := testCase.Dump2JSON("demo_android_douyin_live.json"); err != nil {
- t.Fatal(err)
- }
- if err := testCase.Dump2YAML("demo_android_douyin_live.yaml"); err != nil {
- t.Fatal(err)
- }
-
runner := hrp.NewRunner(t).SetSaveTests(true)
err := runner.Run(testCase)
if err != nil {
diff --git a/examples/uitest/wda_log_data.json b/examples/uitest/wda_log_data.json
index 0237e46a..b89e4fbe 100644
--- a/examples/uitest/wda_log_data.json
+++ b/examples/uitest/wda_log_data.json
@@ -6,9 +6,16 @@
},
"ios": [
{
+ "perf_options": {
+ "sys_cpu": true,
+ "sys_mem": true,
+ "fps": true,
+ "network": true
+ },
"port": 8700,
"mjpeg_port": 8800,
- "log_on": true
+ "log_on": true,
+ "xctest_bundle_id": "com.gtf.wda.runner.xctrunner"
}
]
},
@@ -63,8 +70,8 @@
"actions": [
{
"method": "tap_ocr",
- "params": "购物",
- "identifier": "点击购物"
+ "params": "商城",
+ "identifier": "点击商城"
},
{
"method": "sleep",
diff --git a/examples/uitest/wda_log_test.go b/examples/uitest/wda_log_test.go
index 3c03a458..39943f09 100644
--- a/examples/uitest/wda_log_test.go
+++ b/examples/uitest/wda_log_test.go
@@ -24,6 +24,7 @@ func TestWDALog(t *testing.T) {
uixt.WithIOSPerfNetwork(true),
uixt.WithIOSPerfFPS(true),
),
+ uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
),
TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音").
diff --git a/hrp/cmd/convert.go b/hrp/cmd/convert.go
index 5ec00480..203896f5 100644
--- a/hrp/cmd/convert.go
+++ b/hrp/cmd/convert.go
@@ -1,77 +1,127 @@
package cmd
import (
- "errors"
"fmt"
+ "io/ioutil"
+ "path/filepath"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
+ "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/httprunner/httprunner/v4/hrp/internal/myexec"
"github.com/httprunner/httprunner/v4/hrp/internal/version"
"github.com/httprunner/httprunner/v4/hrp/pkg/convert"
)
var convertCmd = &cobra.Command{
- Use: "convert $path...",
- Short: "convert to JSON/YAML/gotest/pytest testcases",
- Args: cobra.MinimumNArgs(1),
+ Use: "convert $path...",
+ Short: "convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases",
+ Args: cobra.MinimumNArgs(1),
+ SilenceUsage: false,
PreRun: func(cmd *cobra.Command, args []string) {
setLogLevel(logLevel)
},
- RunE: convertRun,
+ RunE: func(cmd *cobra.Command, args []string) error {
+ caseConverter := convert.NewConverter(outputDir, profilePath)
+
+ var fromType convert.FromType
+ if fromYAMLFlag {
+ fromType = convert.FromTypeYAML
+ } else if fromPostmanFlag {
+ fromType = convert.FromTypePostman
+ } else if fromHARFlag {
+ fromType = convert.FromTypeHAR
+ } else if fromCurlFlag {
+ fromType = convert.FromTypeCurl
+ } else {
+ fromType = convert.FromTypeJSON
+ log.Info().Str("fromType", fromType.String()).Msg("set default")
+ }
+
+ var outputType convert.OutputType
+ if toYAMLFlag {
+ outputType = convert.OutputTypeYAML
+ } else if toPyTestFlag {
+ packages := []string{
+ fmt.Sprintf("httprunner==%s", version.HttpRunnerMinimumVersion),
+ }
+ _, err := myexec.EnsurePython3Venv(venv, packages...)
+ if err != nil {
+ log.Error().Err(err).Msg("python3 venv is not ready")
+ return err
+ }
+
+ outputType = convert.OutputTypePyTest
+ } else {
+ outputType = convert.OutputTypeJSON
+ log.Info().Str("outputType", outputType.String()).Msg("set default")
+ }
+
+ var files []string
+ for _, arg := range args {
+ if builtin.IsFolderPathExists(arg) {
+ fs, err := ioutil.ReadDir(arg)
+ if err != nil {
+ log.Error().Err(err).Str("path", arg).Msg("read dir failed")
+ continue
+ }
+ for _, f := range fs {
+ files = append(files, filepath.Join(arg, f.Name()))
+ }
+ } else {
+ files = append(files, arg)
+ }
+ }
+
+ for _, file := range files {
+ extName := filepath.Ext(file)
+ if !builtin.Contains(fromType.Extensions(), extName) {
+ log.Warn().Str("path", file).
+ Strs("expectExtensions", fromType.Extensions()).
+ Msg("skip file")
+ continue
+ }
+
+ if err := caseConverter.Convert(file, fromType, outputType); err != nil {
+ log.Error().Err(err).Str("path", file).
+ Str("outputType", outputType.String()).
+ Msg("convert case failed")
+ }
+ }
+
+ return nil
+ },
}
var (
+ outputDir string
+ profilePath string
+
+ fromJSONFlag bool
+ fromYAMLFlag bool
+ fromPostmanFlag bool
+ fromHARFlag bool
+ fromCurlFlag bool
+
toJSONFlag bool
toYAMLFlag bool
- toGoTestFlag bool
toPyTestFlag bool
- outputDir string
- profilePath string
-
- outputType convert.OutputType
)
func init() {
rootCmd.AddCommand(convertCmd)
+
+ convertCmd.Flags().BoolVar(&fromJSONFlag, "from-json", true, "load from json case format")
+ convertCmd.Flags().BoolVar(&fromYAMLFlag, "from-yaml", false, "load from yaml case format")
+ convertCmd.Flags().BoolVar(&fromHARFlag, "from-har", false, "load from HAR format")
+ convertCmd.Flags().BoolVar(&fromPostmanFlag, "from-postman", false, "load from postman format")
+ convertCmd.Flags().BoolVar(&fromCurlFlag, "from-curl", false, "load from curl format")
+
+ convertCmd.Flags().BoolVar(&toJSONFlag, "to-json", true, "convert to JSON case scripts")
+ convertCmd.Flags().BoolVar(&toYAMLFlag, "to-yaml", false, "convert to YAML case scripts")
convertCmd.Flags().BoolVar(&toPyTestFlag, "to-pytest", false, "convert to pytest scripts")
- convertCmd.Flags().BoolVar(&toGoTestFlag, "to-gotest", false, "convert to gotest scripts (TODO)")
- convertCmd.Flags().BoolVar(&toJSONFlag, "to-json", false, "convert to JSON scripts (default)")
- convertCmd.Flags().BoolVar(&toYAMLFlag, "to-yaml", false, "convert to YAML scripts")
- convertCmd.Flags().StringVarP(&outputDir, "output-dir", "d", "", "specify output directory, default to the same dir with har file")
+
+ convertCmd.Flags().StringVarP(&outputDir, "output-dir", "d", "", "specify output directory")
convertCmd.Flags().StringVarP(&profilePath, "profile", "p", "", "specify profile path to override headers and cookies")
}
-
-func convertRun(cmd *cobra.Command, args []string) error {
- var flagCount int
- if toJSONFlag {
- flagCount++
- }
- if toYAMLFlag {
- flagCount++
- outputType = convert.OutputTypeYAML
- }
- if toGoTestFlag {
- flagCount++
- outputType = convert.OutputTypeGoTest
- }
- if toPyTestFlag {
- flagCount++
- outputType = convert.OutputTypePyTest
-
- packages := []string{
- fmt.Sprintf("httprunner==%s", version.HttpRunnerMinimumVersion),
- }
- _, err := myexec.EnsurePython3Venv(venv, packages...)
- if err != nil {
- log.Error().Err(err).Msg("python3 venv is not ready")
- return err
- }
- }
- if flagCount > 1 {
- return errors.New("please specify at most one conversion flag")
- }
- convert.Run(outputType, outputDir, profilePath, args)
- return nil
-}
diff --git a/hrp/cmd/curl.go b/hrp/cmd/curl.go
deleted file mode 100644
index a75ae84b..00000000
--- a/hrp/cmd/curl.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package cmd
-
-import (
- "fmt"
- "os"
- "strings"
-
- "github.com/rs/zerolog/log"
- "github.com/spf13/cobra"
-
- "github.com/httprunner/httprunner/v4/hrp"
- "github.com/httprunner/httprunner/v4/hrp/internal/env"
- "github.com/httprunner/httprunner/v4/hrp/pkg/boomer"
- "github.com/httprunner/httprunner/v4/hrp/pkg/convert"
-)
-
-var runCurlCmd = &cobra.Command{
- Use: "curl URLs",
- Short: "run API test with curl command",
- Args: cobra.MinimumNArgs(1),
- DisableFlagParsing: true,
- PreRun: func(cmd *cobra.Command, args []string) {
- setLogLevel(logLevel)
- },
- RunE: func(cmd *cobra.Command, args []string) error {
- runner := makeHRPRunner()
- return runner.Run(makeCurlTestCase(args))
- },
-}
-
-var boomCurlCmd = &cobra.Command{
- Use: "curl URLs",
- Short: "run load test with curl command",
- Args: cobra.MinimumNArgs(1),
- DisableFlagParsing: true,
- PreRun: func(cmd *cobra.Command, args []string) {
- boomer.SetUlimit(10240)
- if !strings.EqualFold(logLevel, "DEBUG") {
- logLevel = "WARN" // disable info logs for load testing
- }
- setLogLevel(logLevel)
- },
- RunE: func(cmd *cobra.Command, args []string) error {
- boomer, err := makeHRPBoomer()
- if err != nil {
- return err
- }
- boomer.Run(makeCurlTestCase(args))
- return nil
- },
-}
-
-var convertCurlCmd = &cobra.Command{
- Use: "curl URLs",
- Short: "convert curl command to httprunner testcase",
- Args: cobra.MinimumNArgs(1),
- DisableFlagParsing: true,
- PreRun: func(cmd *cobra.Command, args []string) {
- setLogLevel(logLevel)
- },
- RunE: func(cmd *cobra.Command, args []string) error {
- curlCommand := makeCurlCommand(args)
- return convertRun(cmd, []string{curlCommand})
- },
-}
-
-func init() {
- runCmd.AddCommand(runCurlCmd)
- boomCmd.AddCommand(boomCurlCmd)
- convertCmd.AddCommand(convertCurlCmd)
-}
-
-func makeCurlTestCase(args []string) *hrp.TestCase {
- curlCommand := makeCurlCommand(args)
- tCase, err := convert.LoadSingleCurlCase(curlCommand)
- if err != nil {
- log.Error().Err(err).Msg("convert curl command failed")
- os.Exit(1)
- }
- testCase, err := tCase.ToTestCase(env.RootDir)
- if err != nil {
- log.Error().Err(err).Msg("convert testcase to failed")
- os.Exit(1)
- }
- return testCase
-}
-
-func makeCurlCommand(args []string) string {
- for i := 0; i < len(args); i++ {
- if !strings.HasPrefix(args[i], "-") {
- args[i] = fmt.Sprintf("\"%s\"", args[i])
- }
- }
- var curlCmd []string
- curlCmd = append(curlCmd, "curl")
- curlCmd = append(curlCmd, args...)
- return strings.Join(curlCmd, " ")
-}
diff --git a/hrp/cmd/dial.go b/hrp/cmd/dial.go
index df14f3b1..9b6c9d77 100644
--- a/hrp/cmd/dial.go
+++ b/hrp/cmd/dial.go
@@ -1,7 +1,6 @@
package cmd
import (
- "runtime"
"time"
"github.com/rs/zerolog/log"
@@ -11,9 +10,8 @@ import (
)
var (
- pingOptions dial.PingOptions
- dnsOptions dial.DnsOptions
- traceRouteOptions dial.TraceRouteOptions
+ pingOptions dial.PingOptions
+ dnsOptions dial.DnsOptions
)
var pingCmd = &cobra.Command{
@@ -46,34 +44,6 @@ var dnsCmd = &cobra.Command{
},
}
-var traceRouteCmd = &cobra.Command{
- Use: "traceroute $url",
- Short: "run integrated traceroute command",
- Args: cobra.ExactArgs(1),
- PreRun: func(cmd *cobra.Command, args []string) {
- setLogLevel(logLevel)
- },
- RunE: func(cmd *cobra.Command, args []string) error {
- if runtime.GOOS == "windows" {
- log.Info().Msg("using default probe number (3) on Windows")
- }
- return dial.DoTraceRoute(&traceRouteOptions, args)
- },
-}
-
-var curlCmd = &cobra.Command{
- Use: "curl $url",
- Short: "run integrated curl command",
- Args: cobra.MinimumNArgs(1),
- DisableFlagParsing: true,
- PreRun: func(cmd *cobra.Command, args []string) {
- setLogLevel(logLevel)
- },
- RunE: func(cmd *cobra.Command, args []string) error {
- return dial.DoCurl(args)
- },
-}
-
func init() {
rootCmd.AddCommand(pingCmd)
pingCmd.Flags().IntVarP(&pingOptions.Count, "count", "c", 10, "Stop after sending (and receiving) N packets")
@@ -86,11 +56,4 @@ func init() {
dnsCmd.Flags().IntVar(&dnsOptions.DnsRecordType, "dns-record", 1, "DNS record type\n1: A\n28: AAAA\n5: CNAME")
dnsCmd.Flags().StringVar(&dnsOptions.DnsServer, "dns-server", "", "DNS server, only available for local DNS source")
dnsCmd.Flags().BoolVar(&dnsOptions.SaveTests, "save-tests", false, "Save DNS resolution result as json")
-
- rootCmd.AddCommand(traceRouteCmd)
- traceRouteCmd.Flags().IntVarP(&traceRouteOptions.MaxTTL, "max-hops", "m", 30, "Set the max number of hops (max TTL to be reached)")
- traceRouteCmd.Flags().IntVarP(&traceRouteOptions.Queries, "queries", "q", 1, "Set the number of probes per each hop")
- traceRouteCmd.Flags().BoolVar(&traceRouteOptions.SaveTests, "save-tests", false, "Save traceroute result as json")
-
- rootCmd.AddCommand(curlCmd)
}
diff --git a/hrp/convert.go b/hrp/convert.go
new file mode 100644
index 00000000..d10a1454
--- /dev/null
+++ b/hrp/convert.go
@@ -0,0 +1 @@
+package hrp
diff --git a/hrp/internal/builtin/utils.go b/hrp/internal/builtin/utils.go
index 2f109a5b..63dbfd0f 100644
--- a/hrp/internal/builtin/utils.go
+++ b/hrp/internal/builtin/utils.go
@@ -1,7 +1,6 @@
package builtin
import (
- "bufio"
"bytes"
"crypto/hmac"
"crypto/sha256"
@@ -352,40 +351,6 @@ func ReadFile(path string) ([]byte, error) {
return file, nil
}
-func ReadCmdLines(path string) ([]string, error) {
- var err error
- path, err = filepath.Abs(path)
- if err != nil {
- log.Error().Err(err).Str("path", path).Msg("convert absolute path failed")
- return nil, err
- }
- file, err := os.Open(path)
- if err != nil {
- log.Error().Err(err).Str("path", path).Msg("open file failed")
- return nil, err
- }
- defer file.Close()
-
- var line string
- var lines []string
- scanner := bufio.NewScanner(file)
- // FIXME: resize scanner's capacity for lines over 64K
- for scanner.Scan() {
- text := strings.TrimSpace(scanner.Text())
- if text == "" || text == "\n" {
- continue
- }
- if strings.HasSuffix(text, "\\") {
- line = line + strings.Trim(text, "\\")
- continue
- }
- line = line + text
- lines = append(lines, line)
- line = ""
- }
- return lines, scanner.Err()
-}
-
func GetFileNameWithoutExtension(path string) string {
base := filepath.Base(path)
ext := filepath.Ext(base)
diff --git a/hrp/internal/dial/curl.go b/hrp/internal/dial/curl.go
deleted file mode 100644
index b40b29b3..00000000
--- a/hrp/internal/dial/curl.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package dial
-
-import (
- "bytes"
- "fmt"
- "path/filepath"
-
- "github.com/rs/zerolog/log"
-
- "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
- "github.com/httprunner/httprunner/v4/hrp/internal/env"
- "github.com/httprunner/httprunner/v4/hrp/internal/myexec"
-)
-
-const (
- normalResult = "STDOUT"
- errorResult = "STDERR"
- failedResult = "FAILED"
-)
-
-type CurlResult struct {
- Result string `json:"result"`
- ErrorMsg string `json:"errorMsg"`
- ResultType string `json:"resultType"`
-}
-
-func DoCurl(args []string) (err error) {
- var saveTests bool
- for i, arg := range args {
- if arg == "--save-tests" {
- args = append(args[:i], args[i+1:]...)
- saveTests = true
- }
- }
- var curlResult CurlResult
- defer func() {
- if saveTests {
- curlResultName := fmt.Sprintf("curl_result_%v.json", env.StartTimeStr)
- curlResultPath := filepath.Join(env.RootDir, curlResultName)
- err = builtin.Dump2JSON(curlResult, curlResultPath)
- if err != nil {
- log.Error().Err(err).Msg("save dns resolution result failed")
- }
- }
- }()
-
- cmd := myexec.Command("curl", args...)
- var stdout, stderr bytes.Buffer
- cmd.Stdout = &stdout
- cmd.Stderr = &stderr
-
- err = cmd.Run()
- if err != nil {
- log.Error().Err(err).Msgf("fail to run curl command")
- curlResult.ErrorMsg = err.Error()
- curlResult.Result = stderr.String()
- curlResult.ResultType = errorResult
- return
- }
- if stdout.String() != "" {
- fmt.Println(stdout.String())
- curlResult.Result = stdout.String()
- curlResult.ResultType = normalResult
- } else if stderr.String() != "" {
- fmt.Println(stderr.String())
- curlResult.ErrorMsg = stderr.String()
- curlResult.ResultType = errorResult
- }
- return
-}
diff --git a/hrp/internal/dial/traceroute.go b/hrp/internal/dial/traceroute.go
deleted file mode 100644
index d20e5f1b..00000000
--- a/hrp/internal/dial/traceroute.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package dial
-
-type TraceRouteOptions struct {
- MaxTTL int
- Queries int
- SaveTests bool
-}
-
-type TraceRouteResult struct {
- IP string `json:"ip"`
- Details []TraceRouteResultNode `json:"details"`
- Suc bool `json:"suc"`
- ErrMsg string `json:"errMsg"`
-}
-
-type TraceRouteResultNode struct {
- Id int `json:"id"`
- Ip string `json:"ip"`
- Time string `json:"time"`
-}
diff --git a/hrp/internal/dial/traceroute_unix.go b/hrp/internal/dial/traceroute_unix.go
deleted file mode 100644
index 14532da9..00000000
--- a/hrp/internal/dial/traceroute_unix.go
+++ /dev/null
@@ -1,104 +0,0 @@
-//go:build darwin || linux
-
-package dial
-
-import (
- "bufio"
- "fmt"
- "net/url"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "time"
-
- "github.com/pkg/errors"
- "github.com/rs/zerolog/log"
-
- "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
- "github.com/httprunner/httprunner/v4/hrp/internal/env"
- "github.com/httprunner/httprunner/v4/hrp/internal/myexec"
-)
-
-var (
- regexIPAddr = regexp.MustCompile(`([\d.]+)`)
- regexElapsedTime = regexp.MustCompile(`(\d+\.\d+)`)
- regexTraceroutePass = regexp.MustCompile(fmt.Sprintf(`(\d+)[\s*]+(\S+)\s+\(%s\)\s+%s\s+ms`, regexIPAddr, regexElapsedTime))
- regexTracerouteFailure = regexp.MustCompile(`(\d+)[\s*]+$`)
-)
-
-func DoTraceRoute(traceRouteOptions *TraceRouteOptions, args []string) (err error) {
- if len(args) != 1 {
- return errors.New("there should be one argument")
- }
- var traceRouteResult TraceRouteResult
- defer func() {
- if traceRouteOptions.SaveTests {
- traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", env.StartTimeStr)
- traceRouteResultPath := filepath.Join(env.RootDir, traceRouteResultName)
- err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath)
- if err != nil {
- log.Error().Err(err).Msg("save traceroute result failed")
- }
- }
- }()
-
- traceRouteTarget := args[0]
- parsedURL, err := url.Parse(traceRouteTarget)
- if err == nil && parsedURL.Host != "" {
- log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host)
- traceRouteTarget = strings.Split(parsedURL.Host, ":")[0]
- }
-
- cmd := myexec.Command("traceroute", "-m", strconv.Itoa(traceRouteOptions.MaxTTL),
- "-q", strconv.Itoa(traceRouteOptions.Queries), traceRouteTarget)
- stdout, _ := cmd.StdoutPipe()
-
- startT := time.Now()
- defer func() {
- log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT))
- }()
-
- log.Info().Msgf("start to traceroute %v", traceRouteTarget)
- err = cmd.Start()
- if err != nil {
- traceRouteResult.Suc = false
- traceRouteResult.ErrMsg = "execute traceroute failed"
- log.Error().Err(err).Msg("start command failed")
- return
- }
-
- scanner := bufio.NewScanner(stdout)
- for scanner.Scan() {
- hopLine := scanner.Text()
- fmt.Println(hopLine)
- failureLine := regexTracerouteFailure.FindStringSubmatch(hopLine)
- if len(failureLine) == 2 {
- hopID, _ := strconv.Atoi(failureLine[1])
- traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{
- Id: hopID,
- })
- continue
- }
- passLine := regexTraceroutePass.FindStringSubmatch(hopLine)
- if len(passLine) == 5 {
- hopID, _ := strconv.Atoi(passLine[1])
- traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{
- Id: hopID,
- Ip: passLine[3],
- Time: passLine[4],
- })
- traceRouteResult.Suc = true
- }
- }
- hopCount := len(traceRouteResult.Details)
- traceRouteResult.IP = traceRouteResult.Details[hopCount-1].Ip
- err = cmd.Wait()
- if err != nil {
- traceRouteResult.Suc = false
- traceRouteResult.ErrMsg = "wait traceroute finish failed"
- log.Error().Err(err).Msg("wait command failed")
- return
- }
- return
-}
diff --git a/hrp/internal/dial/traceroute_windows.go b/hrp/internal/dial/traceroute_windows.go
deleted file mode 100644
index 92f5ea28..00000000
--- a/hrp/internal/dial/traceroute_windows.go
+++ /dev/null
@@ -1,103 +0,0 @@
-//go:build windows
-
-package dial
-
-import (
- "bufio"
- "fmt"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "time"
-
- "github.com/pkg/errors"
- "github.com/rs/zerolog/log"
-
- "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
- "github.com/httprunner/httprunner/v4/hrp/internal/myexec"
-)
-
-var (
- regexTracertPass = regexp.MustCompile(`(\d+)[\s*<]+(\d+)\s+ms`)
- regexTracertFailure = regexp.MustCompile(`(\d+)[\s*]+Request timed out`)
-)
-
-func DoTraceRoute(traceRouteOptions *TraceRouteOptions, args []string) (err error) {
- if len(args) != 1 {
- return errors.New("there should be one argument")
- }
- var traceRouteResult TraceRouteResult
- defer func() {
- if traceRouteOptions.SaveTests {
- traceRouteResultName := fmt.Sprintf("traceroute_result_%v.json", env.StartTimeStr)
- traceRouteResultPath := filepath.Join(env.RootDir, traceRouteResultName)
- err = builtin.Dump2JSON(traceRouteResult, traceRouteResultPath)
- if err != nil {
- log.Error().Err(err).Msg("save traceroute result failed")
- }
- }
- }()
-
- traceRouteTarget := args[0]
- parsedURL, err := url.Parse(traceRouteTarget)
- if err == nil && parsedURL.Host != "" {
- log.Info().Msgf("parse input url %v and extract host %v", traceRouteTarget, parsedURL.Host)
- traceRouteTarget = strings.Split(parsedURL.Host, ":")[0]
- }
-
- cmd := myexec.Command("tracert", "-h", strconv.Itoa(traceRouteOptions.MaxTTL), traceRouteTarget)
- stdout, _ := cmd.StdoutPipe()
-
- startT := time.Now()
- defer func() {
- log.Info().Msgf("for target %s, traceroute costs %v", traceRouteTarget, time.Since(startT))
- }()
-
- log.Info().Msgf("start to traceroute %v", traceRouteTarget)
- err = cmd.Start()
- if err != nil {
- traceRouteResult.Suc = false
- traceRouteResult.ErrMsg = "execute traceroute failed"
- log.Error().Err(err).Msg("start command failed")
- return
- }
-
- scanner := bufio.NewScanner(stdout)
- for scanner.Scan() {
- hopLine := scanner.Text()
- fmt.Println(hopLine)
- failureLine := regexTracertFailure.FindStringSubmatch(hopLine)
- if len(failureLine) == 2 {
- hopID, _ := strconv.Atoi(failureLine[1])
- traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{
- Id: hopID,
- })
- continue
- }
- passLine := regexTracertPass.FindStringSubmatch(hopLine)
- if len(passLine) == 3 {
- hopID, _ := strconv.Atoi(passLine[1])
- fields := strings.Fields(hopLine)
- hopIP := strings.Trim(fields[len(fields)-1], "[]")
- traceRouteResult.Details = append(traceRouteResult.Details, TraceRouteResultNode{
- Id: hopID,
- Ip: hopIP,
- Time: passLine[2],
- })
- traceRouteResult.Suc = true
- }
- }
- hopCount := len(traceRouteResult.Details)
- traceRouteResult.IP = traceRouteResult.Details[hopCount-1].Ip
- err = cmd.Wait()
- if err != nil {
- traceRouteResult.Suc = false
- traceRouteResult.ErrMsg = "wait traceroute finish failed"
- log.Error().Err(err).Msg("wait command failed")
- return
- }
- return
-}
diff --git a/hrp/pkg/boomer/output_test.go b/hrp/pkg/boomer/output_test.go
index 58e3dee1..7a9d3c76 100644
--- a/hrp/pkg/boomer/output_test.go
+++ b/hrp/pkg/boomer/output_test.go
@@ -81,7 +81,7 @@ func TestConsoleOutput(t *testing.T) {
data["stats"] = []interface{}{stat}
stat["name"] = "http"
- stat["method"] = "post"
+ stat["method"] = "POST"
stat["num_requests"] = int64(100)
stat["num_failures"] = int64(10)
stat["response_times"] = map[int64]int64{
diff --git a/hrp/pkg/convert/README.md b/hrp/pkg/convert/README.md
index 90c0314f..56e8b3ad 100644
--- a/hrp/pkg/convert/README.md
+++ b/hrp/pkg/convert/README.md
@@ -4,32 +4,36 @@
```shell
$ hrp convert -h
-convert to JSON/YAML/gotest/pytest testcases
+convert multiple source format to HttpRunner JSON/YAML/gotest/pytest cases
Usage:
hrp convert $path... [flags]
Flags:
+ --from-har load from HAR format
+ --from-json load from json case format (default true)
+ --from-postman load from postman format
+ --from-yaml load from yaml case format
-h, --help help for convert
- -d, --output-dir string specify output directory, default to the same dir with har file
- -p, --profile string specify profile path to override headers (except for auto-generated headers) and cookies
- --to-gotest convert to gotest scripts (TODO)
- --to-json convert to JSON scripts (default true)
+ -d, --output-dir string specify output directory
+ -p, --profile string specify profile path to override headers and cookies
+ --to-json convert to JSON case scripts (default true)
--to-pytest convert to pytest scripts
- --to-yaml convert to YAML scripts
+ --to-yaml convert to YAML case scripts
Global Flags:
--log-json set log to json format
-l, --log-level string set log level (default "INFO")
+ --venv string specify python3 venv path
```
-`hrp convert` 指令用于将 HAR/Postman/JMeter/Swagger 文件或 curl/Apache ab 指令转化为 JSON/YAML/gotest/pytest 形态的测试用例,同时也支持测试用例各个形态之间的相互转化。
+`hrp convert` 指令用于将 HAR/Postman/JMeter/Swagger 文件或 curl/Apache ab 指令转化为 HttpRunner JSON/YAML/gotest/pytest 形态的测试用例,同时也支持 HttpRunner 测试用例各个形态之间的相互转化。
该指令所有选项的详细说明如下:
-1. `--to-json / --to-yaml / --to-gotest / --to-pytest` 用于将输入转化为对应形态的测试用例,四个选项中最多只能指定一个,如果不指定则默认会将输入转化为 JSON 形态的测试用例
-2. `--output-dir` 后接测试用例的期望输出目录的路径,用于将转换生成的测试用例输出到对应的文件夹
-3. `--profile` 后接 profile 配置文件的路径,目前支持替换(不存在则会创建)或者覆盖输入的外部脚本/测试用例中的 `Headers` 和 `Cookies` 信息,profile 文件的后缀可以为 `json/yaml/yml`,下面给出两类 profile 配置文件的示例:
+- `--to-json / --to-yaml / --to-gotest / --to-pytest` 用于将输入转化为对应形态的 HttpRunner 测试用例,四个选项中最多只能指定一个,如果不指定则默认会将输入转化为 JSON 形态的测试用例
+- `--output-dir` 后接测试用例的期望输出目录的路径,用于将转换生成的测试用例输出到对应的文件夹;默认输出的文件夹为源文件所在的文件夹
+- `--profile` 后接 profile 配置文件的路径,目前支持替换(不存在则会创建)或者覆盖输入的外部脚本/测试用例中的 `Headers` 和 `Cookies` 信息,profile 文件的后缀可以为 `json/yaml/yml`,下面给出两类 profile 配置文件的示例:
- 根据 profile 替换指定的 `Headers` 和 `Cookies` 信息
@@ -52,10 +56,9 @@ cookies:
## 注意事项
-1. 输出的测试用例文件名格式为 `Postman 工程文件名称(不带拓展名)` + `_test` + `.json/.yaml/.go/.py 后缀`,如果该文件已经存在则会进行覆盖
-2. `hrp convert` 可以自动识别输入类型,因此不需要通过选项来手动制定输入类型,如遇到无法识别、不支持或转换失败的情况,则会输出错误日志并跳过,不会影响其他转换过程的正常进行
-3. 在 profile 文件中,指定 `override` 字段为 `false/true` 可以选择修改模式为替换/覆盖。需要注意的是,如果不指定该字段则 profile 的默认修改模式为替换模式
-4. 输入为 JSON/YAML 测试用例时,良好兼容 Golang/Python 双引擎的请求体、断言格式细微差异,输出的 JSON/YAML 则统一采用 Golang 引擎的风格
+1. 输出的测试用例文件名格式为 `源文件名称(不带拓展名)` + `_test` + `.json/.yaml/.go/.py 后缀`,如果该文件已经存在则会进行覆盖
+2. 在 profile 文件中,指定 `override` 字段为 `false/true` 可以选择修改模式为替换/覆盖。需要注意的是,如果不指定该字段则 profile 的默认修改模式为替换模式
+3. 输入为 JSON/YAML 测试用例时,良好兼容 Golang/Python 双引擎的请求体、断言格式细微差异,输出的 JSON/YAML 则统一采用 Golang 引擎的风格
## 转换流程图
diff --git a/hrp/pkg/convert/converter.go b/hrp/pkg/convert/converter.go
deleted file mode 100644
index c849df65..00000000
--- a/hrp/pkg/convert/converter.go
+++ /dev/null
@@ -1,278 +0,0 @@
-package convert
-
-import (
- _ "embed"
- "fmt"
- "path/filepath"
- "strings"
-
- "github.com/pkg/errors"
- "github.com/rs/zerolog/log"
-
- "github.com/httprunner/httprunner/v4/hrp"
- "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
- "github.com/httprunner/httprunner/v4/hrp/internal/env"
- "github.com/httprunner/httprunner/v4/hrp/internal/myexec"
- "github.com/httprunner/httprunner/v4/hrp/internal/sdk"
-)
-
-// target testcase format extensions
-const (
- suffixJSON = ".json"
- suffixYAML = ".yaml"
- suffixGoTest = ".go"
- suffixPyTest = ".py"
-)
-
-type OutputType int
-
-const (
- OutputTypeJSON OutputType = iota // default output type: JSON
- OutputTypeYAML
- OutputTypeGoTest
- OutputTypePyTest
-)
-
-func (outputType OutputType) String() string {
- switch outputType {
- case OutputTypeYAML:
- return "yaml"
- case OutputTypeGoTest:
- return "gotest"
- case OutputTypePyTest:
- return "pytest"
- default:
- return "json"
- }
-}
-
-// Profile is used to override or update(create if not existed) original headers and cookies
-type Profile struct {
- Override bool `json:"override" yaml:"override"`
- Headers map[string]string `json:"headers" yaml:"headers"`
- Cookies map[string]string `json:"cookies" yaml:"cookies"`
-}
-
-func Run(outputType OutputType, outputDir, profilePath string, args []string) {
- // report event
- sdk.SendEvent(sdk.EventTracking{
- Category: "ConvertTests",
- Action: fmt.Sprintf("hrp convert --to-%s", outputType.String()),
- })
-
- var outputFiles []string
- for _, inputSample := range args {
- // loads source file and convert to TCase format
- tCase, err := LoadTCase(inputSample)
- if err != nil {
- log.Warn().Err(err).Str("input sample", inputSample).Msg("convert input sample failed")
- continue
- }
-
- caseConverter := &TCaseConverter{
- InputSample: inputSample,
- OutputDir: outputDir,
- TCase: tCase,
- }
-
- // override TCase with profile
- if profilePath != "" {
- caseConverter.overrideWithProfile(profilePath)
- }
-
- // convert TCase format to target case format
- var outputFile string
- switch outputType {
- case OutputTypeYAML:
- outputFile, err = caseConverter.ToYAML()
- case OutputTypeGoTest:
- outputFile, err = caseConverter.ToGoTest()
- case OutputTypePyTest:
- outputFile, err = caseConverter.ToPyTest()
- default:
- outputFile, err = caseConverter.ToJSON()
- }
- if err != nil {
- log.Error().Err(err).
- Str("input sample", caseConverter.InputSample).
- Msg("convert case failed")
- continue
- }
- outputFiles = append(outputFiles, outputFile)
- }
- log.Info().Strs("output files", outputFiles).Msg("conversion completed")
-}
-
-// LoadTCase loads source file and convert to TCase type
-func LoadTCase(inputSample string) (*hrp.TCase, error) {
- if strings.HasPrefix(inputSample, "curl ") {
- // 'path' contains curl command
- curlCase, err := LoadSingleCurlCase(inputSample)
- if err != nil {
- return nil, err
- }
- return curlCase, nil
- }
- extName := filepath.Ext(inputSample)
- if extName == "" {
- return nil, errors.New("file extension is not specified")
- }
- switch extName {
- case ".har":
- tCase, err := LoadHARCase(inputSample)
- if err != nil {
- return nil, err
- }
- return tCase, nil
- case ".json":
- // priority: hrp JSON case > postman > swagger
- // check if hrp JSON case
- tCase, err := LoadJSONCase(inputSample)
- if err == nil {
- return tCase, nil
- }
-
- // check if postman format
- casePostman, err := LoadPostmanCase(inputSample)
- if err == nil {
- return casePostman, nil
- }
-
- // check if swagger format
- caseSwagger, err := LoadSwaggerCase(inputSample)
- if err == nil {
- return caseSwagger, nil
- }
-
- return nil, errors.New("unexpected JSON format")
- case ".yaml", ".yml":
- // priority: hrp YAML case > swagger
- // check if hrp YAML case
- tCase, err := NewYAMLCase(inputSample)
- if err == nil {
- return tCase, nil
- }
-
- // check if swagger format
- caseSwagger, err := LoadSwaggerCase(inputSample)
- if err == nil {
- return caseSwagger, nil
- }
-
- return nil, errors.New("unexpected YAML format")
- case ".go": // TODO
- return nil, errors.New("convert gotest is not implemented")
- case ".py": // TODO
- return nil, errors.New("convert pytest is not implemented")
- case ".jmx": // TODO
- return nil, errors.New("convert JMeter jmx is not implemented")
- case ".txt":
- curlCase, err := LoadCurlCase(inputSample)
- if err != nil {
- return nil, err
- }
- return curlCase, nil
- }
-
- return nil, fmt.Errorf("unsupported file type: %v", extName)
-}
-
-// TCaseConverter holds the common properties of case converter
-type TCaseConverter struct {
- InputSample string
- OutputDir string
- TCase *hrp.TCase
-}
-
-func (c *TCaseConverter) genOutputPath(suffix string) string {
- var outFileFullName string
- if curlCmd := strings.TrimSpace(c.InputSample); strings.HasPrefix(curlCmd, "curl ") {
- outFileFullName = fmt.Sprintf("curl_%v_test%v", env.StartTimeStr, suffix)
- if c.OutputDir != "" {
- return filepath.Join(c.OutputDir, outFileFullName)
- } else {
- return filepath.Join(env.RootDir, outFileFullName)
- }
- }
- outFileFullName = builtin.GetFileNameWithoutExtension(c.InputSample) + "_test" + suffix
- if c.OutputDir != "" {
- return filepath.Join(c.OutputDir, outFileFullName)
- } else {
- return filepath.Join(filepath.Dir(c.InputSample), outFileFullName)
- }
- // TODO avoid outFileFullName conflict?
-}
-
-// convert TCase to pytest case
-func (c *TCaseConverter) ToPyTest() (string, error) {
- jsonPath, err := c.ToJSON()
- if err != nil {
- return "", errors.Wrap(err, "convert to JSON case failed")
- }
-
- args := append([]string{"make"}, jsonPath)
- err = myexec.ExecPython3Command("httprunner", args...)
- if err != nil {
- return "", err
- }
- return c.genOutputPath(suffixPyTest), nil
-}
-
-// TODO: convert TCase to gotest case
-func (c *TCaseConverter) ToGoTest() (string, error) {
- return "", nil
-}
-
-// convert TCase to JSON case
-func (c *TCaseConverter) ToJSON() (string, error) {
- jsonPath := c.genOutputPath(suffixJSON)
- err := builtin.Dump2JSON(c.TCase, jsonPath)
- if err != nil {
- return "", err
- }
- return jsonPath, nil
-}
-
-// convert TCase to YAML case
-func (c *TCaseConverter) ToYAML() (string, error) {
- yamlPath := c.genOutputPath(suffixYAML)
- err := builtin.Dump2YAML(c.TCase, yamlPath)
- if err != nil {
- return "", err
- }
- return yamlPath, nil
-}
-
-func (c *TCaseConverter) overrideWithProfile(path string) error {
- log.Info().Str("path", path).Msg("load profile")
- profile := new(Profile)
- err := builtin.LoadFile(path, profile)
- if err != nil {
- log.Warn().Str("path", path).
- Msg("failed to load profile, ignore!")
- return err
- }
-
- log.Info().Interface("profile", profile).Msg("override with profile")
- for _, step := range c.TCase.TestSteps {
- // override original headers and cookies
- if profile.Override {
- step.Request.Headers = make(map[string]string)
- step.Request.Cookies = make(map[string]string)
- }
- // update (create if not existed) original headers and cookies
- if step.Request.Headers == nil {
- step.Request.Headers = make(map[string]string)
- }
- if step.Request.Cookies == nil {
- step.Request.Cookies = make(map[string]string)
- }
- for k, v := range profile.Headers {
- step.Request.Headers[k] = v
- }
- for k, v := range profile.Cookies {
- step.Request.Cookies[k] = v
- }
- }
- return nil
-}
diff --git a/hrp/pkg/convert/from_ab.go b/hrp/pkg/convert/from_ab.go
new file mode 100644
index 00000000..233bcded
--- /dev/null
+++ b/hrp/pkg/convert/from_ab.go
@@ -0,0 +1 @@
+package convert
diff --git a/hrp/pkg/convert/from_curl.go b/hrp/pkg/convert/from_curl.go
index 7c60bf76..4ec2c81e 100644
--- a/hrp/pkg/convert/from_curl.go
+++ b/hrp/pkg/convert/from_curl.go
@@ -1,9 +1,11 @@
package convert
import (
+ "bufio"
"fmt"
"net/http"
"net/url"
+ "os"
"strings"
"github.com/google/shlex"
@@ -11,7 +13,6 @@ import (
"github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp"
- "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
"github.com/httprunner/httprunner/v4/hrp/internal/json"
)
@@ -94,12 +95,14 @@ func init() {
// LoadCurlCase loads testcase from one or more curl commands in .txt file
func LoadCurlCase(path string) (*hrp.TCase, error) {
- cmds, err := builtin.ReadCmdLines(path)
+ cmds, err := readFileLines(path)
if err != nil {
return nil, err
}
tCase := &hrp.TCase{
- Config: &hrp.TConfig{Name: "testcase converted from curl command"},
+ Config: &hrp.TConfig{
+ Name: "testcase converted from curl command",
+ },
}
for _, cmd := range cmds {
tSteps, err := LoadCurlSteps(cmd)
@@ -115,21 +118,24 @@ func LoadCurlCase(path string) (*hrp.TCase, error) {
return tCase, nil
}
-// LoadSingleCurlCase one testcase from one curl command
-func LoadSingleCurlCase(cmd string) (*hrp.TCase, error) {
- tSteps, err := LoadCurlSteps(cmd)
+func readFileLines(path string) ([]string, error) {
+ file, err := os.Open(path)
if err != nil {
+ log.Error().Err(err).Str("path", path).Msg("open file failed")
return nil, err
}
- tCase := &hrp.TCase{
- Config: &hrp.TConfig{Name: "testcase converted from curl command"},
- TestSteps: tSteps,
+ defer file.Close()
+
+ var lines []string
+ scanner := bufio.NewScanner(file)
+ for scanner.Scan() {
+ line := strings.TrimSpace(scanner.Text())
+ if line == "" || line == "\n" {
+ continue
+ }
+ lines = append(lines, line)
}
- err = tCase.MakeCompat()
- if err != nil {
- return nil, err
- }
- return tCase, nil
+ return lines, scanner.Err()
}
// LoadCurlSteps loads one teststep from one curl command
diff --git a/hrp/pkg/convert/from_gotest.go b/hrp/pkg/convert/from_gotest.go
index eecde5a5..d25dff85 100644
--- a/hrp/pkg/convert/from_gotest.go
+++ b/hrp/pkg/convert/from_gotest.go
@@ -31,9 +31,9 @@ func convert2GoTestScripts(paths ...string) error {
for _, testCase := range testCases {
tc := testCase.ToTCase()
converter := TCaseConverter{
- TCase: tc,
+ tCase: tc,
}
- pytestPath, err := converter.ToPyTest()
+ pytestPath, err := converter.toPyTest()
if err != nil {
log.Error().Err(err).
Str("originPath", tc.Config.Path).
diff --git a/hrp/pkg/convert/from_jmeter.go b/hrp/pkg/convert/from_jmeter.go
new file mode 100644
index 00000000..233bcded
--- /dev/null
+++ b/hrp/pkg/convert/from_jmeter.go
@@ -0,0 +1 @@
+package convert
diff --git a/hrp/pkg/convert/from_json.go b/hrp/pkg/convert/from_json.go
index 8e40f88b..b1c05ac5 100644
--- a/hrp/pkg/convert/from_json.go
+++ b/hrp/pkg/convert/from_json.go
@@ -2,13 +2,14 @@ package convert
import (
"github.com/pkg/errors"
+ "github.com/rs/zerolog/log"
"github.com/httprunner/httprunner/v4/hrp"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
)
func LoadJSONCase(path string) (*hrp.TCase, error) {
- // load json case file
+ log.Info().Str("path", path).Msg("load json case file")
caseJSON := new(hrp.TCase)
err := builtin.LoadFile(path, caseJSON)
if err != nil {
diff --git a/hrp/pkg/convert/from_postman.go b/hrp/pkg/convert/from_postman.go
index 9dbbfa7c..8518789b 100644
--- a/hrp/pkg/convert/from_postman.go
+++ b/hrp/pkg/convert/from_postman.go
@@ -113,7 +113,7 @@ var contentTypeMap = map[string]string{
}
func LoadPostmanCase(path string) (*hrp.TCase, error) {
- // load postman file
+ log.Info().Str("path", path).Msg("load postman case file")
casePostman, err := loadCasePostman(path)
if err != nil {
return nil, err
@@ -330,7 +330,6 @@ func (s *stepFromPostman) makeRequestBodyRaw(item *TItem) (err error) {
}
}()
- // extract language type, default languageType: text
languageType := "text"
iOptions := item.Request.Body.Options
if iOptions != nil {
@@ -340,19 +339,20 @@ func (s *stepFromPostman) makeRequestBodyRaw(item *TItem) (err error) {
}
}
- // make request body and indicate Content-Type
- rawBody := item.Request.Body.Raw
- if languageType == "json" {
+ s.Request.Body = item.Request.Body.Raw
+ contentType := s.Request.Headers["Content-Type"]
+ if strings.Contains(contentType, "application/json") || languageType == "json" {
var iBody interface{}
- err = json.Unmarshal([]byte(rawBody), &iBody)
+ err = json.Unmarshal([]byte(item.Request.Body.Raw), &iBody)
if err != nil {
return errors.Wrap(err, "make request body (raw -> json) failed")
}
s.Request.Body = iBody
- } else {
- s.Request.Body = rawBody
}
- s.Request.Headers["Content-Type"] = contentTypeMap[languageType]
+
+ if contentType == "" {
+ s.Request.Headers["Content-Type"] = contentTypeMap[languageType]
+ }
return
}
diff --git a/hrp/pkg/convert/from_yaml.go b/hrp/pkg/convert/from_yaml.go
index 977e47de..b96f0a2d 100644
--- a/hrp/pkg/convert/from_yaml.go
+++ b/hrp/pkg/convert/from_yaml.go
@@ -9,7 +9,7 @@ import (
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
)
-func NewYAMLCase(path string) (*hrp.TCase, error) {
+func LoadYAMLCase(path string) (*hrp.TCase, error) {
// load yaml case file
caseJSON := new(hrp.TCase)
err := builtin.LoadFile(path, caseJSON)
diff --git a/hrp/pkg/convert/main.go b/hrp/pkg/convert/main.go
new file mode 100644
index 00000000..7ac5a238
--- /dev/null
+++ b/hrp/pkg/convert/main.go
@@ -0,0 +1,225 @@
+package convert
+
+import (
+ _ "embed"
+ "fmt"
+ "path/filepath"
+
+ "github.com/rs/zerolog/log"
+
+ "github.com/httprunner/httprunner/v4/hrp"
+ "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
+ "github.com/httprunner/httprunner/v4/hrp/internal/sdk"
+)
+
+// target testcase format extensions
+const (
+ suffixJSON = ".json"
+ suffixYAML = ".yaml"
+ suffixGoTest = ".go"
+ suffixPyTest = ".py"
+ suffixHAR = ".har"
+)
+
+type FromType int
+
+const (
+ FromTypeJSON FromType = iota
+ FromTypeYAML
+ FromTypeHAR
+ FromTypePostman
+ FromTypeCurl
+ FromTypeSwagger
+ FromTypePyest
+ FromTypeGotest
+)
+
+func (fromType FromType) String() string {
+ switch fromType {
+ case FromTypeYAML:
+ return "yaml"
+ case FromTypeHAR:
+ return "har"
+ case FromTypePostman:
+ return "postman"
+ case FromTypeSwagger:
+ return "swagger"
+ case FromTypeCurl:
+ return "curl"
+ case FromTypeGotest:
+ return "gotest"
+ case FromTypePyest:
+ return "pytest"
+ default:
+ return "json"
+ }
+}
+
+func (fromType FromType) Extensions() []string {
+ switch fromType {
+ case FromTypeYAML:
+ return []string{suffixYAML, ".yml"}
+ case FromTypeHAR:
+ return []string{suffixHAR}
+ case FromTypePostman, FromTypeSwagger:
+ return []string{suffixJSON}
+ case FromTypeCurl:
+ return []string{".txt", ".curl"}
+ case FromTypeGotest:
+ return []string{suffixGoTest}
+ case FromTypePyest:
+ return []string{suffixPyTest}
+ default:
+ return []string{suffixJSON}
+ }
+}
+
+type OutputType int
+
+const (
+ OutputTypeJSON OutputType = iota // default output type: JSON
+ OutputTypeYAML
+ OutputTypeGoTest
+ OutputTypePyTest
+)
+
+func (outputType OutputType) String() string {
+ switch outputType {
+ case OutputTypeYAML:
+ return "yaml"
+ case OutputTypeGoTest:
+ return "gotest"
+ case OutputTypePyTest:
+ return "pytest"
+ default:
+ return "json"
+ }
+}
+
+// Profile is used to override or update(create if not existed) original headers and cookies
+type Profile struct {
+ Override bool `json:"override" yaml:"override"`
+ Headers map[string]string `json:"headers" yaml:"headers"`
+ Cookies map[string]string `json:"cookies" yaml:"cookies"`
+}
+
+func NewConverter(outputDir, profilePath string) *TCaseConverter {
+ return &TCaseConverter{
+ profilePath: profilePath,
+ outputDir: outputDir,
+ }
+}
+
+// TCaseConverter holds the common properties of case converter
+type TCaseConverter struct {
+ fromFile string
+ profilePath string
+ outputDir string
+ tCase *hrp.TCase
+}
+
+// LoadCase loads source file and convert to TCase type
+func (c *TCaseConverter) loadCase(casePath string, fromType FromType) error {
+ c.fromFile = casePath
+ var err error
+ switch fromType {
+ case FromTypeJSON:
+ c.tCase, err = LoadJSONCase(casePath)
+ case FromTypeYAML:
+ c.tCase, err = LoadYAMLCase(casePath)
+ case FromTypeHAR:
+ c.tCase, err = LoadHARCase(casePath)
+ case FromTypePostman:
+ c.tCase, err = LoadPostmanCase(casePath)
+ case FromTypeSwagger:
+ c.tCase, err = LoadSwaggerCase(casePath)
+ case FromTypeCurl:
+ c.tCase, err = LoadCurlCase(casePath)
+ }
+ return err
+}
+
+func (c *TCaseConverter) Convert(casePath string, fromType FromType, outputType OutputType) error {
+ // report event
+ sdk.SendEvent(sdk.EventTracking{
+ Category: "ConvertTests",
+ Action: fmt.Sprintf("hrp convert --to-%s", outputType.String()),
+ })
+ log.Info().Str("path", casePath).
+ Str("fromType", fromType.String()).
+ Str("outputType", outputType.String()).
+ Msg("convert testcase")
+
+ // load source file
+ err := c.loadCase(casePath, fromType)
+ if err != nil {
+ return err
+ }
+
+ // override TCase with profile
+ if c.profilePath != "" {
+ c.overrideWithProfile(c.profilePath)
+ }
+
+ // convert to target format
+ var outputFile string
+ switch outputType {
+ case OutputTypeYAML:
+ outputFile, err = c.toYAML()
+ case OutputTypeGoTest:
+ outputFile, err = c.toGoTest()
+ case OutputTypePyTest:
+ outputFile, err = c.toPyTest()
+ default:
+ outputFile, err = c.toJSON()
+ }
+ if err != nil {
+ return err
+ }
+
+ log.Info().Str("outputFile", outputFile).Msg("conversion completed")
+ return nil
+}
+
+func (c *TCaseConverter) genOutputPath(suffix string) string {
+ outFileFullName := builtin.GetFileNameWithoutExtension(c.fromFile) + "_test" + suffix
+ if c.outputDir != "" {
+ return filepath.Join(c.outputDir, outFileFullName)
+ } else {
+ return filepath.Join(filepath.Dir(c.fromFile), outFileFullName)
+ }
+}
+
+func (c *TCaseConverter) overrideWithProfile(path string) error {
+ log.Info().Str("path", path).Msg("load profile")
+ profile := new(Profile)
+ err := builtin.LoadFile(path, profile)
+ if err != nil {
+ log.Warn().Str("path", path).
+ Msg("failed to load profile, ignore!")
+ return err
+ }
+
+ log.Info().Interface("profile", profile).Msg("override with profile")
+ for _, step := range c.tCase.TestSteps {
+ // override original headers and cookies
+ if profile.Override {
+ step.Request.Headers = make(map[string]string)
+ step.Request.Cookies = make(map[string]string)
+ }
+ // update (create if not existed) original headers and cookies
+ if step.Request.Headers == nil {
+ step.Request.Headers = make(map[string]string)
+ }
+ if step.Request.Cookies == nil {
+ step.Request.Cookies = make(map[string]string)
+ }
+ for k, v := range profile.Headers {
+ step.Request.Headers[k] = v
+ }
+ for k, v := range profile.Cookies {
+ step.Request.Cookies[k] = v
+ }
+ }
+ return nil
+}
diff --git a/hrp/pkg/convert/converter_test.go b/hrp/pkg/convert/main_test.go
similarity index 77%
rename from hrp/pkg/convert/converter_test.go
rename to hrp/pkg/convert/main_test.go
index 4c6c0f58..9ed3e432 100644
--- a/hrp/pkg/convert/converter_test.go
+++ b/hrp/pkg/convert/main_test.go
@@ -13,31 +13,33 @@ const (
profileOverridePath = "../../../examples/data/profile_override.yml"
)
+var converter *TCaseConverter
+
+func init() {
+ converter = NewConverter("", "")
+}
+
func TestLoadTCase(t *testing.T) {
- tCase, err := LoadTCase(harPath)
+ err := converter.loadCase(harPath, FromTypeHAR)
if !assert.NoError(t, err) {
t.Fatal()
}
- if !assert.NotEmpty(t, tCase) {
+ if !assert.NotEmpty(t, converter.tCase) {
t.Fatal()
}
}
func TestLoadHARWithProfileOverride(t *testing.T) {
- tCase, err := LoadTCase(harPath)
+ err := converter.loadCase(harPath, FromTypeHAR)
if !assert.NoError(t, err) {
t.Fatal()
}
- if !assert.NotEmpty(t, tCase) {
+ if !assert.NotEmpty(t, converter.tCase) {
t.Fatal()
}
- caseConverter := &TCaseConverter{
- TCase: tCase,
- }
-
// override TCase with profile
- err = caseConverter.overrideWithProfile(profileOverridePath)
+ err = converter.overrideWithProfile(profileOverridePath)
if !assert.NoError(t, err) {
t.Fatal()
}
@@ -45,12 +47,12 @@ func TestLoadHARWithProfileOverride(t *testing.T) {
for i := 0; i < 3; i++ {
if !assert.Equal(t,
map[string]string{"Content-Type": "application/x-www-form-urlencoded"},
- caseConverter.TCase.TestSteps[i].Request.Headers) {
+ converter.tCase.TestSteps[i].Request.Headers) {
t.FailNow()
}
if !assert.Equal(t,
map[string]string{"UserName": "debugtalk"},
- caseConverter.TCase.TestSteps[i].Request.Cookies) {
+ converter.tCase.TestSteps[i].Request.Cookies) {
t.FailNow()
}
}
@@ -58,7 +60,7 @@ func TestLoadHARWithProfileOverride(t *testing.T) {
func TestMakeRequestWithProfile(t *testing.T) {
caseConverter := &TCaseConverter{
- TCase: &hrp.TCase{
+ tCase: &hrp.TCase{
TestSteps: []*hrp.TStep{
{
Request: &hrp.Request{
@@ -87,19 +89,19 @@ func TestMakeRequestWithProfile(t *testing.T) {
if !assert.Equal(t, map[string]string{
"Content-Type": "application/x-www-form-urlencoded", "User-Agent": "hrp",
- }, caseConverter.TCase.TestSteps[0].Request.Headers) {
+ }, caseConverter.tCase.TestSteps[0].Request.Headers) {
t.Fatal()
}
if !assert.Equal(t, map[string]string{
"UserName": "debugtalk", "abc": "123",
- }, caseConverter.TCase.TestSteps[0].Request.Cookies) {
+ }, caseConverter.tCase.TestSteps[0].Request.Cookies) {
t.Fatal()
}
}
func TestMakeRequestWithProfileOverride(t *testing.T) {
caseConverter := &TCaseConverter{
- TCase: &hrp.TCase{
+ tCase: &hrp.TCase{
TestSteps: []*hrp.TStep{
{
Request: &hrp.Request{
@@ -129,12 +131,12 @@ func TestMakeRequestWithProfileOverride(t *testing.T) {
if !assert.Equal(t, map[string]string{
"Content-Type": "application/x-www-form-urlencoded",
- }, caseConverter.TCase.TestSteps[0].Request.Headers) {
+ }, caseConverter.tCase.TestSteps[0].Request.Headers) {
t.Fatal()
}
if !assert.Equal(t, map[string]string{
"UserName": "debugtalk",
- }, caseConverter.TCase.TestSteps[0].Request.Cookies) {
+ }, caseConverter.tCase.TestSteps[0].Request.Cookies) {
t.Fatal()
}
}
diff --git a/hrp/pkg/convert/to_gotest.go b/hrp/pkg/convert/to_gotest.go
new file mode 100644
index 00000000..643c896a
--- /dev/null
+++ b/hrp/pkg/convert/to_gotest.go
@@ -0,0 +1,6 @@
+package convert
+
+// TODO: convert TCase to gotest case
+func (c *TCaseConverter) toGoTest() (string, error) {
+ return "", nil
+}
diff --git a/hrp/pkg/convert/to_json.go b/hrp/pkg/convert/to_json.go
new file mode 100644
index 00000000..1fb47ed4
--- /dev/null
+++ b/hrp/pkg/convert/to_json.go
@@ -0,0 +1,13 @@
+package convert
+
+import "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
+
+// convert TCase to JSON case
+func (c *TCaseConverter) toJSON() (string, error) {
+ jsonPath := c.genOutputPath(suffixJSON)
+ err := builtin.Dump2JSON(c.tCase, jsonPath)
+ if err != nil {
+ return "", err
+ }
+ return jsonPath, nil
+}
diff --git a/hrp/pkg/convert/to_pytest.go b/hrp/pkg/convert/to_pytest.go
new file mode 100644
index 00000000..224974c3
--- /dev/null
+++ b/hrp/pkg/convert/to_pytest.go
@@ -0,0 +1,22 @@
+package convert
+
+import (
+ "github.com/pkg/errors"
+
+ "github.com/httprunner/httprunner/v4/hrp/internal/myexec"
+)
+
+// convert TCase to pytest case
+func (c *TCaseConverter) toPyTest() (string, error) {
+ jsonPath, err := c.toJSON()
+ if err != nil {
+ return "", errors.Wrap(err, "convert to JSON case failed")
+ }
+
+ args := append([]string{"make"}, jsonPath)
+ err = myexec.ExecPython3Command("httprunner", args...)
+ if err != nil {
+ return "", err
+ }
+ return c.genOutputPath(suffixPyTest), nil
+}
diff --git a/hrp/pkg/convert/to_yaml.go b/hrp/pkg/convert/to_yaml.go
new file mode 100644
index 00000000..0ada6511
--- /dev/null
+++ b/hrp/pkg/convert/to_yaml.go
@@ -0,0 +1,13 @@
+package convert
+
+import "github.com/httprunner/httprunner/v4/hrp/internal/builtin"
+
+// convert TCase to YAML case
+func (c *TCaseConverter) toYAML() (string, error) {
+ yamlPath := c.genOutputPath(suffixYAML)
+ err := builtin.Dump2YAML(c.tCase, yamlPath)
+ if err != nil {
+ return "", err
+ }
+ return yamlPath, nil
+}
diff --git a/hrp/pkg/uixt/android_action.go b/hrp/pkg/uixt/android_action.go
deleted file mode 100644
index b8081614..00000000
--- a/hrp/pkg/uixt/android_action.go
+++ /dev/null
@@ -1,158 +0,0 @@
-package uixt
-
-import "strings"
-
-type touchGesture struct {
- Touch PointF `json:"touch"`
- Time float64 `json:"time"`
-}
-
-type TouchAction []touchGesture
-
-func NewTouchAction(cap ...int) *TouchAction {
- if len(cap) == 0 || cap[0] <= 0 {
- cap = []int{8}
- }
- tmp := make(TouchAction, 0, cap[0])
- return &tmp
-}
-
-func (ta *TouchAction) Add(x, y int, startTime ...float64) *TouchAction {
- return ta.AddFloat(float64(x), float64(y), startTime...)
-}
-
-func (ta *TouchAction) AddFloat(x, y float64, startTime ...float64) *TouchAction {
- if len(startTime) == 0 {
- var tmp float64 = 0
- if len(*ta) != 0 {
- g := (*ta)[len(*ta)-1]
- tmp = g.Time + 0.05
- }
- startTime = []float64{tmp}
- }
- *ta = append(*ta, touchGesture{Touch: PointF{x, y}, Time: startTime[0]})
- return ta
-}
-
-func (ta *TouchAction) AddPoint(point Point, startTime ...float64) *TouchAction {
- return ta.AddFloat(float64(point.X), float64(point.Y), startTime...)
-}
-
-func (ta *TouchAction) AddPointF(point PointF, startTime ...float64) *TouchAction {
- return ta.AddFloat(point.X, point.Y, startTime...)
-}
-
-func (ud *uiaDriver) MultiPointerGesture(gesture1 *TouchAction, gesture2 *TouchAction, tas ...*TouchAction) (err error) {
- // Must provide coordinates for at least 2 pointers
- actions := make([]*TouchAction, 0)
- actions = append(actions, gesture1, gesture2)
- if len(tas) != 0 {
- actions = append(actions, tas...)
- }
- data := map[string]interface{}{
- "actions": actions,
- }
- // register(postHandler, new MultiPointerGesture("/wd/hub/session/:sessionId/touch/multi/perform"))
- _, err = ud.httpPOST(data, "/session", ud.sessionId, "/touch/multi/perform")
- return
-}
-
-type w3cGesture map[string]interface{}
-
-func _newW3CGesture() w3cGesture {
- return make(w3cGesture)
-}
-
-func (g w3cGesture) _set(key string, value interface{}) w3cGesture {
- g[key] = value
- return g
-}
-
-func (g w3cGesture) pause(duration float64) w3cGesture {
- return g._set("type", "pause").
- _set("duration", duration)
-}
-
-func (g w3cGesture) keyDown(value string) w3cGesture {
- return g._set("type", "keyDown").
- _set("value", value)
-}
-
-func (g w3cGesture) keyUp(value string) w3cGesture {
- return g._set("type", "keyUp").
- _set("value", value)
-}
-
-func (g w3cGesture) pointerDown(button int) w3cGesture {
- return g._set("type", "pointerDown")._set("button", button)
-}
-
-func (g w3cGesture) pointerUp(button int) w3cGesture {
- return g._set("type", "pointerUp")._set("button", button)
-}
-
-func (g w3cGesture) pointerMove(x, y float64, origin string, duration float64, pressureAndSize ...float64) w3cGesture {
- switch len(pressureAndSize) {
- case 1:
- g._set("pressure", pressureAndSize[0])
- case 2:
- g._set("pressure", pressureAndSize[0])
- g._set("size", pressureAndSize[1])
- }
- return g._set("type", "pointerMove").
- _set("duration", duration).
- _set("origin", origin).
- _set("x", x).
- _set("y", y)
-}
-
-func (g w3cGesture) size(size ...float64) w3cGesture {
- if len(size) == 0 {
- size = []float64{1.0}
- }
- return g._set("size", size[0])
-}
-
-func (g w3cGesture) pressure(pressure ...float64) w3cGesture {
- if len(pressure) == 0 {
- pressure = []float64{1.0}
- }
- return g._set("pressure", pressure[0])
-}
-
-type W3CGestures []w3cGesture
-
-func NewW3CGestures(cap ...int) *W3CGestures {
- if len(cap) == 0 || cap[0] <= 0 {
- cap = []int{8}
- }
- tmp := make(W3CGestures, 0, cap[0])
- return &tmp
-}
-
-func (g *W3CGestures) Pause(duration ...float64) *W3CGestures {
- if len(duration) == 0 || duration[0] < 0 {
- duration = []float64{0.5}
- }
- *g = append(*g, _newW3CGesture().pause(duration[0]*1000))
- return g
-}
-
-func (g *W3CGestures) KeyDown(value string) *W3CGestures {
- *g = append(*g, _newW3CGesture().keyDown(value))
- return g
-}
-
-func (g *W3CGestures) KeyUp(value string) *W3CGestures {
- *g = append(*g, _newW3CGesture().keyUp(value))
- return g
-}
-
-func (g *W3CGestures) SendKeys(text string) *W3CGestures {
- ss := strings.Split(text, "")
- for i := range ss {
- g.KeyDown(ss[i])
- g.KeyUp(ss[i])
- }
- return g
-}
diff --git a/hrp/pkg/uixt/android_device_test.go b/hrp/pkg/uixt/android_device_test.go
deleted file mode 100644
index 2167d2f5..00000000
--- a/hrp/pkg/uixt/android_device_test.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package uixt
-
-import (
- "fmt"
- "testing"
-
- "github.com/httprunner/httprunner/v4/hrp/internal/json"
-)
-
-func TestConvertPoints(t *testing.T) {
- 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) != 3 {
- t.Fatal()
- }
- jsons, _ := json.Marshal(eps)
- println(fmt.Sprintf("%v", string(jsons)))
-}
diff --git a/hrp/pkg/uixt/android_driver.go b/hrp/pkg/uixt/android_driver.go
index 31d43c07..45c7e519 100644
--- a/hrp/pkg/uixt/android_driver.go
+++ b/hrp/pkg/uixt/android_driver.go
@@ -309,45 +309,12 @@ func (ud *uiaDriver) StopCamera() (err error) {
return
}
-func (ud *uiaDriver) ActiveAppInfo() (info AppInfo, err error) {
- // TODO
- return info, errDriverNotImplemented
-}
-
-func (ud *uiaDriver) ActiveAppsList() (appsList []AppBaseInfo, err error) {
- // TODO
- return appsList, errDriverNotImplemented
-}
-
-func (ud *uiaDriver) AppState(bundleId string) (runState AppState, err error) {
- // TODO
- return runState, errDriverNotImplemented
-}
-
-func (ud *uiaDriver) IsLocked() (locked bool, err error) {
- // TODO
- return locked, errDriverNotImplemented
-}
-
-func (ud *uiaDriver) Unlock() (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) Lock() (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
func (ud *uiaDriver) Homescreen() (err error) {
return ud.PressKeyCode(KCHome, KMEmpty)
}
func (ud *uiaDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) {
- if len(flags) == 0 {
- flags = []KeyFlag{KFFromSystem}
- }
- return ud._pressKeyCode(keyCode, metaState, KFFromSystem)
+ return ud._pressKeyCode(keyCode, metaState, flags...)
}
func (ud *uiaDriver) _pressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...KeyFlag) (err error) {
@@ -365,138 +332,28 @@ func (ud *uiaDriver) _pressKeyCode(keyCode KeyCode, metaState KeyMeta, flags ...
return
}
-func (ud *uiaDriver) AlertText() (text string, err error) {
- // register(getHandler, new GetAlertText("/wd/hub/session/:sessionId/alert/text"))
- var rawResp rawResponse
- if rawResp, err = ud.httpGET("/session", ud.sessionId, "alert/text"); err != nil {
- return "", err
- }
- reply := new(struct{ Value string })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return "", err
- }
-
- text = reply.Value
- return
-}
-
-func (ud *uiaDriver) AlertButtons() (btnLabels []string, err error) {
- // TODO
- return btnLabels, errDriverNotImplemented
-}
-
-func (ud *uiaDriver) AlertAccept(label ...string) (err error) {
- data := map[string]interface{}{
- "buttonLabel": nil,
- }
- if len(label) != 0 {
- data["buttonLabel"] = label[0]
- }
- // register(postHandler, new AcceptAlert("/wd/hub/session/:sessionId/alert/accept"))
- _, err = ud.httpPOST(data, "/session", ud.sessionId, "alert/accept")
- return
-}
-
-func (ud *uiaDriver) AlertDismiss(label ...string) (err error) {
- data := map[string]interface{}{
- "buttonLabel": nil,
- }
- if len(label) != 0 {
- data["buttonLabel"] = label[0]
- }
- // register(postHandler, new DismissAlert("/wd/hub/session/:sessionId/alert/dismiss"))
- _, err = ud.httpPOST(data, "/session", ud.sessionId, "alert/dismiss")
- return
-}
-
-func (ud *uiaDriver) AlertSendKeys(text string) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) check() error {
- if ud.adbDevice.Serial() == "" {
- return errors.New("adb daemon: the device is not ready")
- }
- return nil
-}
-
-func (ud *uiaDriver) AppLaunch(bundleId string, launchOpt ...AppLaunchOption) (err error) {
- if err = ud.check(); err != nil {
- return err
- }
-
- var sOutput string
- if sOutput, err = ud.adbDevice.RunShellCommand("monkey -p", bundleId, "-c android.intent.category.LAUNCHER 1"); err != nil {
+func (ud *uiaDriver) AppLaunch(bundleId string) (err error) {
+ // 不指定 Activity 名称启动(启动主 Activity)
+ // adb shell monkey -p -c android.intent.category.LAUNCHER 1
+ sOutput, err := ud.adbDevice.RunShellCommand(
+ "monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1",
+ )
+ if err != nil {
return err
}
if strings.Contains(sOutput, "monkey aborted") {
return fmt.Errorf("app launch: %s", strings.TrimSpace(sOutput))
}
-
- if len(launchOpt) != 0 {
- var ce error
- exists := func(ud WebDriver) (bool, error) {
- for _, opt := range launchOpt {
- if bySelector, ok := opt["bySelector"]; ok {
- for _, e := range bySelector.([]BySelector) {
- _, ce = ud.FindElement(e)
- if ce == nil {
- return true, nil
- }
- }
- }
- }
- return false, nil
- }
- if err = ud.WaitWithTimeoutAndInterval(exists, 45, 1); err != nil {
- return fmt.Errorf("app launch: %s: %w", err.Error(), ce)
- }
- }
- return
-}
-
-func (ud *uiaDriver) AppLaunchUnattached(bundleId string) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-// Dispose corresponds to the command:
-// adb -s $serial forward --remove $localPort
-func (ud *uiaDriver) Dispose() (err error) {
- if err = ud.check(); err != nil {
- return err
- }
- if ud.localPort == 0 {
- return nil
- }
- return ud.adbDevice.ForwardKill(ud.localPort)
+ return nil
}
func (ud *uiaDriver) AppTerminate(bundleId string) (successful bool, err error) {
- if err = ud.check(); err != nil {
- return false, err
- }
-
+ // 强制停止应用,停止 相关的进程
+ // adb shell am force-stop
_, err = ud.adbDevice.RunShellCommand("am", "force-stop", bundleId)
return err == nil, err
}
-func (ud *uiaDriver) AppActivate(bundleId string) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) AppDeactivate(second float64) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) AppAuthReset(resource ProtectedResource) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
func (ud *uiaDriver) Tap(x, y int, options ...DataOption) error {
return ud.TapFloat(float64(x), float64(y), options...)
}
@@ -608,20 +465,6 @@ func (ud *uiaDriver) ForceTouchFloat(x, y, pressure float64, second ...float64)
return errDriverNotImplemented
}
-func (ud *uiaDriver) PerformW3CActions(actions *W3CActions) (err error) {
- data := map[string]interface{}{
- "actions": actions,
- }
- // register(postHandler, new W3CActions("/wd/hub/session/:sessionId/actions"))
- _, err = ud.httpPOST(data, "/session", ud.sessionId, "/actions")
- return
-}
-
-func (ud *uiaDriver) PerformAppiumTouchActions(touchActs *TouchActions) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
func (ud *uiaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
lbl := content
@@ -679,31 +522,7 @@ func (ud *uiaDriver) SendKeys(text string, options ...DataOption) (err error) {
}
func (ud *uiaDriver) Input(text string, options ...DataOption) (err error) {
- data := map[string]interface{}{
- "view": text,
- }
- // new data options in post data for extra uiautomator configurations
- newData := NewData(data, options...)
-
- var element WebElement
- if valuetext, ok := newData["textview"]; ok {
- element, err = ud.FindElement(BySelector{UiAutomator: NewUiSelectorHelper().TextContains(fmt.Sprintf("%v", valuetext)).String()})
- } else if valueid, ok := newData["id"]; ok {
- element, err = ud.FindElement(BySelector{ResourceIdID: fmt.Sprintf("%v", valueid)})
- } else if valuedesc, ok := newData["description"]; ok {
- element, err = ud.FindElement(BySelector{UiAutomator: NewUiSelectorHelper().Description(fmt.Sprintf("%v", valuedesc)).String()})
- } else {
- element, err = ud.FindElement(BySelector{ClassName: ElementType{EditText: true}})
- }
- if err != nil {
- return err
- }
- return element.SendKeys(text, options...)
-}
-
-func (ud *uiaDriver) KeyboardDismiss(keyNames ...string) (err error) {
- // TODO
- return errDriverNotImplemented
+ return ud.SendKeys(text, options...)
}
func (ud *uiaDriver) PressButton(devBtn DeviceButton) (err error) {
@@ -711,47 +530,6 @@ func (ud *uiaDriver) PressButton(devBtn DeviceButton) (err error) {
return errDriverNotImplemented
}
-func (ud *uiaDriver) IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) ExpectNotification(notifyName string, notifyType NotificationType, second ...int) (err error) {
- // register(postHandler, new OpenNotification("/wd/hub/session/:sessionId/appium/device/open_notifications"))
- _, err = ud.httpPOST(nil, "/session", ud.sessionId, "appium/device/open_notifications")
- return
-}
-
-func (ud *uiaDriver) SiriActivate(text string) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) SiriOpenUrl(url string) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) Orientation() (orientation Orientation, err error) {
- // register(getHandler, new GetOrientation("/wd/hub/session/:sessionId/orientation"))
- var rawResp rawResponse
- if rawResp, err = ud.httpGET("/session", ud.sessionId, "orientation"); err != nil {
- return "", err
- }
- reply := new(struct{ Value Orientation })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return "", err
- }
-
- orientation = reply.Value
- return
-}
-
-func (ud *uiaDriver) SetOrientation(orientation Orientation) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
func (ud *uiaDriver) Rotation() (rotation Rotation, err error) {
// register(getHandler, new GetRotation("/wd/hub/session/:sessionId/rotation"))
var rawResp rawResponse
@@ -772,105 +550,6 @@ func (ud *uiaDriver) SetRotation(rotation Rotation) (err error) {
return errDriverNotImplemented
}
-func (ud *uiaDriver) MatchTouchID(isMatch bool) (err error) {
- // TODO
- return errDriverNotImplemented
-}
-
-func (ud *uiaDriver) _findElements(method, selector string, elementID ...string) (elements []WebElement, err error) {
- // register(postHandler, new FindElements("/wd/hub/session/:sessionId/elements"))
- data := map[string]interface{}{
- "strategy": method,
- "selector": selector,
- }
- if len(elementID) != 0 {
- data["context"] = elementID[0]
- }
- var rawResp rawResponse
- if rawResp, err = ud.httpPOST(data, "/session", ud.sessionId, "/elements"); err != nil {
- return nil, err
- }
- reply := new(struct{ Value []map[string]string })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return nil, err
- }
- if len(reply.Value) == 0 {
- return nil, fmt.Errorf("no such element: unable to find an element using '%s', value '%s'", method, selector)
- }
- elements = make([]WebElement, len(reply.Value))
- for i, elem := range reply.Value {
- var id string
- if id = elementIDFromValue(elem); id == "" {
- return nil, fmt.Errorf("invalid element returned: %+v", reply)
- }
- uie := WebElement(uiaElement{parent: ud, id: id})
- elements[i] = uie
- }
- return
-}
-
-func (ud *uiaDriver) _findElement(method, selector string, elementID ...string) (elem *uiaElement, err error) {
- // register(postHandler, new FindElement("/wd/hub/session/:sessionId/element"))
- data := map[string]interface{}{
- "strategy": method,
- "selector": selector,
- }
- if len(elementID) != 0 {
- data["context"] = elementID[0]
- }
- var rawResp rawResponse
- if rawResp, err = ud.httpPOST(data, "/session", ud.sessionId, "/element"); err != nil {
- return nil, err
- }
- reply := new(struct{ Value map[string]string })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return nil, err
- }
- if len(reply.Value) == 0 {
- return nil, fmt.Errorf("no such element: unable to find an element using '%s', value '%s'", method, selector)
- }
- var id string
- if id = elementIDFromValue(reply.Value); id == "" {
- return nil, fmt.Errorf("invalid element returned: %+v", reply)
- }
- elem = &uiaElement{parent: ud, id: id}
- return
-}
-
-func (ud *uiaDriver) ActiveElement() (element WebElement, err error) {
- // TODO
- return element, errDriverNotImplemented
-}
-
-func (ud *uiaDriver) FindElement(by BySelector) (element WebElement, err error) {
- return ud._findElement(by.getUsingAndValue())
-}
-
-func (ud *uiaDriver) FindElements(by BySelector) (elements []WebElement, err error) {
- // [[FBRoute POST:@"/elements"] respondWithTarget:self action:@selector(handleFindElements:)]
- using, value := by.getUsingAndValue()
- data := map[string]interface{}{
- "using": using,
- "value": value,
- }
- var rawResp rawResponse
- if rawResp, err = ud.httpPOST(data, "/session", ud.sessionId, "/elements"); err != nil {
- return nil, err
- }
- var elementIDs []string
- if elementIDs, err = rawResp.valueConvertToElementIDs(); err != nil {
- if errors.Is(err, errNoSuchElement) {
- return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
- }
- return nil, err
- }
- elements = make([]WebElement, len(elementIDs))
- for i := range elementIDs {
- elements[i] = WebElement(uiaElement{parent: ud, id: elementIDs[i]})
- }
- return
-}
-
func (ud *uiaDriver) Screenshot() (raw *bytes.Buffer, err error) {
// register(getHandler, new CaptureScreenshot("/wd/hub/session/:sessionId/screenshot"))
var rawResp rawResponse
diff --git a/hrp/pkg/uixt/android_elment.go b/hrp/pkg/uixt/android_elment.go
deleted file mode 100644
index 760f52ab..00000000
--- a/hrp/pkg/uixt/android_elment.go
+++ /dev/null
@@ -1,306 +0,0 @@
-package uixt
-
-import (
- "bytes"
- "encoding/base64"
- "encoding/json"
-
- "github.com/pkg/errors"
- "github.com/rs/zerolog/log"
-)
-
-var errElementNotImplemented = errors.New("element method not implemented")
-
-type uiaElement struct {
- parent *uiaDriver
- id string
-}
-
-func (ue uiaElement) Click() (err error) {
- // register(postHandler, new Click("/wd/hub/session/:sessionId/element/:id/click"))
- _, err = ue.parent.httpPOST(nil, "/session", ue.parent.sessionId, "/element", ue.id, "/click")
- return
-}
-
-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,
- }
-
- // new data options in post data for extra uiautomator configurations
- newData := NewData(data, options...)
-
- _, err = ue.parent.httpPOST(newData, "/session", ue.parent.sessionId, "/element", ue.id, "/value")
- return
-}
-
-func (ue uiaElement) Clear() (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) Tap(x, y int) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) TapFloat(x, y float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) DoubleTap() (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) TouchAndHold(second ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) TwoFingerTap() (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) TapWithNumberOfTaps(numberOfTaps, numberOfTouches int) (err error) {
- // Todo: implement
- log.Fatal().Msg("not support")
- return
-}
-
-func (ue uiaElement) ForceTouch(pressure float64, second ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) Drag(fromX, fromY, toX, toY int, steps ...float64) (err error) {
- return ue.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), steps...)
-}
-
-func (ue uiaElement) DragFloat(fromX, fromY, toX, toY float64, steps ...float64) (err error) {
- if len(steps) == 0 {
- steps = []float64{12 * 10}
- } else {
- steps[0] = 12 * 10
- }
- data := map[string]interface{}{
- "elementId": ue.id,
- "endX": toX,
- "endY": toY,
- "steps": steps[0],
- }
- return ue.parent._drag(data)
-}
-
-func (ue uiaElement) Swipe(fromX, fromY, toX, toY int) error {
- return ue.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY))
-}
-
-func (ue uiaElement) SwipeFloat(fromX, fromY, toX, toY float64) error {
- options := []DataOption{
- WithDataSteps(12),
- WithCustomOption("elementId", ue.id),
- }
- return ue.parent._swipe(fromX, fromY, toX, toY, options...)
-}
-
-func (ue uiaElement) SwipeDirection(direction Direction, velocity ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) Pinch(scale, velocity float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) PinchToZoomOutByW3CAction(scale ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) Rotate(rotation float64, velocity ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) PickerWheelSelect(order PickerWheelOrder, offset ...int) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) scroll(data interface{}) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) ScrollElementByName(name string) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) ScrollElementByPredicate(predicate string) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) ScrollToVisible() (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) ScrollDirection(direction Direction, distance ...float64) (err error) {
- // TODO
- return errElementNotImplemented
-}
-
-func (ue uiaElement) FindElement(by BySelector) (element WebElement, err error) {
- method, selector := by.getMethodAndSelector()
- return ue.parent._findElement(method, selector, ue.id)
-}
-
-func (ue uiaElement) FindElements(by BySelector) (elements []WebElement, err error) {
- method, selector := by.getMethodAndSelector()
- return ue.parent._findElements(method, selector, ue.id)
-}
-
-func (ue uiaElement) FindVisibleCells() (elements []WebElement, err error) {
- // TODO
- return elements, errElementNotImplemented
-}
-
-func (ue uiaElement) Rect() (rect Rect, err error) {
- // register(getHandler, new GetRect("/wd/hub/session/:sessionId/element/:id/rect"))
- var rawResp rawResponse
- if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/rect"); err != nil {
- return Rect{}, err
- }
- reply := new(struct{ Value Rect })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return Rect{}, err
- }
- rect = reply.Value
- return
-}
-
-func (ue uiaElement) Location() (point Point, err error) {
- // register(getHandler, new Location("/wd/hub/session/:sessionId/element/:id/location"))
- var rawResp rawResponse
- if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/location"); err != nil {
- return Point{-1, -1}, err
- }
- reply := new(struct{ Value Point })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return Point{-1, -1}, err
- }
- point = reply.Value
- return
-}
-
-func (ue uiaElement) Size() (size Size, err error) {
- // register(getHandler, new GetSize("/wd/hub/session/:sessionId/element/:id/size"))
- var rawResp rawResponse
- if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/size"); err != nil {
- return Size{-1, -1}, err
- }
- reply := new(struct{ Value Size })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return Size{-1, -1}, err
- }
- size = reply.Value
- return
-}
-
-func (ue uiaElement) Text() (text string, err error) {
- // register(getHandler, new GetText("/wd/hub/session/:sessionId/element/:id/text"))
- var rawResp rawResponse
- if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/text"); err != nil {
- return "", err
- }
- reply := new(struct{ Value string })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return "", err
- }
- text = reply.Value
- return
-}
-
-func (ue uiaElement) Type() (elemType string, err error) {
- // TODO
- return elemType, errElementNotImplemented
-}
-
-func (ue uiaElement) IsEnabled() (enabled bool, err error) {
- // TODO
- return enabled, errElementNotImplemented
-}
-
-func (ue uiaElement) IsDisplayed() (displayed bool, err error) {
- // TODO
- return displayed, errElementNotImplemented
-}
-
-func (ue uiaElement) IsSelected() (selected bool, err error) {
- // TODO
- return selected, errElementNotImplemented
-}
-
-func (ue uiaElement) IsAccessible() (accessible bool, err error) {
- // TODO
- return accessible, errElementNotImplemented
-}
-
-func (ue uiaElement) IsAccessibilityContainer() (isAccessibilityContainer bool, err error) {
- // TODO
- return isAccessibilityContainer, errElementNotImplemented
-}
-
-func (ue uiaElement) GetAttribute(attr ElementAttribute) (value string, err error) {
- // register(getHandler, new GetElementAttribute("/wd/hub/session/:sessionId/element/:id/attribute/:name"))
- var rawResp rawResponse
- if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/attribute", attr.getAttributeName()); err != nil {
- return "", err
- }
- reply := new(struct{ Value string })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return "", err
- }
- value = reply.Value
- return
-}
-
-func (ue uiaElement) UID() (uid string) {
- return ue.id
-}
-
-func (ue uiaElement) Screenshot() (raw *bytes.Buffer, err error) {
- // W3C endpoint
- // register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/element/:id/screenshot"))
- // JSONWP endpoint
- // register(getHandler, new GetElementScreenshot("/wd/hub/session/:sessionId/screenshot/:id"))
- var rawResp rawResponse
- if rawResp, err = ue.parent.httpGET("/session", ue.parent.sessionId, "/element", ue.id, "/screenshot"); err != nil {
- return nil, err
- }
- reply := new(struct{ Value string })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return nil, err
- }
-
- var decodeStr []byte
- if decodeStr, err = base64.StdEncoding.DecodeString(reply.Value); err != nil {
- return nil, err
- }
-
- raw = bytes.NewBuffer(decodeStr)
- return
-}
diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go
index 21deeaf3..aa119e0f 100644
--- a/hrp/pkg/uixt/android_test.go
+++ b/hrp/pkg/uixt/android_test.go
@@ -3,6 +3,8 @@
package uixt
import (
+ "encoding/json"
+ "fmt"
"io/ioutil"
"testing"
"time"
@@ -115,20 +117,6 @@ func TestDriver_Screenshot(t *testing.T) {
t.Log(ioutil.WriteFile("/Users/hero/Desktop/s1.png", screenshot.Bytes(), 0o600))
}
-func TestDriver_Orientation(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- orientation, err := driver.Orientation()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(orientation)
-}
-
func TestDriver_Rotation(t *testing.T) {
driver, err := NewUIADriver(nil, uiaServerURL)
if err != nil {
@@ -232,20 +220,6 @@ func TestDriver_DeviceInfo(t *testing.T) {
t.Logf("bluetooth state: %s", devInfo.Bluetooth.State)
}
-func TestDriver_AlertText(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- alertText, err := driver.AlertText()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(alertText)
-}
-
func TestDriver_Tap(t *testing.T) {
driver, err := NewUIADriver(nil, uiaServerURL)
if err != nil {
@@ -326,333 +300,13 @@ func TestDriver_SendKeys(t *testing.T) {
}
}
-//func TestDriver_PressBack(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.PressBack()
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestDriver_PressKeyCode(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.PressKeyCodeAsync(KCx)
-// if err != nil {
-// t.Fatal(err)
-// }
-// err = driver.PressKeyCodeAsync(KCx, KMCapLocked)
-// if err != nil {
-// t.Fatal(err)
-// }
-// // err = driver.PressKeyCodeAsync(KCExplorer)
-// // if err != nil {
-// // t.Fatal(err)
-// // }
-//
-// err = driver.PressKeyCode(KCExplorer, KMEmpty)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestDriver_LongPressKeyCode(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.LongPressKeyCode(KCAt, KMEmpty)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestDriver_TouchDown(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// doTouchUp := func() {
-// err = driver.TouchUp(400, 260)
-// if err != nil {
-// t.Fatal(err)
-// }
-// }
-//
-// err = driver.TouchDown(400, 260)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // _ = driver.TapPoint(Point{400, 500})
-// doTouchUp()
-//
-// err = driver.TouchDownPoint(Point{400, 260})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// doTouchUp()
-//}
-//
-//func TestDriver_TouchUp(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.TouchDown(400, 260)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // err = driver.TouchUp(400, 260)
-// err = driver.TouchUpPoint(Point{400, 260})
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestDriver_TouchMove(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// doTouchDown := func(x, y int) {
-// err = driver.TouchDown(x, y)
-// if err != nil {
-// t.Fatal(err)
-// }
-// }
-//
-// doTouchUp := func(x, y int) {
-// err = driver.TouchUp(x, y)
-// if err != nil {
-// t.Fatal(err)
-// }
-// }
-//
-// doTouchDown(400, 260)
-//
-// err = driver.TouchMove(400, 500)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// doTouchUp(400, 500)
-//
-// doTouchDown(400, 500)
-//
-// err = driver.TouchMove(400, 260)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// doTouchUp(400, 260)
-//}
-//
-//func TestDriver_OpenNotification(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.OpenNotification()
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestDriver_Flick(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.Flick(50, -100)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestDriver_ScrollTo(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.ScrollTo(BySelector{ClassName: "android.widget.SeekBar"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestDriver_MultiPointerGesture(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// gesture1 := NewTouchAction().Add(150, 340, 0.35).AddFloat(50, 300)
-// gesture2 := NewTouchAction().Add(200, 340).AddFloat(300, 300)
-// gesture3 := NewTouchAction().Add(300, 500).AddFloat(350, 500).AddPoint(Point{300, 550}).AddPointF(PointF{350, 550})
-// _ = gesture3
-//
-// // err = driver.MultiPointerGesture(gesture1, gesture2)
-// err = driver.MultiPointerGesture(gesture1, gesture2, gesture3)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestDriver_PerformW3CActions(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // actionKey := NewW3CAction(ATKey, NewW3CGestures().KeyDown("g").KeyUp("g").Pause().KeyDown("o").KeyUp("o"))
-// // actionKey := NewW3CAction(ATKey, NewW3CGestures().SendKeys("golang"))
-// // err = driver.PerformW3CActions(actionKey)
-// // if err != nil {
-// // t.Fatal(err)
-// // }
-//
-// // var queryField map[string]string
-// // queryField = make(map[string]string)
-// // {
-// // queryField = map[string]string{
-// // "a": "",
-// // }
-// // }
-//
-// elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/search"})
-// if err != nil {
-// t.Fatal(err)
-// }
-// // actionPointer := NewW3CAction(ATPointer, NewW3CGestures().PointerMove(0, 0, elem.id).PointerDown().Pause(3).PointerUp())
-// // actionPointer := NewW3CAction(ATPointer,
-// // NewW3CGestures().PointerMove(400, 500, "viewport").PointerDown().Pause(2).
-// // PointerMove(0, 0, elem.id).Pause(2).
-// // PointerMove(20, 0, "pointer").Pause(2).
-// // PointerUp(),
-// // )
-// actionPointer := NewW3CAction(ATPointer,
-// NewW3CGestures().PointerMoveTo(400, 500).PointerDown().
-// PointerMouseOver(0, 0, elem).
-// PointerMoveRelative(20, 0).PointerUp())
-// err = driver.PerformW3CActions(actionPointer)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-//
-//func TestDriver_GetClipboard(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// text, err := driver.GetClipboard()
-// if err != nil {
-// t.Fatal(err)
-// }
-// t.Log(text)
-//}
-//
-//func TestDriver_SetClipboard(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// content := "test123"
-// err = driver.SetClipboard(ClipDataTypePlaintext, content)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// text, err := driver.GetClipboard()
-// if err != nil {
-// t.Fatal(err)
-// }
-// if text != content {
-// t.Fatal("should be the same")
-// }
-//}
-
-func TestDriver_AlertAccept(t *testing.T) {
+func TestDriver_PressBack(t *testing.T) {
driver, err := NewUIADriver(nil, uiaServerURL)
if err != nil {
t.Fatal(err)
}
- err = driver.AlertAccept()
- // err = driver.AlertAccept("是")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestDriver_AlertDismiss(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- // err = driver.AlertDismiss()
- err = driver.AlertDismiss("否")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-//func TestDriver_SetAppiumSettings(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// appiumSettings, err := driver.GetAppiumSettings()
-// if err != nil {
-// t.Fatal(err)
-// }
-// sdopd := appiumSettings["shutdownOnPowerDisconnect"]
-// t.Log("shutdownOnPowerDisconnect:", sdopd)
-//
-// err = driver.SetAppiumSettings(map[string]interface{}{"shutdownOnPowerDisconnect": !sdopd.(bool)})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// appiumSettings, err = driver.GetAppiumSettings()
-// if err != nil {
-// t.Fatal(err)
-// }
-// if appiumSettings["shutdownOnPowerDisconnect"] == sdopd.(bool) {
-// t.Fatal("should not be equal")
-// }
-// t.Log("shutdownOnPowerDisconnect:", appiumSettings["shutdownOnPowerDisconnect"])
-//}
-
-func TestDriver_SetOrientation(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- err = driver.SetOrientation(OrientationLandscapeLeft)
- // err = driver.SetOrientation(OrientationPortrait)
+ err = driver.PressBack()
if err != nil {
t.Fatal(err)
}
@@ -671,118 +325,6 @@ func TestDriver_SetRotation(t *testing.T) {
}
}
-//func TestDriver_NetworkConnection(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = driver.NetworkConnection(NetworkTypeWifi)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-func TestDriver_FindElement(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "android:id/content"})
- if err != nil {
- t.Fatal(err)
- }
- e := ElementAttribute{}.WithLabel("class")
- t.Log(elem.GetAttribute(e))
-}
-
-func TestDriver_FindElements(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- // elements, err := driver.FindElements(BySelector{ResourceIdID: "com.android.settings:id/title"})
- elements, err := driver.FindElements(BySelector{UiAutomator: "new UiSelector().textStartsWith(\"应\");"})
- if err != nil {
- t.Fatal(err)
- }
- t.Log(len(elements))
-}
-
-func TestDriver_WaitWithTimeoutAndInterval(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
- element, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().className(\"android.view.ViewGroup\");"})
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := element.FindElement(BySelector{UiAutomator: "new UiSelector().className(\"android.widget.LinearLayout\").index(6);"})
- if err != nil {
- t.Fatal(err)
- }
-
- rect, err := elem.Rect()
- if err != nil {
- t.Fatal(err)
- }
-
- x := rect.X + int(float64(rect.Width)*2)
- y := rect.Y + rect.Height/2
- err = driver.Tap(x, y)
- if err != nil {
- t.Fatal(err)
- }
-
- by := BySelector{UiAutomator: "new UiSelector().text(\"科技\");"}
- exists := func(d WebDriver) (bool, error) {
- element, err = d.FindElement(by)
- if err == nil {
- return true, nil
- }
- return false, nil
- }
-
- err = driver.WaitWithTimeoutAndInterval(exists, 1, 1)
- if err != nil {
- t.Fatal(err)
- }
-
- // element, err = driver.FindElement(by)
- // if err != nil {
- // t.Fatal(err)
- // }
-
- err = element.Click()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-//func TestDriver_ActiveElement(t *testing.T) {
-// device, _ := NewAndroidDevice()
-// driver, err := device.NewUSBDriver(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-// defer func() {
-// _ = driver.Dispose()
-// }()
-//
-// element, err := driver.ActiveElement()
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// if err = element.SendKeys("test"); err != nil {
-// t.Fatal(err)
-// }
-//}
-
func TestUiSelectorHelper_NewUiSelectorHelper(t *testing.T) {
uiSelector := NewUiSelectorHelper().Text("a").String()
if uiSelector != `new UiSelector().text("a");` {
@@ -828,39 +370,6 @@ func TestDeviceList(t *testing.T) {
}
}
-//func TestAndroidNewUSBDriver(t *testing.T) {
-// device, _ := NewAndroidDevice()
-// driver, err := device.NewUSBDriver(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-// defer driver.Dispose()
-//
-// ready, err := driver.Status()
-// if err != nil {
-// t.Fatal(err)
-// }
-// if !ready {
-// t.Fatal("should be 'true'")
-// }
-//}
-
-//func TestDriver_ActiveAppPackageName(t *testing.T) {
-// device, _ := NewAndroidDevice()
-// driver, err := device.NewUSBDriver(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-// defer driver.Dispose()
-//
-// appPackageName, err := driver.ActiveAppPackageName()
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// t.Log(appPackageName)
-//}
-
func TestDriver_AppLaunch(t *testing.T) {
device, _ := NewAndroidDevice()
driver, err := device.NewUSBDriver(nil)
@@ -912,7 +421,6 @@ func TestDriver_AppTerminate(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- defer driver.Dispose()
_, err = driver.AppTerminate("tv.danmaku.bili")
if err != nil {
@@ -920,413 +428,12 @@ func TestDriver_AppTerminate(t *testing.T) {
}
}
-//func TestNewWiFiDriver(t *testing.T) {
-// device, _ := NewAndroidDevice(WithAdbIP("192.168.1.28"))
-// driver, err := device.NewHTTPDriver(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // SetDebug(false, true)
-// _, err = driver.ActiveAppActivity()
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestDriver_AppInstall(t *testing.T) {
-// device, _ := NewAndroidDevice()
-// driver, err := device.NewUSBDriver(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-// defer driver.Dispose()
-//
-// err = driver.AppInstall("/Users/hero/Desktop/xuexi_android_10002068.apk")
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestDriver_AppUninstall(t *testing.T) {
-// device, _ := NewAndroidDevice()
-// driver, err := device.NewUSBDriver(nil)
-// if err != nil {
-// t.Fatal(err)
-// }
-// defer driver.Dispose()
-//
-// err = driver.AppUninstall("cn.xuexi.android")
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-func TestBySelector_getMethodAndSelector(t *testing.T) {
- testVal := "test id"
- bySelector := BySelector{ResourceIdID: testVal}
- method, selector := bySelector.getMethodAndSelector()
- if method != "id" || selector != testVal {
- t.Fatal(method, "=", selector)
- }
-
- bySelector = BySelector{ContentDescription: testVal}
- method, selector = bySelector.getMethodAndSelector()
- if method != "accessibility id" || selector != testVal {
- t.Fatal(method, "=", selector)
+func TestConvertPoints(t *testing.T) {
+ 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) != 3 {
+ t.Fatal()
}
+ jsons, _ := json.Marshal(eps)
+ println(fmt.Sprintf("%v", string(jsons)))
}
-
-func TestElement_Text(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- text, err := elem.Text()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(text)
-}
-
-func TestElement_GetAttribute(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- e := ElementAttribute{}.WithName("class")
- attribute, err := elem.GetAttribute(e)
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(attribute)
-}
-
-//func TestElement_ContentDescription(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/search"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// name, err := elem.ContentDescription()
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// t.Log(name)
-//}
-
-func TestElement_Size(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/search"})
- if err != nil {
- t.Fatal(err)
- }
-
- size, err := elem.Size()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(size)
-}
-
-func TestElement_Rect(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- rect, err := elem.Rect()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(rect)
-}
-
-func TestElement_Screenshot(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- screenshot, err := elem.Screenshot()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(ioutil.WriteFile("/Users/hero/Desktop/e1.png", screenshot.Bytes(), 0o600))
-}
-
-func TestElement_Location(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- location, err := elem.Location()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(location)
-}
-
-func TestElement_Click(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/title"})
- if err != nil {
- t.Fatal(err)
- }
-
- err = elem.Click()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestElement_Clear(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "android:id/search_src_text"})
- if err != nil {
- t.Fatal(err)
- }
-
- err = elem.Clear()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestElement_SendKeys(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "android:id/search_src_text"})
- if err != nil {
- t.Fatal(err)
- }
-
- // return
-
- // err = elem.SendKeys("abc")
- err = elem.SendKeys("456")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestElement_FindElements(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/main_content"})
- if err != nil {
- t.Fatal(err)
- }
-
- elements, err := parentElem.FindElements(BySelector{ResourceIdID: "com.android.settings:id/category"})
- if err != nil {
- t.Fatal(err)
- }
- t.Log(len(elements))
-}
-
-func TestElement_FindElement(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/main_content"})
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := parentElem.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(elem.Text())
-}
-
-func TestElement_Swipe(t *testing.T) {
- driver, err := NewUIADriver(nil, uiaServerURL)
- if err != nil {
- t.Fatal(err)
- }
-
- elem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/category_title"})
- if err != nil {
- t.Fatal(err)
- }
-
- rect, err := elem.Rect()
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(rect)
-
- var startX, startY, endX, endY int
- startX = rect.X + rect.Width/20
- startY = rect.Y + rect.Height/2
- endX = startX
- endY = startY - startY/2
- err = elem.Swipe(startX, startY, endX, endY)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-//func TestElement_Drag(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// elements, err := driver.FindElements(BySelector{ClassName: "android.widget.TextView"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// for i, elem := range elements {
-// text, _ := elem.Text()
-// t.Log(i, text)
-// }
-//
-// rect, err := elements[0].Rect()
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // err = elements[0].Drag(300, 450, 256)
-// err = elements[0].Drag(300, 450, 256)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = elements[0].DragTo(elements[1], 256)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// endPoint := PointF{X: float64(rect.X + rect.Width/3*2), Y: float64(rect.Y + rect.Height/2)}
-// err = elements[0].DragPointF(endPoint, 256)
-// if err != nil {
-// t.Fatal()
-// }
-//}
-
-//func TestElement_Flick(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// elem, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().text(\"提示音和通知\");"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = elem.Flick(36, 20, 100)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestElement_ScrollTo(t *testing.T) {
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // how to make it work?
-// // parentElem, err := driver.FindElement(BySelector{ClassName: "android.widget.ScrollView"})
-// // parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.cyanogenmod.filemanager:id/navigation_view_layout"})
-// parentElem, err := driver.FindElement(BySelector{ResourceIdID: "com.android.settings:id/dashboard"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = parentElem.ScrollTo(BySelector{ContentDescription: "电池"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
-
-//func TestElement_ScrollToElement(t *testing.T) {
-// // android.widget.HorizontalScrollView
-// driver, err := NewUIADriver(nil, uiaServerURL)
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// // how to make it work?
-// parentElem, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().resourceId(\"com.android.settings:id/dashboard\");"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// element, err := driver.FindElement(BySelector{UiAutomator: "new UiSelector().text(\"电池\");"})
-// if err != nil {
-// t.Fatal(err)
-// }
-//
-// err = parentElem.ScrollToElement(element)
-// if err != nil {
-// t.Fatal(err)
-// }
-//}
diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go
index 67b21c52..d87e7b18 100644
--- a/hrp/pkg/uixt/ext.go
+++ b/hrp/pkg/uixt/ext.go
@@ -7,10 +7,12 @@ import (
"image"
"image/jpeg"
"image/png"
+ "mime"
+ "mime/multipart"
+ "net/http"
"os"
"path/filepath"
"strings"
- "testing"
"time"
"github.com/httprunner/httprunner/v4/hrp/internal/builtin"
@@ -22,19 +24,18 @@ import (
type MobileMethod string
const (
- AppInstall MobileMethod = "install"
- AppUninstall MobileMethod = "uninstall"
- AppStart MobileMethod = "app_start"
- AppLaunch MobileMethod = "app_launch" // 等待 app 打开并堵塞到 app 首屏加载完成,可以传入 app 的启动参数、环境变量
- AppLaunchUnattached MobileMethod = "app_launch_unattached" // 只负责通知打开 app,不堵塞等待,不可传入启动参数
- AppTerminate MobileMethod = "app_terminate"
- AppStop MobileMethod = "app_stop"
- CtlScreenShot MobileMethod = "screenshot"
- CtlSleep MobileMethod = "sleep"
- CtlStartCamera MobileMethod = "camera_start" // alias for app_launch camera
- CtlStopCamera MobileMethod = "camera_stop" // alias for app_terminate camera
- RecordStart MobileMethod = "record_start"
- RecordStop MobileMethod = "record_stop"
+ AppInstall MobileMethod = "install"
+ AppUninstall MobileMethod = "uninstall"
+ AppStart MobileMethod = "app_start"
+ AppLaunch MobileMethod = "app_launch" // 启动 app 并堵塞等待 app 首屏加载完成
+ AppTerminate MobileMethod = "app_terminate"
+ AppStop MobileMethod = "app_stop"
+ CtlScreenShot MobileMethod = "screenshot"
+ CtlSleep MobileMethod = "sleep"
+ CtlStartCamera MobileMethod = "camera_start" // alias for app_launch camera
+ CtlStopCamera MobileMethod = "camera_stop" // alias for app_terminate camera
+ RecordStart MobileMethod = "record_start"
+ RecordStop MobileMethod = "record_stop"
// UI validation
SelectorName string = "ui_name"
@@ -314,28 +315,6 @@ func isPathExists(path string) bool {
return true
}
-func (dExt *DriverExt) FindUIElement(param string) (ele WebElement, err error) {
- var selector BySelector
- if strings.HasPrefix(param, "/") {
- // xpath
- selector = BySelector{
- XPath: param,
- }
- } else if strings.HasPrefix(param, "com.") {
- // name
- selector = BySelector{
- ResourceIdID: param,
- }
- } else {
- // name
- selector = BySelector{
- LinkText: NewElementAttribute().WithName(param),
- }
- }
-
- return dExt.Driver.FindElement(selector)
-}
-
func (dExt *DriverExt) FindUIRectInUIKit(search string, options ...DataOption) (x, y, width, height float64, err error) {
// click on text, using OCR
if !isPathExists(search) {
@@ -351,30 +330,6 @@ func (dExt *DriverExt) MappingToRectInUIKit(rect image.Rectangle) (x, y, width,
return
}
-func (dExt *DriverExt) PerformTouchActions(touchActions *TouchActions) error {
- return dExt.Driver.PerformAppiumTouchActions(touchActions)
-}
-
-func (dExt *DriverExt) PerformActions(actions *W3CActions) error {
- return dExt.Driver.PerformW3CActions(actions)
-}
-
-func (dExt *DriverExt) IsNameExist(name string) bool {
- selector := BySelector{
- LinkText: NewElementAttribute().WithName(name),
- }
- _, err := dExt.Driver.FindElement(selector)
- return err == nil
-}
-
-func (dExt *DriverExt) IsLabelExist(label string) bool {
- selector := BySelector{
- LinkText: NewElementAttribute().WithLabel(label),
- }
- _, err := dExt.Driver.FindElement(selector)
- return err == nil
-}
-
func (dExt *DriverExt) IsOCRExist(text string) bool {
_, _, _, _, err := dExt.FindTextByOCR(text)
return err == nil
@@ -400,12 +355,6 @@ func (dExt *DriverExt) DoAction(action MobileAction) error {
}
return fmt.Errorf("invalid %s params, should be bundleId(string), got %v",
AppLaunch, action.Params)
- case AppLaunchUnattached:
- if bundleId, ok := action.Params.(string); ok {
- return dExt.Driver.AppLaunchUnattached(bundleId)
- }
- return fmt.Errorf("invalid %s params, should be bundleId(string), got %v",
- AppLaunchUnattached, action.Params)
case ACTION_SwipeToTapApp:
if appName, ok := action.Params.(string); ok {
return dExt.swipeToTapApp(appName, action)
@@ -688,10 +637,6 @@ func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...s
}
var result bool
switch check {
- case SelectorName:
- result = (dExt.IsNameExist(expected) == exists)
- case SelectorLabel:
- result = (dExt.IsLabelExist(expected) == exists)
case SelectorOCR:
result = (dExt.IsOCRExist(expected) == exists)
case SelectorImage:
@@ -717,12 +662,57 @@ func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...s
return true
}
-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 (dExt *DriverExt) ConnectMjpegStream(httpClient *http.Client) (err error) {
+ if httpClient == nil {
+ return errors.New(`'httpClient' can't be nil`)
}
+
+ var req *http.Request
+ if req, err = http.NewRequest(http.MethodGet, "http://*", nil); err != nil {
+ return err
+ }
+
+ var resp *http.Response
+ if resp, err = httpClient.Do(req); err != nil {
+ return err
+ }
+ // defer func() { _ = resp.Body.Close() }()
+
+ var boundary string
+ if _, param, err := mime.ParseMediaType(resp.Header.Get("Content-Type")); err != nil {
+ return err
+ } else {
+ boundary = strings.Trim(param["boundary"], "-")
+ }
+
+ mjpegReader := multipart.NewReader(resp.Body, boundary)
+
+ go func() {
+ for {
+ select {
+ case <-dExt.doneMjpegStream:
+ _ = resp.Body.Close()
+ return
+ default:
+ var part *multipart.Part
+ if part, err = mjpegReader.NextPart(); err != nil {
+ dExt.frame = nil
+ continue
+ }
+
+ raw := new(bytes.Buffer)
+ if _, err = raw.ReadFrom(part); err != nil {
+ dExt.frame = nil
+ continue
+ }
+ dExt.frame = raw
+ }
+ }
+ }()
+
+ return
+}
+
+func (dExt *DriverExt) CloseMjpegStream() {
+ dExt.doneMjpegStream <- true
}
diff --git a/hrp/pkg/uixt/gesture.go b/hrp/pkg/uixt/gesture.go
deleted file mode 100644
index 7642425c..00000000
--- a/hrp/pkg/uixt/gesture.go
+++ /dev/null
@@ -1,44 +0,0 @@
-//go:build opencv
-
-package uixt
-
-import (
- "image"
- "sort"
-)
-
-func (dExt *DriverExt) GesturePassword(pathname string, password ...int) (err error) {
- var rects []image.Rectangle
- if rects, err = dExt.FindAllImageRect(pathname); err != nil {
- return err
- }
-
- sort.Slice(rects, func(i, j int) bool {
- if rects[i].Min.Y < rects[j].Min.Y {
- return true
- } else if rects[i].Min.Y == rects[j].Min.Y {
- if rects[i].Min.X < rects[j].Min.X {
- return true
- }
- }
- return false
- })
-
- touchActions := NewTouchActions(len(password)*2 + 1)
- for i := range password {
- x, y, width, height := dExt.MappingToRectInUIKit(rects[password[i]])
- x = x + width*0.5
- y = y + height*0.5
-
- if i == 0 {
- touchActions.Press(NewTouchActionPress().WithXYFloat(x, y)).
- Wait(0.2)
- } else {
- touchActions.MoveTo(NewTouchActionMoveTo().WithXYFloat(x, y)).
- Wait(0.2)
- }
- }
- touchActions.Release()
-
- return dExt.PerformTouchActions(touchActions)
-}
diff --git a/hrp/pkg/uixt/gesture_test.go b/hrp/pkg/uixt/gesture_test.go
deleted file mode 100644
index c3b8dcad..00000000
--- a/hrp/pkg/uixt/gesture_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-//go:build opencv
-
-package uixt
-
-import (
- "strconv"
- "strings"
- "testing"
-)
-
-func TestDriverExt_GesturePassword(t *testing.T) {
- split := strings.Split("6304258", "")
- password := make([]int, len(split))
- for i := range split {
- password[i], _ = strconv.Atoi(split[i])
- }
-
- driverExt, err := iosDevice.NewDriver(nil)
- checkErr(t, err)
-
- pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/IMG_5.png"
-
- err = driverExt.GesturePassword(pathSearch, password...)
- checkErr(t, err)
-}
diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go
index 3eed2b5b..d324872f 100644
--- a/hrp/pkg/uixt/interface.go
+++ b/hrp/pkg/uixt/interface.go
@@ -4,8 +4,6 @@ import (
"bytes"
"fmt"
"math"
- "reflect"
- "strconv"
"strings"
"time"
@@ -30,13 +28,6 @@ func NewCapabilities() Capabilities {
return make(Capabilities)
}
-func (caps Capabilities) WithAppLaunchOption(launchOpt AppLaunchOption) Capabilities {
- for k, v := range launchOpt {
- caps[k] = v
- }
- return caps
-}
-
// WithDefaultAlertAction
func (caps Capabilities) WithDefaultAlertAction(alertAction AlertAction) Capabilities {
caps["defaultAlertAction"] = alertAction
@@ -286,44 +277,6 @@ func (v AppState) String() string {
}
}
-// AppLaunchOption Configure app launch parameters
-type AppLaunchOption map[string]interface{}
-
-func NewAppLaunchOption() AppLaunchOption {
- return make(AppLaunchOption)
-}
-
-func (opt AppLaunchOption) WithBundleId(bundleId string) AppLaunchOption {
- opt["bundleId"] = bundleId
- return opt
-}
-
-// WithShouldWaitForQuiescence whether to wait for quiescence on application startup
-// Defaults to `true`
-func (opt AppLaunchOption) WithShouldWaitForQuiescence(b bool) AppLaunchOption {
- opt["shouldWaitForQuiescence"] = b
- return opt
-}
-
-// WithArguments The optional array of application command line arguments.
-// The arguments are going to be applied if the application was not running before.
-func (opt AppLaunchOption) WithArguments(args []string) AppLaunchOption {
- opt["arguments"] = args
- return opt
-}
-
-// WithEnvironment The optional dictionary of environment variables for the application, which is going to be executed.
-// The environment variables are going to be applied if the application was not running before.
-func (opt AppLaunchOption) WithEnvironment(env map[string]string) AppLaunchOption {
- opt["environment"] = env
- return opt
-}
-
-func (opt AppLaunchOption) WithBySelector(bySelector ...BySelector) AppLaunchOption {
- opt["bySelector"] = bySelector
- return opt
-}
-
// PasteboardType The type of the item on the pasteboard.
type PasteboardType string
@@ -443,306 +396,6 @@ func (opt SourceOption) WithExcludedAttributes(attributes []string) SourceOption
return opt
}
-const (
- // legacyWebElementIdentifier is the string constant used in the old
- // WebDriver JSON protocol that is the key for the map that contains an
- // unique element identifier.
- legacyWebElementIdentifier = "ELEMENT"
-
- // webElementIdentifier is the string constant defined by the W3C
- // specification that is the key for the map that contains a unique element identifier.
- webElementIdentifier = "element-6066-11e4-a52e-4f735466cecf"
-)
-
-func elementIDFromValue(val map[string]string) string {
- for _, key := range []string{webElementIdentifier, legacyWebElementIdentifier} {
- if v, ok := val[key]; ok && v != "" {
- return v
- }
- }
- return ""
-}
-
-// performance ranking: class name > accessibility id > link text > predicate > class chain > xpath
-type BySelector struct {
- ClassName ElementType `json:"class name"`
-
- // isSearchByIdentifier
- Name string `json:"name"`
- Id string `json:"id"`
- AccessibilityId string `json:"accessibility id"`
-
- // partialSearch
- LinkText ElementAttribute `json:"link text"`
- PartialLinkText ElementAttribute `json:"partial link text"`
- // partialSearch
-
- Predicate string `json:"predicate string"`
-
- ClassChain string `json:"class chain"`
-
- XPath string `json:"xpath"` // not recommended, it's slow because it is not supported by XCTest natively
-
- // Set the search criteria to match the given resource ResourceIdID.
- ResourceIdID string `json:"id"`
- // Set the search criteria to match the content-description property for a widget.
- ContentDescription string `json:"accessibility id"`
-
- UiAutomator string `json:"-android uiautomator"`
-}
-
-func (wl BySelector) getUsingAndValue() (using, value string) {
- vBy := reflect.ValueOf(wl)
- tBy := reflect.TypeOf(wl)
- for i := 0; i < vBy.NumField(); i++ {
- vi := vBy.Field(i).Interface()
- switch vi := vi.(type) {
- case ElementType:
- value = vi.String()
- case string:
- value = vi
- case ElementAttribute:
- value = vi.String()
- }
- if value != "" && value != "UNKNOWN" {
- using = tBy.Field(i).Tag.Get("json")
- return
- }
- }
- return
-}
-
-func (by BySelector) getMethodAndSelector() (method, selector string) {
- vBy := reflect.ValueOf(by)
- tBy := reflect.TypeOf(by)
- for i := 0; i < vBy.NumField(); i++ {
- vi := vBy.Field(i).Interface()
- // switch vi := vi.(type) {
- // case string:
- // selector = vi
- // }
- selector = vi.(string)
- if selector != "" && selector != "UNKNOWN" {
- method = tBy.Field(i).Tag.Get("json")
- return
- }
- }
- return
-}
-
-type ElementAttribute map[string]interface{}
-
-func (ea ElementAttribute) String() string {
- for k, v := range ea {
- switch v := v.(type) {
- case bool:
- return k + "=" + strconv.FormatBool(v)
- case string:
- return k + "=" + v
- default:
- return k + "=" + fmt.Sprintf("%v", v)
- }
- }
- return "UNKNOWN"
-}
-
-func (ea ElementAttribute) getAttributeName() string {
- for k := range ea {
- return k
- }
- return "UNKNOWN"
-}
-
-func NewElementAttribute() ElementAttribute {
- return make(ElementAttribute)
-}
-
-// WithUID Element's unique identifier
-func (ea ElementAttribute) WithUID(uid string) ElementAttribute {
- ea["UID"] = uid
- return ea
-}
-
-// WithAccessibilityContainer Whether element is an accessibility container
-// (contains children of any depth that are accessible)
-func (ea ElementAttribute) WithAccessibilityContainer(b bool) ElementAttribute {
- ea["accessibilityContainer"] = b
- return ea
-}
-
-// WithAccessible Whether element is accessible
-func (ea ElementAttribute) WithAccessible(b bool) ElementAttribute {
- ea["accessible"] = b
- return ea
-}
-
-// WithEnabled Whether element is enabled
-func (ea ElementAttribute) WithEnabled(b bool) ElementAttribute {
- ea["enabled"] = b
- return ea
-}
-
-// WithLabel Element's label
-func (ea ElementAttribute) WithLabel(s string) ElementAttribute {
- ea["label"] = s
- return ea
-}
-
-// WithName Element's name
-func (ea ElementAttribute) WithName(s string) ElementAttribute {
- ea["name"] = s
- return ea
-}
-
-// WithSelected Element's selected state
-func (ea ElementAttribute) WithSelected(b bool) ElementAttribute {
- ea["selected"] = b
- return ea
-}
-
-// WithType Element's type
-func (ea ElementAttribute) WithType(elemType ElementType) ElementAttribute {
- ea["type"] = elemType
- return ea
-}
-
-// WithValue Element's value
-func (ea ElementAttribute) WithValue(s string) ElementAttribute {
- ea["value"] = s
- return ea
-}
-
-// WithVisible
-//
-// Whether element is visible
-func (ea ElementAttribute) WithVisible(b bool) ElementAttribute {
- ea["visible"] = b
- return ea
-}
-
-func (et ElementType) String() string {
- vBy := reflect.ValueOf(et)
- tBy := reflect.TypeOf(et)
- for i := 0; i < vBy.NumField(); i++ {
- if vBy.Field(i).Bool() {
- return tBy.Field(i).Tag.Get("json")
- }
- }
- return "UNKNOWN"
-}
-
-// ElementType
-// !!! This mapping should be updated if there are changes after each new XCTest release"`
-type ElementType struct {
- Any bool `json:"XCUIElementTypeAny"`
- Other bool `json:"XCUIElementTypeOther"`
- Application bool `json:"XCUIElementTypeApplication"`
- Group bool `json:"XCUIElementTypeGroup"`
- Window bool `json:"XCUIElementTypeWindow"`
- Sheet bool `json:"XCUIElementTypeSheet"`
- Drawer bool `json:"XCUIElementTypeDrawer"`
- Alert bool `json:"XCUIElementTypeAlert"`
- Dialog bool `json:"XCUIElementTypeDialog"`
- Button bool `json:"XCUIElementTypeButton"`
- RadioButton bool `json:"XCUIElementTypeRadioButton"`
- RadioGroup bool `json:"XCUIElementTypeRadioGroup"`
- CheckBox bool `json:"XCUIElementTypeCheckBox"`
- DisclosureTriangle bool `json:"XCUIElementTypeDisclosureTriangle"`
- PopUpButton bool `json:"XCUIElementTypePopUpButton"`
- ComboBox bool `json:"XCUIElementTypeComboBox"`
- MenuButton bool `json:"XCUIElementTypeMenuButton"`
- ToolbarButton bool `json:"XCUIElementTypeToolbarButton"`
- Popover bool `json:"XCUIElementTypePopover"`
- Keyboard bool `json:"XCUIElementTypeKeyboard"`
- Key bool `json:"XCUIElementTypeKey"`
- NavigationBar bool `json:"XCUIElementTypeNavigationBar"`
- TabBar bool `json:"XCUIElementTypeTabBar"`
- TabGroup bool `json:"XCUIElementTypeTabGroup"`
- Toolbar bool `json:"XCUIElementTypeToolbar"`
- StatusBar bool `json:"XCUIElementTypeStatusBar"`
- Table bool `json:"XCUIElementTypeTable"`
- TableRow bool `json:"XCUIElementTypeTableRow"`
- TableColumn bool `json:"XCUIElementTypeTableColumn"`
- Outline bool `json:"XCUIElementTypeOutline"`
- OutlineRow bool `json:"XCUIElementTypeOutlineRow"`
- Browser bool `json:"XCUIElementTypeBrowser"`
- CollectionView bool `json:"XCUIElementTypeCollectionView"`
- Slider bool `json:"XCUIElementTypeSlider"`
- PageIndicator bool `json:"XCUIElementTypePageIndicator"`
- ProgressIndicator bool `json:"XCUIElementTypeProgressIndicator"`
- ActivityIndicator bool `json:"XCUIElementTypeActivityIndicator"`
- SegmentedControl bool `json:"XCUIElementTypeSegmentedControl"`
- Picker bool `json:"XCUIElementTypePicker"`
- PickerWheel bool `json:"XCUIElementTypePickerWheel"`
- Switch bool `json:"XCUIElementTypeSwitch"`
- Toggle bool `json:"XCUIElementTypeToggle"`
- Link bool `json:"XCUIElementTypeLink"`
- Image bool `json:"XCUIElementTypeImage"`
- Icon bool `json:"XCUIElementTypeIcon"`
- SearchField bool `json:"XCUIElementTypeSearchField"`
- ScrollView bool `json:"XCUIElementTypeScrollView"`
- ScrollBar bool `json:"XCUIElementTypeScrollBar"`
- StaticText bool `json:"XCUIElementTypeStaticText"`
- TextField bool `json:"XCUIElementTypeTextField"`
- SecureTextField bool `json:"XCUIElementTypeSecureTextField"`
- DatePicker bool `json:"XCUIElementTypeDatePicker"`
- TextView bool `json:"XCUIElementTypeTextView"`
- Menu bool `json:"XCUIElementTypeMenu"`
- MenuItem bool `json:"XCUIElementTypeMenuItem"`
- MenuBar bool `json:"XCUIElementTypeMenuBar"`
- MenuBarItem bool `json:"XCUIElementTypeMenuBarItem"`
- Map bool `json:"XCUIElementTypeMap"`
- WebView bool `json:"XCUIElementTypeWebView"`
- IncrementArrow bool `json:"XCUIElementTypeIncrementArrow"`
- DecrementArrow bool `json:"XCUIElementTypeDecrementArrow"`
- Timeline bool `json:"XCUIElementTypeTimeline"`
- RatingIndicator bool `json:"XCUIElementTypeRatingIndicator"`
- ValueIndicator bool `json:"XCUIElementTypeValueIndicator"`
- SplitGroup bool `json:"XCUIElementTypeSplitGroup"`
- Splitter bool `json:"XCUIElementTypeSplitter"`
- RelevanceIndicator bool `json:"XCUIElementTypeRelevanceIndicator"`
- ColorWell bool `json:"XCUIElementTypeColorWell"`
- HelpTag bool `json:"XCUIElementTypeHelpTag"`
- Matte bool `json:"XCUIElementTypeMatte"`
- DockItem bool `json:"XCUIElementTypeDockItem"`
- Ruler bool `json:"XCUIElementTypeRuler"`
- RulerMarker bool `json:"XCUIElementTypeRulerMarker"`
- Grid bool `json:"XCUIElementTypeGrid"`
- LevelIndicator bool `json:"XCUIElementTypeLevelIndicator"`
- Cell bool `json:"XCUIElementTypeCell"`
- LayoutArea bool `json:"XCUIElementTypeLayoutArea"`
- LayoutItem bool `json:"XCUIElementTypeLayoutItem"`
- Handle bool `json:"XCUIElementTypeHandle"`
- Stepper bool `json:"XCUIElementTypeStepper"`
- Tab bool `json:"XCUIElementTypeTab"`
- TouchBar bool `json:"XCUIElementTypeTouchBar"`
- StatusItem bool `json:"XCUIElementTypeStatusItem"`
- EditText bool `json:"android.widget.EditText"`
-}
-
-// ProtectedResource A system resource that requires user authorization to access.
-type ProtectedResource int
-
-// https://developer.apple.com/documentation/xctest/xcuiprotectedresource?language=objc
-const (
- ProtectedResourceContacts ProtectedResource = 1
- ProtectedResourceCalendar ProtectedResource = 2
- ProtectedResourceReminders ProtectedResource = 3
- ProtectedResourcePhotos ProtectedResource = 4
- ProtectedResourceMicrophone ProtectedResource = 5
- ProtectedResourceCamera ProtectedResource = 6
- ProtectedResourceMediaLibrary ProtectedResource = 7
- ProtectedResourceHomeKit ProtectedResource = 8
- ProtectedResourceSystemRootDirectory ProtectedResource = 0x40000000
- ProtectedResourceUserDesktopDirectory ProtectedResource = 0x40000001
- ProtectedResourceUserDownloadsDirectory ProtectedResource = 0x40000002
- ProtectedResourceUserDocumentsDirectory ProtectedResource = 0x40000003
- ProtectedResourceBluetooth ProtectedResource = -0x40000000
- ProtectedResourceKeyboardNetwork ProtectedResource = -0x40000001
- ProtectedResourceLocation ProtectedResource = -0x40000002
- ProtectedResourceHealth ProtectedResource = -0x40000003
-)
-
type Condition func(wd WebDriver) (bool, error)
type Direction string
@@ -968,55 +621,16 @@ type WebDriver interface {
WindowSize() (Size, error)
Screen() (Screen, error)
Scale() (float64, error)
- ActiveAppInfo() (AppInfo, error)
- // ActiveAppsList Retrieves the information about the currently active apps
- ActiveAppsList() ([]AppBaseInfo, error)
- // AppState Get the state of the particular application in scope of the current session.
- // !This method is only returning reliable results since Xcode9 SDK
- AppState(bundleId string) (AppState, error)
-
- // IsLocked Checks if the screen is locked or not.
- IsLocked() (bool, error)
- // Unlock Forces the device under test to unlock.
- // An immediate return will happen if the device is already unlocked
- // and an error is going to be thrown if the screen has not been unlocked after the timeout.
- Unlock() error
- // Lock Forces the device under test to switch to the lock screen.
- // An immediate return will happen if the device is already locked
- // and an error is going to be thrown if the screen has not been locked after the timeout.
- Lock() error
// Homescreen Forces the device under test to switch to the home screen
Homescreen() error
- // AlertText Returns alert's title and description separated by new lines
- AlertText() (string, error)
- // AlertButtons Gets the labels of the buttons visible in the alert
- AlertButtons() ([]string, error)
- // AlertAccept Accepts alert, if present
- AlertAccept(label ...string) error
- // AlertDismiss Dismisses alert, if present
- AlertDismiss(label ...string) error
- // AlertSendKeys Types a text into an input inside the alert container, if it is present
- AlertSendKeys(text string) error
-
// AppLaunch Launch an application with given bundle identifier in scope of current session.
// !This method is only available since Xcode9 SDK
- AppLaunch(bundleId string, launchOpt ...AppLaunchOption) error
- // AppLaunchUnattached Launch the app with the specified bundle ID.
- AppLaunchUnattached(bundleId string) error
+ AppLaunch(bundleId string) error
// AppTerminate Terminate an application with the given bundle id.
// Either `true` if the app has been successfully terminated or `false` if it was not running
AppTerminate(bundleId string) (bool, error)
- // AppActivate Activate an application with given bundle identifier in scope of current session.
- // !This method is only available since Xcode9 SDK
- AppActivate(bundleId string) error
- // AppDeactivate Deactivates application for given time and then activate it again
- // The minimum application switch wait is 3 seconds
- AppDeactivate(second float64) error
-
- // AppAuthReset Resets the authorization status for a protected resource. Available since Xcode 11.4
- AppAuthReset(ProtectedResource) error
// StartCamera Starts a new camera for recording
StartCamera() error
@@ -1045,13 +659,6 @@ type WebDriver interface {
Swipe(fromX, fromY, toX, toY int, options ...DataOption) error
SwipeFloat(fromX, fromY, toX, toY float64, options ...DataOption) error
- ForceTouch(x, y int, pressure float64, second ...float64) error
- ForceTouchFloat(x, y, pressure float64, second ...float64) error
-
- // PerformW3CActions Perform complex touch action in scope of the current application.
- PerformW3CActions(actions *W3CActions) error
- PerformAppiumTouchActions(touchActs *TouchActions) error
-
// SetPasteboard Sets data to the general pasteboard
SetPasteboard(contentType PasteboardType, content string) error
// GetPasteboard Gets the data contained in the general pasteboard.
@@ -1066,45 +673,12 @@ type WebDriver interface {
// Input works like SendKeys
Input(text string, options ...DataOption) error
- // KeyboardDismiss Tries to dismiss the on-screen keyboard
- KeyboardDismiss(keyNames ...string) error
-
// PressButton Presses the corresponding hardware button on the device
PressButton(devBtn DeviceButton) error
// PressBack Presses the back button
PressBack(options ...DataOption) error
- // IOHIDEvent Emulated triggering of the given low-level IOHID device event.
- // duration: The event duration in float seconds (XCTest uses 0.005 for a single press event)
- IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) error
-
- // ExpectNotification Creates an expectation that is fulfilled when an expected Notification is received
- ExpectNotification(notifyName string, notifyType NotificationType, second ...int) error
-
- // SiriActivate Activates Siri service voice recognition with the given text to parse
- SiriActivate(text string) error
- // SiriOpenUrl Opens the particular url scheme using Siri voice recognition helpers.
- // !This will only work since XCode 8.3/iOS 10.3
- // It doesn't actually work, right?
- SiriOpenUrl(url string) error
-
- Orientation() (Orientation, error)
- // SetOrientation Sets requested device interface orientation.
- SetOrientation(Orientation) error
-
- Rotation() (Rotation, error)
- // SetRotation Sets the devices orientation to the rotation passed.
- SetRotation(Rotation) error
-
- // MatchTouchID Matches or mismatches TouchID request
- MatchTouchID(isMatch bool) error
-
- // ActiveElement Returns the element, which currently holds the keyboard input focus or nil if there are no such elements.
- ActiveElement() (WebElement, error)
- FindElement(by BySelector) (WebElement, error)
- FindElements(by BySelector) ([]WebElement, error)
-
Screenshot() (*bytes.Buffer, error)
// Source Return application elements tree
@@ -1122,13 +696,6 @@ type WebDriver interface {
IsHealthy() (bool, error)
- // WaitWithTimeoutAndInterval waits for the condition to evaluate to true.
- WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error
- // WaitWithTimeout works like WaitWithTimeoutAndInterval, but with default polling interval.
- WaitWithTimeout(condition Condition, timeout time.Duration) error
- // Wait works like WaitWithTimeoutAndInterval, but using the default timeout and polling interval.
- Wait(condition Condition) error
-
// Close inner connections properly
Close() error
@@ -1136,94 +703,3 @@ type WebDriver interface {
StartCaptureLog(identifier ...string) (err error)
StopCaptureLog() (result interface{}, err error)
}
-
-// WebElement defines method supported by web elements.
-type WebElement interface {
- // Click Waits for element to become stable (not move) and performs sync tap on element.
- Click() error
- // 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, options ...DataOption) error
- // Clear Clears text on element. It will try to activate keyboard on element,
- // if element has no keyboard focus.
- Clear() error
-
- // Tap Waits for element to become stable (not move) and performs sync tap on element,
- // relative to the current element position
- Tap(x, y int) error
- TapFloat(x, y float64) error
-
- // DoubleTap Sends a double tap event to a hittable point computed for the element.
- DoubleTap() error
-
- // TouchAndHold Sends a long-press gesture to a hittable point computed for the element,
- // holding for the specified duration.
- // second: The default value is 1
- TouchAndHold(second ...float64) error
- // TwoFingerTap Sends a two finger tap event to a hittable point computed for the element.
- TwoFingerTap() error
- // TapWithNumberOfTaps Sends one or more taps with one or more touch points.
- TapWithNumberOfTaps(numberOfTaps, numberOfTouches int) error
- // ForceTouch Waits for element to become stable (not move) and performs sync force touch on element.
- // second: The default value is 1
- ForceTouch(pressure float64, second ...float64) error
- // ForceTouchFloat works like ForceTouch, but relative to the current element position
- ForceTouchFloat(x, y, pressure float64, second ...float64) error
-
- // Drag Initiates a press-and-hold gesture at the coordinate, then drags to another coordinate.
- // relative to the current element position
- // pressForDuration: The default value is 1 second.
- Drag(fromX, fromY, toX, toY int, pressForDuration ...float64) error
- DragFloat(fromX, fromY, toX, toY float64, pressForDuration ...float64) error
-
- // Swipe works like Drag, but `pressForDuration` value is 0.
- // relative to the current element position
- Swipe(fromX, fromY, toX, toY int) error
- SwipeFloat(fromX, fromY, toX, toY float64) error
- // SwipeDirection Performs swipe gesture on the element.
- // velocity: swipe speed in pixels per second. Custom velocity values are only supported since Xcode SDK 11.4.
- SwipeDirection(direction Direction, velocity ...float64) error
-
- // Pinch Sends a pinching gesture with two touches.
- // scale: The scale of the pinch gesture. Use a scale between 0 and 1 to "pinch close" or zoom out
- // and a scale greater than 1 to "pinch open" or zoom in.
- // velocity: The velocity of the pinch in scale factor per second.
- Pinch(scale, velocity float64) error
- PinchToZoomOutByW3CAction(scale ...float64) error
-
- // Rotate Sends a rotation gesture with two touches.
- // rotation: The rotation of the gesture in radians.
- // velocity: The velocity of the rotation gesture in radians per second.
- Rotate(rotation float64, velocity ...float64) error
-
- // PickerWheelSelect
- // offset: The default value is 2
- PickerWheelSelect(order PickerWheelOrder, offset ...int) error
-
- ScrollElementByName(name string) error
- ScrollElementByPredicate(predicate string) error
- ScrollToVisible() error
- // ScrollDirection
- // distance: The default value is 0.5
- ScrollDirection(direction Direction, distance ...float64) error
-
- FindElement(by BySelector) (element WebElement, err error)
- FindElements(by BySelector) (elements []WebElement, err error)
- FindVisibleCells() (elements []WebElement, err error)
-
- Rect() (rect Rect, err error)
- Location() (Point, error)
- Size() (Size, error)
- Text() (text string, err error)
- Type() (elemType string, err error)
- IsEnabled() (enabled bool, err error)
- IsDisplayed() (displayed bool, err error)
- IsSelected() (selected bool, err error)
- IsAccessible() (accessible bool, err error)
- IsAccessibilityContainer() (isAccessibilityContainer bool, err error)
- GetAttribute(attr ElementAttribute) (value string, err error)
- UID() (uid string)
-
- Screenshot() (raw *bytes.Buffer, err error)
-}
diff --git a/hrp/pkg/uixt/ios_action.go b/hrp/pkg/uixt/ios_action.go
deleted file mode 100644
index 0541f827..00000000
--- a/hrp/pkg/uixt/ios_action.go
+++ /dev/null
@@ -1,373 +0,0 @@
-package uixt
-
-import (
- "strconv"
- "strings"
-)
-
-type W3CActions []map[string]interface{}
-
-func NewW3CActions(capacity ...int) *W3CActions {
- if len(capacity) == 0 || capacity[0] <= 0 {
- capacity = []int{8}
- }
- tmp := make(W3CActions, 0, capacity[0])
- return &tmp
-}
-
-func (act *W3CActions) SendKeys(text string) *W3CActions {
- keyboard := make(map[string]interface{})
- keyboard["type"] = "key"
- keyboard["id"] = "keyboard" + strconv.FormatInt(int64(len(*act)+1), 10)
-
- ss := strings.Split(text, "")
- type KeyEvent struct {
- Type string `json:"type"`
- Value string `json:"value"`
- }
- actOptKey := make([]KeyEvent, 0, len(ss)+1)
- for i := range ss {
- actOptKey = append(
- actOptKey,
- KeyEvent{Type: "keyDown", Value: ss[i]},
- KeyEvent{Type: "keyUp", Value: ss[i]},
- )
- }
- keyboard["actions"] = actOptKey
- *act = append(*act, keyboard)
- return act
-}
-
-func (act *W3CActions) _newFinger() map[string]interface{} {
- pointer := make(map[string]interface{})
- pointer["type"] = "pointer"
- pointer["id"] = "finger" + strconv.FormatInt(int64(len(*act)+1), 10)
- pointer["parameters"] = map[string]string{"pointerType": "touch"}
- return pointer
-}
-
-func (act *W3CActions) FingerAction(fingerAct *FingerAction, fActs ...*FingerAction) *W3CActions {
- fActs = append([]*FingerAction{fingerAct}, fActs...)
- for i := range fActs {
- pointer := act._newFinger()
- pointer["actions"] = *fActs[i]
- *act = append(*act, pointer)
- }
- return act
-}
-
-type FingerAction []map[string]interface{}
-
-func NewFingerAction(capacity ...int) *FingerAction {
- if len(capacity) == 0 || capacity[0] <= 0 {
- capacity = []int{8}
- }
- tmp := make(FingerAction, 0, capacity[0])
- return &tmp
-}
-
-type FingerMove map[string]interface{}
-
-func NewFingerMove() FingerMove {
- return map[string]interface{}{"type": "pointerMove"}
-}
-
-func (fm FingerMove) WithXY(x, y int) FingerMove {
- fm["x"] = x
- fm["y"] = y
- return fm
-}
-
-func (fm FingerMove) WithXYFloat(x, y float64) FingerMove {
- fm["x"] = x
- fm["y"] = y
- return fm
-}
-
-func (fm FingerMove) WithOrigin(element WebElement) FingerMove {
- fm["origin"] = element.UID()
- return fm
-}
-
-func (fm FingerMove) WithDuration(second float64) FingerMove {
- fm["duration"] = second
- return fm
-}
-
-func (fa *FingerAction) Move(fm FingerMove) *FingerAction {
- *fa = append(*fa, fm)
- return fa
-}
-
-func (fa *FingerAction) Down() *FingerAction {
- *fa = append(*fa, map[string]interface{}{"type": "pointerDown"})
- return fa
-}
-
-func (fa *FingerAction) Up() *FingerAction {
- *fa = append(*fa, map[string]interface{}{"type": "pointerUp"})
- return fa
-}
-
-func (fa *FingerAction) Pause(second ...float64) *FingerAction {
- if len(second) == 0 || second[0] < 0 {
- second = []float64{0.5}
- }
- tmp := map[string]interface{}{
- "type": "pause",
- "duration": second[0] * 1000,
- }
- *fa = append(*fa, tmp)
- return fa
-}
-
-func (act *W3CActions) Tap(x, y int, element ...WebElement) *W3CActions {
- fm := NewFingerMove().WithXY(x, y)
- if len(element) != 0 {
- fm.WithOrigin(element[0])
- }
- fingerAction := NewFingerAction().
- Move(fm).
- Down().
- Pause(0.1).
- Up()
- return act.FingerAction(fingerAction)
-}
-
-func (act *W3CActions) DoubleTap(x, y int, element ...WebElement) *W3CActions {
- fm := NewFingerMove().WithXY(x, y)
- if len(element) != 0 {
- fm.WithOrigin(element[0])
- }
- fingerAction := NewFingerAction().
- Move(fm).
- Down().
- Pause(0.1).
- Up().
- Pause(0.04).
- Down().
- Pause(0.1).
- Up()
- return act.FingerAction(fingerAction)
-}
-
-func (act *W3CActions) Press(x, y int, second float64, element ...WebElement) *W3CActions {
- fm := NewFingerMove().WithXY(x, y)
- if len(element) != 0 {
- fm.WithOrigin(element[0])
- }
- fingerAction := NewFingerAction().
- Move(fm).
- Down().
- Pause(second).
- Up()
- return act.FingerAction(fingerAction)
-}
-
-func (act *W3CActions) Swipe(fromX, fromY, toX, toY int, element ...WebElement) *W3CActions {
- fmFrom := NewFingerMove().WithXY(fromX, fromY)
- fmTo := NewFingerMove().WithXY(toX, toY)
- if len(element) != 0 {
- fmFrom.WithOrigin(element[0])
- fmTo.WithOrigin(element[0])
- }
- fingerAction := NewFingerAction().
- Move(fmFrom).
- Down().
- Pause(0.25).
- Move(fmTo).
- Pause(0.25).
- Up()
- return act.FingerAction(fingerAction)
-}
-
-func (act *W3CActions) SwipeFloat(fromX, fromY, toX, toY float64, element ...WebElement) *W3CActions {
- fmFrom := NewFingerMove().WithXYFloat(fromX, fromY)
- fmTo := NewFingerMove().WithXYFloat(toX, toY)
- if len(element) != 0 {
- fmFrom.WithOrigin(element[0])
- fmTo.WithOrigin(element[0])
- }
- fingerAction := NewFingerAction().
- Move(fmFrom).
- Down().
- Pause(0.25).
- Move(fmTo).
- Pause(0.25).
- Up()
- return act.FingerAction(fingerAction)
-}
-
-/* ---------------------------------------------------------------------------------------------------------------- */
-
-type TouchActions []map[string]interface{}
-
-func NewTouchActions(capacity ...int) *TouchActions {
- if len(capacity) == 0 || capacity[0] <= 0 {
- capacity = []int{8}
- }
- tmp := make(TouchActions, 0, capacity[0])
- return &tmp
-}
-
-func (act *TouchActions) MoveTo(opt TouchActionMoveTo) *TouchActions {
- tmp := map[string]interface{}{
- "action": "moveTo",
- "options": opt,
- }
- *act = append(*act, tmp)
- return act
-}
-
-func (act *TouchActions) Tap(opt TouchActionTap) *TouchActions {
- tmp := map[string]interface{}{
- "action": "tap",
- "options": opt,
- }
- *act = append(*act, tmp)
- return act
-}
-
-func (act *TouchActions) Press(opt TouchActionPress) *TouchActions {
- tmp := map[string]interface{}{
- "action": "press",
- "options": opt,
- }
- *act = append(*act, tmp)
- return act
-}
-
-func (act *TouchActions) LongPress(opt TouchActionLongPress) *TouchActions {
- tmp := map[string]interface{}{
- "action": "longPress",
- "options": opt,
- }
- *act = append(*act, tmp)
- return act
-}
-
-func (act *TouchActions) Wait(second ...float64) *TouchActions {
- if len(second) == 0 || second[0] < 0 {
- second = []float64{0.5}
- }
- tmp := map[string]interface{}{
- "action": "wait",
- "options": map[string]interface{}{"ms": second[0] * 1000},
- }
- *act = append(*act, tmp)
- return act
-}
-
-func (act *TouchActions) Release() *TouchActions {
- tmp := map[string]interface{}{"action": "release"}
- *act = append(*act, tmp)
- return act
-}
-
-func (act *TouchActions) Cancel() *TouchActions {
- tmp := map[string]interface{}{"action": "cancel"}
- *act = append(*act, tmp)
- return act
-}
-
-type TouchActionMoveTo map[string]interface{}
-
-func NewTouchActionMoveTo() TouchActionMoveTo {
- return make(map[string]interface{})
-}
-
-func (opt TouchActionMoveTo) WithXY(x, y int) TouchActionMoveTo {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionMoveTo) WithXYFloat(x, y float64) TouchActionMoveTo {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionMoveTo) WithElement(element WebElement) TouchActionMoveTo {
- opt["element"] = element.UID()
- return opt
-}
-
-type TouchActionTap map[string]interface{}
-
-func NewTouchActionTap() TouchActionTap {
- return make(map[string]interface{})
-}
-
-func (opt TouchActionTap) WithXY(x, y int) TouchActionTap {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionTap) WithXYFloat(x, y float64) TouchActionTap {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionTap) WithElement(element WebElement) TouchActionTap {
- opt["element"] = element.UID()
- return opt
-}
-
-func (opt TouchActionTap) WithCount(count int) TouchActionTap {
- opt["count"] = count
- return opt
-}
-
-type TouchActionPress map[string]interface{}
-
-func NewTouchActionPress() TouchActionPress {
- return make(map[string]interface{})
-}
-
-func (opt TouchActionPress) WithXY(x, y int) TouchActionPress {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionPress) WithXYFloat(x, y float64) TouchActionPress {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionPress) WithElement(element WebElement) TouchActionPress {
- opt["element"] = element.UID()
- return opt
-}
-
-func (opt TouchActionPress) WithPressure(pressure float64) TouchActionPress {
- opt["pressure"] = pressure
- return opt
-}
-
-type TouchActionLongPress map[string]interface{}
-
-func NewTouchActionLongPress() TouchActionLongPress {
- return make(map[string]interface{})
-}
-
-func (opt TouchActionLongPress) WithXY(x, y int) TouchActionLongPress {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionLongPress) WithXYFloat(x, y float64) TouchActionLongPress {
- opt["x"] = x
- opt["y"] = y
- return opt
-}
-
-func (opt TouchActionLongPress) WithElement(element WebElement) TouchActionLongPress {
- opt["element"] = element.UID()
- return opt
-}
diff --git a/hrp/pkg/uixt/ios_device.go b/hrp/pkg/uixt/ios_device.go
index 9d301b7b..da8a9a33 100644
--- a/hrp/pkg/uixt/ios_device.go
+++ b/hrp/pkg/uixt/ios_device.go
@@ -1,23 +1,16 @@
package uixt
import (
- "bytes"
"context"
- "encoding/base64"
- builtinJSON "encoding/json"
"fmt"
"io"
builtinLog "log"
- "mime"
- "mime/multipart"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
- "regexp"
"strconv"
- "strings"
"time"
"github.com/pkg/errors"
@@ -25,7 +18,6 @@ import (
"github.com/httprunner/httprunner/v4/hrp/internal/code"
"github.com/httprunner/httprunner/v4/hrp/internal/env"
- "github.com/httprunner/httprunner/v4/hrp/internal/json"
"github.com/httprunner/httprunner/v4/hrp/pkg/gidevice"
)
@@ -687,168 +679,3 @@ func (dev *IOSDevice) RunXCTest(bundleID string) (cancel context.CancelFunc, err
return cancel, nil
}
-
-func (dExt *DriverExt) ConnectMjpegStream(httpClient *http.Client) (err error) {
- if httpClient == nil {
- return errors.New(`'httpClient' can't be nil`)
- }
-
- var req *http.Request
- if req, err = http.NewRequest(http.MethodGet, "http://*", nil); err != nil {
- return err
- }
-
- var resp *http.Response
- if resp, err = httpClient.Do(req); err != nil {
- return err
- }
- // defer func() { _ = resp.Body.Close() }()
-
- var boundary string
- if _, param, err := mime.ParseMediaType(resp.Header.Get("Content-Type")); err != nil {
- return err
- } else {
- boundary = strings.Trim(param["boundary"], "-")
- }
-
- mjpegReader := multipart.NewReader(resp.Body, boundary)
-
- go func() {
- for {
- select {
- case <-dExt.doneMjpegStream:
- _ = resp.Body.Close()
- return
- default:
- var part *multipart.Part
- if part, err = mjpegReader.NextPart(); err != nil {
- dExt.frame = nil
- continue
- }
-
- raw := new(bytes.Buffer)
- if _, err = raw.ReadFrom(part); err != nil {
- dExt.frame = nil
- continue
- }
- dExt.frame = raw
- }
- }
- }()
-
- return
-}
-
-func (dExt *DriverExt) CloseMjpegStream() {
- dExt.doneMjpegStream <- true
-}
-
-type rawResponse []byte
-
-func (r rawResponse) checkErr() (err error) {
- reply := new(struct {
- Value struct {
- Err string `json:"error"`
- Message string `json:"message"`
- Traceback string `json:"traceback"` // wda
- Stacktrace string `json:"stacktrace"` // uia
- }
- })
- if err = json.Unmarshal(r, reply); err != nil {
- return err
- }
- if reply.Value.Err != "" {
- errText := reply.Value.Message
- re := regexp.MustCompile(`{.+?=(.+?)}`)
- if re.MatchString(reply.Value.Message) {
- subMatch := re.FindStringSubmatch(reply.Value.Message)
- errText = subMatch[len(subMatch)-1]
- }
- return fmt.Errorf("%s: %s", reply.Value.Err, errText)
- }
- return
-}
-
-func (r rawResponse) 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))
- }
- s = reply.Value
- return
-}
-
-func (r rawResponse) valueConvertToBool() (b bool, err error) {
- reply := new(struct{ Value bool })
- if err = json.Unmarshal(r, reply); err != nil {
- return false, err
- }
- b = reply.Value
- return
-}
-
-func (r rawResponse) valueConvertToSessionInfo() (sessionInfo SessionInfo, err error) {
- reply := new(struct{ Value struct{ SessionInfo } })
- if err = json.Unmarshal(r, reply); err != nil {
- return SessionInfo{}, err
- }
- sessionInfo = reply.Value.SessionInfo
- return
-}
-
-func (r rawResponse) valueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) {
- reply := new(struct{ Value builtinJSON.RawMessage })
- if err = json.Unmarshal(r, reply); err != nil {
- return nil, err
- }
- raw = reply.Value
- return
-}
-
-func (r rawResponse) valueDecodeAsBase64() (raw *bytes.Buffer, err error) {
- str, err := r.valueConvertToString()
- if err != nil {
- return nil, errors.Wrap(err, "failed to convert value to string")
- }
- decodeString, err := base64.StdEncoding.DecodeString(str)
- if err != nil {
- return nil, errors.Wrap(err, "failed to decode base64 string")
- }
- raw = bytes.NewBuffer(decodeString)
- return
-}
-
-var errNoSuchElement = errors.New("no such element")
-
-func (r rawResponse) valueConvertToElementID() (id string, err error) {
- reply := new(struct{ Value map[string]string })
- if err = json.Unmarshal(r, reply); err != nil {
- return "", err
- }
- if len(reply.Value) == 0 {
- return "", errNoSuchElement
- }
- if id = elementIDFromValue(reply.Value); id == "" {
- return "", fmt.Errorf("invalid element returned: %+v", reply)
- }
- return
-}
-
-func (r rawResponse) valueConvertToElementIDs() (IDs []string, err error) {
- reply := new(struct{ Value []map[string]string })
- if err = json.Unmarshal(r, reply); err != nil {
- return nil, err
- }
- if len(reply.Value) == 0 {
- return nil, errNoSuchElement
- }
- IDs = make([]string, len(reply.Value))
- for i, elem := range reply.Value {
- var id string
- if id = elementIDFromValue(elem); id == "" {
- return nil, fmt.Errorf("invalid element returned: %+v", reply)
- }
- IDs[i] = id
- }
- return
-}
diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go
index 03f3e213..fc3aede9 100644
--- a/hrp/pkg/uixt/ios_driver.go
+++ b/hrp/pkg/uixt/ios_driver.go
@@ -8,8 +8,8 @@ import (
"net"
"net/http"
"net/url"
+ "regexp"
"strings"
- "time"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
@@ -315,12 +315,9 @@ func (wd *wdaDriver) AlertSendKeys(text string) (err error) {
return
}
-func (wd *wdaDriver) AppLaunch(bundleId string, launchOpt ...AppLaunchOption) (err error) {
+func (wd *wdaDriver) AppLaunch(bundleId string) (err error) {
// [[FBRoute POST:@"/wda/apps/launch"] respondWithTarget:self action:@selector(handleSessionAppLaunch:)]
data := make(map[string]interface{})
- if len(launchOpt) != 0 {
- data = launchOpt[0]
- }
data["bundleId"] = bundleId
_, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/apps/launch")
return
@@ -363,13 +360,6 @@ func (wd *wdaDriver) AppDeactivate(second float64) (err error) {
return
}
-func (wd *wdaDriver) AppAuthReset(resource ProtectedResource) (err error) {
- // [[FBRoute POST:@"/wda/resetAppAuth"] respondWithTarget:self action:@selector(handleResetAppAuth:)]
- data := map[string]interface{}{"resource": resource}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/resetAppAuth")
- return
-}
-
func (wd *wdaDriver) Tap(x, y int, options ...DataOption) error {
return wd.TapFloat(float64(x), float64(y), options...)
}
@@ -447,37 +437,6 @@ func (wd *wdaDriver) SwipeFloat(fromX, fromY, toX, toY float64, options ...DataO
return wd.DragFloat(fromX, fromY, toX, toY, options...)
}
-func (wd *wdaDriver) ForceTouch(x, y int, pressure float64, second ...float64) error {
- return wd.ForceTouchFloat(float64(x), float64(y), pressure, second...)
-}
-
-func (wd *wdaDriver) ForceTouchFloat(x, y, pressure float64, second ...float64) error {
- if len(second) == 0 || second[0] <= 0 {
- second = []float64{1.0}
- }
- actions := NewTouchActions().
- Press(
- NewTouchActionPress().WithXYFloat(x, y).WithPressure(pressure)).
- Wait(second[0]).
- Release()
- return wd.PerformAppiumTouchActions(actions)
-}
-
-func (wd *wdaDriver) PerformW3CActions(actions *W3CActions) (err error) {
- // [[FBRoute POST:@"/actions"] respondWithTarget:self action:@selector(handlePerformW3CTouchActions:)]
- data := map[string]interface{}{"actions": actions}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/actions")
- return
-}
-
-func (wd *wdaDriver) PerformAppiumTouchActions(touchActs *TouchActions) (err error) {
- // [[FBRoute POST:@"/wda/touch/perform"] respondWithTarget:self action:@selector(handlePerformAppiumTouchActions:)]
- // [[FBRoute POST:@"/wda/touch/multi/perform"]
- data := map[string]interface{}{"actions": touchActs}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/touch/multi/perform")
- return
-}
-
func (wd *wdaDriver) SetPasteboard(contentType PasteboardType, content string) (err error) {
// [[FBRoute POST:@"/wda/setPasteboard"] respondWithTarget:self action:@selector(handleSetPasteboard:)]
data := map[string]interface{}{
@@ -516,16 +475,6 @@ func (wd *wdaDriver) Input(text string, options ...DataOption) (err error) {
return wd.SendKeys(text, options...)
}
-func (wd *wdaDriver) KeyboardDismiss(keyNames ...string) (err error) {
- // [[FBRoute POST:@"/wda/keyboard/dismiss"] respondWithTarget:self action:@selector(handleDismissKeyboardCommand:)]
- if len(keyNames) == 0 {
- keyNames = []string{"return"}
- }
- data := map[string]interface{}{"keyNames": keyNames}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/keyboard/dismiss")
- return
-}
-
// PressBack simulates a short press on the BACK button.
func (wd *wdaDriver) PressBack(options ...DataOption) (err error) {
windowSize, err := wd.WindowSize()
@@ -554,20 +503,6 @@ func (wd *wdaDriver) PressButton(devBtn DeviceButton) (err error) {
return
}
-func (wd *wdaDriver) IOHIDEvent(pageID EventPageID, usageID EventUsageID, duration ...float64) (err error) {
- // [[FBRoute POST:@"/wda/performIoHidEvent"] respondWithTarget:self action:@selector(handlePeformIOHIDEvent:)]
- if len(duration) == 0 || duration[0] <= 0 {
- duration = []float64{0.005}
- }
- data := map[string]interface{}{
- "page": pageID,
- "usage": usageID,
- "duration": duration[0],
- }
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/performIoHidEvent")
- return
-}
-
func (wd *wdaDriver) StartCamera() (err error) {
// start camera, alias for app_launch com.apple.camera
return wd.AppLaunch("com.apple.camera")
@@ -585,34 +520,6 @@ func (wd *wdaDriver) StopCamera() (err error) {
return nil
}
-func (wd *wdaDriver) ExpectNotification(notifyName string, notifyType NotificationType, second ...int) (err error) {
- // [[FBRoute POST:@"/wda/expectNotification"] respondWithTarget:self action:@selector(handleExpectNotification:)]
- if len(second) == 0 {
- second = []int{60}
- }
- data := map[string]interface{}{
- "name": notifyName,
- "type": notifyType,
- "timeout": second[0],
- }
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/expectNotification")
- return
-}
-
-func (wd *wdaDriver) SiriActivate(text string) (err error) {
- // [[FBRoute POST:@"/wda/siri/activate"] respondWithTarget:self action:@selector(handleActivateSiri:)]
- data := map[string]interface{}{"text": text}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/siri/activate")
- return
-}
-
-func (wd *wdaDriver) SiriOpenUrl(url string) (err error) {
- // [[FBRoute POST:@"/url"] respondWithTarget:self action:@selector(handleOpenURL:)]
- data := map[string]interface{}{"url": url}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/url")
- return
-}
-
func (wd *wdaDriver) Orientation() (orientation Orientation, err error) {
// [[FBRoute GET:@"/orientation"] respondWithTarget:self action:@selector(handleGetOrientation:)]
var rawResp rawResponse
@@ -654,74 +561,6 @@ func (wd *wdaDriver) SetRotation(rotation Rotation) (err error) {
return
}
-func (wd *wdaDriver) MatchTouchID(isMatch bool) (err error) {
- // [FBRoute POST:@"/wda/touch_id"]
- data := map[string]interface{}{"match": isMatch}
- _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/touch_id")
- return
-}
-
-func (wd *wdaDriver) ActiveElement() (element WebElement, err error) {
- // [[FBRoute GET:@"/element/active"] respondWithTarget:self action:@selector(handleGetActiveElement:)]
- var rawResp rawResponse
- if rawResp, err = wd.httpGET("/session", wd.sessionId, "/element/active"); err != nil {
- return nil, err
- }
- var elementID string
- if elementID, err = rawResp.valueConvertToElementID(); err != nil {
- return nil, err
- }
- element = &wdaElement{parent: wd, id: elementID}
- return
-}
-
-func (wd *wdaDriver) FindElement(by BySelector) (element WebElement, err error) {
- // [[FBRoute POST:@"/element"] respondWithTarget:self action:@selector(handleFindElement:)]
- using, value := by.getUsingAndValue()
- data := map[string]interface{}{
- "using": using,
- "value": value,
- }
- var rawResp rawResponse
- if rawResp, err = wd.httpPOST(data, "/session", wd.sessionId, "/element"); err != nil {
- return nil, err
- }
- var elementID string
- if elementID, err = rawResp.valueConvertToElementID(); err != nil {
- if errors.Is(err, errNoSuchElement) {
- return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
- }
- return nil, err
- }
- element = &wdaElement{parent: wd, id: elementID}
- return
-}
-
-func (wd *wdaDriver) FindElements(by BySelector) (elements []WebElement, err error) {
- // [[FBRoute POST:@"/elements"] respondWithTarget:self action:@selector(handleFindElements:)]
- using, value := by.getUsingAndValue()
- data := map[string]interface{}{
- "using": using,
- "value": value,
- }
- var rawResp rawResponse
- if rawResp, err = wd.httpPOST(data, "/session", wd.sessionId, "/elements"); err != nil {
- return nil, err
- }
- var elementIDs []string
- if elementIDs, err = rawResp.valueConvertToElementIDs(); err != nil {
- if errors.Is(err, errNoSuchElement) {
- return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
- }
- return nil, err
- }
- elements = make([]WebElement, len(elementIDs))
- for i := range elementIDs {
- elements[i] = &wdaElement{parent: wd, id: elementIDs[i]}
- }
- 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:)]
@@ -838,32 +677,6 @@ func (wd *wdaDriver) WdaShutdown() (err error) {
return
}
-func (wd *wdaDriver) WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error {
- startTime := time.Now()
- for {
- done, err := condition(wd)
- if err != nil {
- return err
- }
- if done {
- return nil
- }
-
- if elapsed := time.Since(startTime); elapsed > timeout {
- return fmt.Errorf("timeout after %v", elapsed)
- }
- time.Sleep(interval)
- }
-}
-
-func (wd *wdaDriver) WaitWithTimeout(condition Condition, timeout time.Duration) error {
- return wd.WaitWithTimeoutAndInterval(condition, timeout, DefaultWaitInterval)
-}
-
-func (wd *wdaDriver) Wait(condition Condition) error {
- return wd.WaitWithTimeoutAndInterval(condition, DefaultWaitTimeout, DefaultWaitInterval)
-}
-
func (wd *wdaDriver) triggerWDALog(data map[string]interface{}) (rawResp []byte, err error) {
// [[FBRoute POST:@"/gtf/automation/log"].withoutSession respondWithTarget:self action:@selector(handleAutomationLog:)]
return wd.httpPOST(data, "/gtf/automation/log")
@@ -907,3 +720,78 @@ func (wd *wdaDriver) StopCaptureLog() (result interface{}, err error) {
log.Info().Interface("value", reply.Value).Msg("get WDA log response")
return reply.Value, nil
}
+
+type rawResponse []byte
+
+func (r rawResponse) checkErr() (err error) {
+ reply := new(struct {
+ Value struct {
+ Err string `json:"error"`
+ Message string `json:"message"`
+ Traceback string `json:"traceback"` // wda
+ Stacktrace string `json:"stacktrace"` // uia
+ }
+ })
+ if err = json.Unmarshal(r, reply); err != nil {
+ return err
+ }
+ if reply.Value.Err != "" {
+ errText := reply.Value.Message
+ re := regexp.MustCompile(`{.+?=(.+?)}`)
+ if re.MatchString(reply.Value.Message) {
+ subMatch := re.FindStringSubmatch(reply.Value.Message)
+ errText = subMatch[len(subMatch)-1]
+ }
+ return fmt.Errorf("%s: %s", reply.Value.Err, errText)
+ }
+ return
+}
+
+func (r rawResponse) 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))
+ }
+ s = reply.Value
+ return
+}
+
+func (r rawResponse) valueConvertToBool() (b bool, err error) {
+ reply := new(struct{ Value bool })
+ if err = json.Unmarshal(r, reply); err != nil {
+ return false, err
+ }
+ b = reply.Value
+ return
+}
+
+func (r rawResponse) valueConvertToSessionInfo() (sessionInfo SessionInfo, err error) {
+ reply := new(struct{ Value struct{ SessionInfo } })
+ if err = json.Unmarshal(r, reply); err != nil {
+ return SessionInfo{}, err
+ }
+ sessionInfo = reply.Value.SessionInfo
+ return
+}
+
+func (r rawResponse) valueConvertToJsonRawMessage() (raw builtinJSON.RawMessage, err error) {
+ reply := new(struct{ Value builtinJSON.RawMessage })
+ if err = json.Unmarshal(r, reply); err != nil {
+ return nil, err
+ }
+ raw = reply.Value
+ return
+}
+
+func (r rawResponse) valueDecodeAsBase64() (raw *bytes.Buffer, err error) {
+ str, err := r.valueConvertToString()
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to convert value to string")
+ }
+ decodeString, err := base64.StdEncoding.DecodeString(str)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to decode base64 string")
+ }
+ raw = bytes.NewBuffer(decodeString)
+ return
+}
diff --git a/hrp/pkg/uixt/ios_element.go b/hrp/pkg/uixt/ios_element.go
deleted file mode 100644
index 02f54aac..00000000
--- a/hrp/pkg/uixt/ios_element.go
+++ /dev/null
@@ -1,478 +0,0 @@
-package uixt
-
-import (
- "bytes"
- "fmt"
- "math"
- "strings"
-
- "github.com/pkg/errors"
-
- "github.com/httprunner/httprunner/v4/hrp/internal/json"
-)
-
-// All elements returned by search endpoints have assigned element_id.
-// Given element_id you can query properties like:
-// enabled, rect, size, location, text, displayed, accessible, name
-type wdaElement struct {
- parent *wdaDriver
- id string // element_id
-}
-
-func (we wdaElement) Click() (err error) {
- // [[FBRoute POST:@"/element/:uuid/click"] respondWithTarget:self action:@selector(handleClick:)]
- _, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/element", we.id, "/click")
- return
-}
-
-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, ""),
- }
- // new data options in post data for extra uiautomator configurations
- newData := NewData(data, options...)
-
- _, err = we.parent.httpPOST(newData, "/session", we.parent.sessionId, "/element", we.id, "/value")
- return
-}
-
-func (we wdaElement) Clear() (err error) {
- // [[FBRoute POST:@"/element/:uuid/clear"] respondWithTarget:self action:@selector(handleClear:)]
- _, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/element", we.id, "/clear")
- return
-}
-
-func (we wdaElement) Tap(x, y int) error {
- return we.TapFloat(float64(x), float64(y))
-}
-
-func (we wdaElement) TapFloat(x, y float64) (err error) {
- // [[FBRoute POST:@"/wda/tap/:uuid"] respondWithTarget:self action:@selector(handleTap:)]
- data := map[string]interface{}{
- "x": x,
- "y": y,
- }
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/tap/", we.id)
- return
-}
-
-func (we wdaElement) DoubleTap() (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/doubleTap"] respondWithTarget:self action:@selector(handleDoubleTap:)]
- _, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/wda/element", we.id, "/doubleTap")
- return
-}
-
-func (we wdaElement) TouchAndHold(second ...float64) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/touchAndHold"] respondWithTarget:self action:@selector(handleTouchAndHold:)]
- data := make(map[string]interface{})
- if len(second) == 0 || second[0] <= 0 {
- second = []float64{1.0}
- }
- data["duration"] = second[0]
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/touchAndHold")
- return
-}
-
-func (we wdaElement) TwoFingerTap() (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/twoFingerTap"] respondWithTarget:self action:@selector(handleTwoFingerTap:)]
- _, err = we.parent.httpPOST(nil, "/session", we.parent.sessionId, "/wda/element", we.id, "/twoFingerTap")
- return
-}
-
-func (we wdaElement) TapWithNumberOfTaps(numberOfTaps, numberOfTouches int) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/tapWithNumberOfTaps"] respondWithTarget:self action:@selector(handleTapWithNumberOfTaps:)]
- if numberOfTouches <= 0 {
- return errors.New("'numberOfTouches' must be greater than zero")
- }
- if numberOfTouches > 5 {
- return errors.New("'numberOfTouches' cannot be greater than 5")
- }
- if numberOfTaps <= 0 {
- return errors.New("'numberOfTaps' must be greater than zero")
- }
- if numberOfTaps > 10 {
- return errors.New("'numberOfTaps' cannot be greater than 10")
- }
- data := map[string]interface{}{
- "numberOfTaps": numberOfTaps,
- "numberOfTouches": numberOfTouches,
- }
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/tapWithNumberOfTaps")
- return
-}
-
-func (we wdaElement) ForceTouch(pressure float64, second ...float64) (err error) {
- return we.ForceTouchFloat(-1, -1, pressure, second...)
-}
-
-func (we wdaElement) ForceTouchFloat(x, y, pressure float64, second ...float64) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/forceTouch"] respondWithTarget:self action:@selector(handleForceTouch:)]
- data := make(map[string]interface{})
- if x != -1 && y != -1 {
- data["x"] = x
- data["y"] = y
- }
- if len(second) == 0 || second[0] <= 0 {
- second = []float64{1.0}
- }
- data["pressure"] = pressure
- data["duration"] = second[0]
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/forceTouch")
- return
-}
-
-func (we wdaElement) Drag(fromX, fromY, toX, toY int, pressForDuration ...float64) error {
- return we.DragFloat(float64(fromX), float64(fromY), float64(toX), float64(toY), pressForDuration...)
-}
-
-func (we wdaElement) DragFloat(fromX, fromY, toX, toY float64, pressForDuration ...float64) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/dragfromtoforduration"] respondWithTarget:self action:@selector(handleDrag:)]
- data := map[string]interface{}{
- "fromX": fromX,
- "fromY": fromY,
- "toX": toX,
- "toY": toY,
- }
- if len(pressForDuration) == 0 || pressForDuration[0] < 0 {
- pressForDuration = []float64{1.0}
- }
- data["duration"] = pressForDuration[0]
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/dragfromtoforduration")
- return
-}
-
-func (we wdaElement) Swipe(fromX, fromY, toX, toY int) error {
- return we.SwipeFloat(float64(fromX), float64(fromY), float64(toX), float64(toY))
-}
-
-func (we wdaElement) SwipeFloat(fromX, fromY, toX, toY float64) error {
- return we.DragFloat(fromX, fromY, toX, toY, 0)
-}
-
-func (we wdaElement) SwipeDirection(direction Direction, velocity ...float64) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/swipe"] respondWithTarget:self action:@selector(handleSwipe:)]
- data := map[string]interface{}{"direction": direction}
- if len(velocity) != 0 && velocity[0] > 0 {
- data["velocity"] = velocity[0]
- }
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/swipe")
- return
-}
-
-func (we wdaElement) Pinch(scale, velocity float64) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/pinch"] respondWithTarget:self action:@selector(handlePinch:)]
- if scale <= 0 {
- return errors.New("'scale' must be greater than zero")
- }
- if scale == 1 {
- return errors.New("'scale' must be greater or less than 1")
- }
- if scale < 1 && velocity > 0 {
- return errors.New("'velocity' must be less than zero when 'scale' is less than 1")
- }
- if scale > 1 && velocity <= 0 {
- return errors.New("'velocity' must be greater than zero when 'scale' is greater than 1")
- }
- data := map[string]interface{}{
- "scale": scale,
- "velocity": velocity,
- }
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/pinch")
- return
-}
-
-func (we wdaElement) PinchToZoomOutByW3CAction(scale ...float64) (err error) {
- if len(scale) == 0 {
- scale = []float64{1.0}
- } else if scale[0] > 23 {
- scale[0] = 23
- }
- var size Size
- if size, err = we.Size(); err != nil {
- return err
- }
- r := scale[0] * 2 / 100.0
- offsetX, offsetY := float64(size.Width)*r, float64(size.Height)*r
-
- actions := NewW3CActions().SwipeFloat(0-offsetX, 0-offsetY, 0, 0, we).SwipeFloat(offsetX, offsetY, 0, 0, we)
- return we.parent.PerformW3CActions(actions)
-}
-
-func (we wdaElement) Rotate(rotation float64, velocity ...float64) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/rotate"] respondWithTarget:self action:@selector(handleRotate:)]
- if rotation > math.Pi*2 || rotation < math.Pi*-2 {
- return errors.New("'rotation' must not be more than 2π or less than -2π")
- }
- if len(velocity) == 0 || velocity[0] == 0 {
- velocity = []float64{rotation}
- }
- if rotation > 0 && velocity[0] < 0 || rotation < 0 && velocity[0] > 0 {
- return errors.New("'rotation' and 'velocity' must have the same sign")
- }
- data := map[string]interface{}{
- "rotation": rotation,
- "velocity": velocity[0],
- }
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/rotate")
- return
-}
-
-func (we wdaElement) PickerWheelSelect(order PickerWheelOrder, offset ...int) (err error) {
- // [[FBRoute POST:@"/wda/pickerwheel/:uuid/select"] respondWithTarget:self action:@selector(handleWheelSelect:)]
- if len(offset) == 0 {
- offset = []int{2}
- } else if offset[0] <= 0 || offset[0] > 5 {
- return fmt.Errorf("'offset' value is expected to be in range (0, 5]. '%d' was given instead", offset[0])
- }
- data := map[string]interface{}{
- "order": order,
- "offset": float64(offset[0]) * 0.1,
- }
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/pickerwheel", we.id, "/select")
- return
-}
-
-func (we wdaElement) scroll(data interface{}) (err error) {
- // [[FBRoute POST:@"/wda/element/:uuid/scroll"] respondWithTarget:self action:@selector(handleScroll:)]
- _, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/wda/element", we.id, "/scroll")
- return
-}
-
-func (we wdaElement) ScrollElementByName(name string) error {
- data := map[string]interface{}{"name": name}
- return we.scroll(data)
-}
-
-func (we wdaElement) ScrollElementByPredicate(predicate string) error {
- data := map[string]interface{}{"predicateString": predicate}
- return we.scroll(data)
-}
-
-func (we wdaElement) ScrollToVisible() error {
- data := map[string]interface{}{"toVisible": true}
- return we.scroll(data)
-}
-
-func (we wdaElement) ScrollDirection(direction Direction, distance ...float64) error {
- if len(distance) == 0 || distance[0] <= 0 {
- distance = []float64{0.5}
- }
- data := map[string]interface{}{
- "direction": direction,
- "distance": distance[0],
- }
- return we.scroll(data)
-}
-
-func (we wdaElement) FindElement(by BySelector) (element WebElement, err error) {
- // [[FBRoute POST:@"/element/:uuid/element"] respondWithTarget:self action:@selector(handleFindSubElement:)]
- using, value := by.getUsingAndValue()
- data := map[string]interface{}{
- "using": using,
- "value": value,
- }
- var rawResp rawResponse
- if rawResp, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/element", we.id, "/element"); err != nil {
- return nil, err
- }
- var elementID string
- if elementID, err = rawResp.valueConvertToElementID(); err != nil {
- if errors.Is(err, errNoSuchElement) {
- return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
- }
- return nil, err
- }
- element = &wdaElement{parent: we.parent, id: elementID}
- return
-}
-
-func (we wdaElement) FindElements(by BySelector) (elements []WebElement, err error) {
- // [[FBRoute POST:@"/element/:uuid/elements"] respondWithTarget:self action:@selector(handleFindSubElements:)]
- using, value := by.getUsingAndValue()
- data := map[string]interface{}{
- "using": using,
- "value": value,
- }
- var rawResp rawResponse
- if rawResp, err = we.parent.httpPOST(data, "/session", we.parent.sessionId, "/element", we.id, "/elements"); err != nil {
- return nil, err
- }
- var elementIDs []string
- if elementIDs, err = rawResp.valueConvertToElementIDs(); err != nil {
- if errors.Is(err, errNoSuchElement) {
- return nil, fmt.Errorf("%w: unable to find an element using '%s', value '%s'", err, using, value)
- }
- return nil, err
- }
- elements = make([]WebElement, len(elementIDs))
- for i := range elementIDs {
- elements[i] = &wdaElement{parent: we.parent, id: elementIDs[i]}
- }
- return
-}
-
-func (we wdaElement) FindVisibleCells() (elements []WebElement, err error) {
- // [[FBRoute GET:@"/wda/element/:uuid/getVisibleCells"] respondWithTarget:self action:@selector(handleFindVisibleCells:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/wda/element", we.id, "/getVisibleCells"); err != nil {
- return nil, err
- }
- var elementIDs []string
- if elementIDs, err = rawResp.valueConvertToElementIDs(); err != nil {
- if errors.Is(err, errNoSuchElement) {
- return nil, fmt.Errorf("%w: unable to find a cell element in this element", err)
- }
- return nil, err
- }
- elements = make([]WebElement, len(elementIDs))
- for i := range elementIDs {
- elements[i] = &wdaElement{parent: we.parent, id: elementIDs[i]}
- }
- return
-}
-
-func (we wdaElement) Rect() (rect Rect, err error) {
- // [[FBRoute GET:@"/element/:uuid/rect"] respondWithTarget:self action:@selector(handleGetRect:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/rect"); err != nil {
- return Rect{}, err
- }
- reply := new(struct{ Value struct{ Rect } })
- if err = json.Unmarshal(rawResp, reply); err != nil {
- return Rect{}, err
- }
- rect = reply.Value.Rect
- return
-}
-
-func (we wdaElement) Location() (Point, error) {
- rect, err := we.Rect()
- if err != nil {
- return Point{}, err
- }
- return rect.Point, nil
-}
-
-func (we wdaElement) Size() (Size, error) {
- rect, err := we.Rect()
- if err != nil {
- return Size{}, err
- }
- return rect.Size, nil
-}
-
-func (we wdaElement) Text() (text string, err error) {
- // [[FBRoute GET:@"/element/:uuid/text"] respondWithTarget:self action:@selector(handleGetText:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/text"); err != nil {
- return "", err
- }
- if text, err = rawResp.valueConvertToString(); err != nil {
- return "", err
- }
- return
-}
-
-func (we wdaElement) Type() (elemType string, err error) {
- // [[FBRoute GET:@"/element/:uuid/name"] respondWithTarget:self action:@selector(handleGetName:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/name"); err != nil {
- return "", err
- }
- if elemType, err = rawResp.valueConvertToString(); err != nil {
- return "", err
- }
- return
-}
-
-func (we wdaElement) IsEnabled() (enabled bool, err error) {
- // [[FBRoute GET:@"/element/:uuid/enabled"] respondWithTarget:self action:@selector(handleGetEnabled:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/enabled"); err != nil {
- return false, err
- }
- if enabled, err = rawResp.valueConvertToBool(); err != nil {
- return false, err
- }
- return
-}
-
-func (we wdaElement) IsDisplayed() (displayed bool, err error) {
- // [[FBRoute GET:@"/element/:uuid/displayed"] respondWithTarget:self action:@selector(handleGetDisplayed:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/displayed"); err != nil {
- return false, err
- }
- if displayed, err = rawResp.valueConvertToBool(); err != nil {
- return false, err
- }
- return
-}
-
-func (we wdaElement) IsSelected() (selected bool, err error) {
- // [[FBRoute GET:@"/element/:uuid/selected"] respondWithTarget:self action:@selector(handleGetSelected:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/selected"); err != nil {
- return false, err
- }
- if selected, err = rawResp.valueConvertToBool(); err != nil {
- return false, err
- }
- return
-}
-
-func (we wdaElement) IsAccessible() (accessible bool, err error) {
- // [[FBRoute GET:@"/wda/element/:uuid/accessible"] respondWithTarget:self action:@selector(handleGetAccessible:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/wda/element", we.id, "/accessible"); err != nil {
- return false, err
- }
- if accessible, err = rawResp.valueConvertToBool(); err != nil {
- return false, err
- }
- return
-}
-
-func (we wdaElement) IsAccessibilityContainer() (isAccessibilityContainer bool, err error) {
- // [[FBRoute GET:@"/wda/element/:uuid/accessibilityContainer"] respondWithTarget:self action:@selector(handleGetIsAccessibilityContainer:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/wda/element", we.id, "/accessibilityContainer"); err != nil {
- return false, err
- }
- if isAccessibilityContainer, err = rawResp.valueConvertToBool(); err != nil {
- return false, err
- }
- return
-}
-
-func (we wdaElement) GetAttribute(attr ElementAttribute) (value string, err error) {
- // [[FBRoute GET:@"/element/:uuid/attribute/:name"] respondWithTarget:self action:@selector(handleGetAttribute:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/attribute", attr.getAttributeName()); err != nil {
- return "", err
- }
- if value, err = rawResp.valueConvertToString(); err != nil {
- return "", err
- }
- return
-}
-
-func (we wdaElement) UID() (uid string) {
- return we.id
-}
-
-func (we wdaElement) Screenshot() (raw *bytes.Buffer, err error) {
- // W3C element screenshot
- // [[FBRoute GET:@"/element/:uuid/screenshot"] respondWithTarget:self action:@selector(handleElementScreenshot:)]
- // JSONWP element screenshot
- // [[FBRoute GET:@"/screenshot/:uuid"] respondWithTarget:self action:@selector(handleElementScreenshot:)]
- var rawResp rawResponse
- if rawResp, err = we.parent.httpGET("/session", we.parent.sessionId, "/element", we.id, "/screenshot"); err != nil {
- return nil, err
- }
- if raw, err = rawResp.valueDecodeAsBase64(); err != nil {
- return nil, err
- }
- return
-}
diff --git a/hrp/pkg/uixt/ios_test.go b/hrp/pkg/uixt/ios_test.go
index 07377317..23fff97b 100644
--- a/hrp/pkg/uixt/ios_test.go
+++ b/hrp/pkg/uixt/ios_test.go
@@ -5,7 +5,6 @@ package uixt
import (
"bytes"
"fmt"
- "math"
"testing"
"time"
)
@@ -69,23 +68,6 @@ func TestNewUSBDriver(t *testing.T) {
// t.Log(driver.IsWdaHealthy())
}
-func Test_remoteWD_NewSession(t *testing.T) {
- setup(t)
-
- // sessionInfo, err := driver.NewSession(nil)
- sessionInfo, err := driver.NewSession(
- NewCapabilities().WithAppLaunchOption(
- NewAppLaunchOption().WithBundleId(bundleId).WithArguments([]string{"-AppleLanguages", "(Russian)"}),
- ),
- )
- if err != nil {
- t.Fatal(err)
- }
- if len(sessionInfo.SessionId) == 0 {
- t.Fatal(sessionInfo)
- }
-}
-
func Test_remoteWD_ActiveSession(t *testing.T) {
setup(t)
@@ -219,128 +201,6 @@ func Test_remoteWD_Screen(t *testing.T) {
t.Log(screen)
}
-func Test_remoteWD_ActiveAppInfo(t *testing.T) {
- setup(t)
-
- appInfo, err := driver.ActiveAppInfo()
- if err != nil {
- t.Fatal(err)
- }
- if len(appInfo.BundleId) == 0 {
- t.Fatal(appInfo)
- }
- t.Log(appInfo)
-}
-
-func Test_remoteWD_ActiveAppsList(t *testing.T) {
- setup(t)
-
- appsList, err := driver.ActiveAppsList()
- if err != nil {
- t.Fatal(err)
- }
- if len(appsList) == 0 {
- t.Fatal(appsList)
- }
- t.Log(appsList)
-}
-
-func Test_remoteWD_AppState(t *testing.T) {
- setup(t)
-
- runState, err := driver.AppState(bundleId)
- if err != nil {
- t.Fatal(err)
- }
- t.Log(runState)
-}
-
-func Test_remoteWD_IsLocked(t *testing.T) {
- setup(t)
-
- locked, err := driver.IsLocked()
- if err != nil {
- t.Fatal(err)
- }
- t.Log(locked)
-}
-
-func Test_remoteWD_Unlock(t *testing.T) {
- setup(t)
-
- err := driver.Unlock()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_Lock(t *testing.T) {
- setup(t)
-
- err := driver.Lock()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_AlertText(t *testing.T) {
- setup(t)
-
- text, err := driver.AlertText()
- if err != nil {
- t.Fatal(err)
- }
- _ = text
- t.Log(text)
-}
-
-func Test_remoteWD_AlertButtons(t *testing.T) {
- setup(t)
-
- btnLabels, err := driver.AlertButtons()
- if err != nil {
- t.Fatal(err)
- }
- t.Log(btnLabels)
-}
-
-func Test_remoteWD_AlertAccept(t *testing.T) {
- // Test_remoteWD_AppAuthReset(t)
- // return
-
- setup(t)
-
- err := driver.AlertAccept()
- // err := driver.AlertAccept("")
- // err := driver.AlertAccept("好")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_AlertDismiss(t *testing.T) {
- // Test_remoteWD_AppAuthReset(t)
- // return
-
- setup(t)
-
- err := driver.AlertDismiss()
- // err := driver.AlertDismiss("")
- // err := driver.AlertDismiss("不允许")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_AlertSendKeys(t *testing.T) {
- setup(t)
-
- err := driver.AlertSendKeys("todo")
- if err != nil {
- t.Fatal(err)
- }
-}
-
func Test_remoteWD_Homescreen(t *testing.T) {
setup(t)
@@ -367,15 +227,6 @@ func Test_remoteWD_AppLaunch(t *testing.T) {
}
}
-func Test_remoteWD_AppLaunchUnattached(t *testing.T) {
- setup(t)
-
- err := driver.AppLaunchUnattached(bundleId)
- if err != nil {
- t.Fatal(err)
- }
-}
-
func Test_remoteWD_AppTerminate(t *testing.T) {
setup(t)
@@ -385,33 +236,6 @@ func Test_remoteWD_AppTerminate(t *testing.T) {
}
}
-func Test_remoteWD_AppActivate(t *testing.T) {
- setup(t)
-
- err := driver.AppActivate(bundleId)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_AppDeactivate(t *testing.T) {
- setup(t)
-
- err := driver.AppDeactivate(2)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_AppAuthReset(t *testing.T) {
- setup(t)
-
- err := driver.AppAuthReset(ProtectedResourceCamera)
- if err != nil {
- t.Fatal(err)
- }
-}
-
func Test_remoteWD_Tap(t *testing.T) {
setup(t)
@@ -450,15 +274,6 @@ func Test_remoteWD_Drag(t *testing.T) {
}
}
-func Test_remoteWD_ForceTouch(t *testing.T) {
- setup(t)
-
- err := driver.ForceTouch(256, 400, 0.8, -1)
- if err != nil {
- t.Fatal(err)
- }
-}
-
func Test_remoteWD_SetPasteboard(t *testing.T) {
setup(t)
@@ -524,147 +339,6 @@ func Test_remoteWD_PressButton(t *testing.T) {
}
}
-func Test_remoteWD_SiriActivate(t *testing.T) {
- setup(t)
-
- err := driver.SiriActivate("What's the weather like today")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_SiriOpenUrl(t *testing.T) {
- setup(t)
-
- err := driver.SiriOpenUrl("Prefs:root=Bluetooth")
- // err := driver.SiriOpenUrl("Prefs:root=WIFI![]()")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_Orientation(t *testing.T) {
- setup(t)
-
- orientation, err := driver.Orientation()
- if err != nil {
- t.Fatal(err)
- }
- if orientation == "" {
- t.Fatal(orientation)
- }
-}
-
-func Test_remoteWD_SetOrientation(t *testing.T) {
- setup(t)
-
- var err error
- err = driver.SetOrientation(OrientationLandscapeLeft)
- err = driver.SetOrientation(OrientationLandscapeRight)
- err = driver.SetOrientation(OrientationPortrait)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_Rotation(t *testing.T) {
- setup(t)
-
- rotation, err := driver.Rotation()
- if err != nil {
- t.Fatal()
- }
- t.Log(rotation)
-}
-
-func Test_remoteWD_SetRotation(t *testing.T) {
- setup(t)
-
- err := driver.SetRotation(Rotation{X: 0, Y: 0, Z: 270})
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_PerformW3CActions(t *testing.T) {
- // setup(t)
- // actions := NewW3CActions().SendKeys("App Store")
-
- element := setupElement(t, BySelector{Name: "touchableView"})
- actions := NewW3CActions().FingerAction(
- NewFingerAction().
- Move(NewFingerMove().WithXY(-15, -85).WithOrigin(element)).
- Down().
- Pause(0.25).
- Move(NewFingerMove().WithOrigin(element)).
- Pause(0.25).
- Up(),
- NewFingerAction().
- Move(NewFingerMove().WithXY(15, 85).WithOrigin(element)).
- Down().
- Pause(0.25).
- Move(NewFingerMove().WithOrigin(element)).
- Pause(0.25).
- Up(),
- )
- err := driver.PerformW3CActions(actions)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_PerformAppiumTouchActions(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- actions := NewTouchActions().
- Press(NewTouchActionPress().WithElement(element).WithXY(100, 150).WithPressure(0.2)).
- Wait(0.2).
- MoveTo(NewTouchActionMoveTo().WithXY(300, 150)).
- Wait(0.2).
- MoveTo(NewTouchActionMoveTo().WithElement(element)).
- Wait(0.2).
- MoveTo(NewTouchActionMoveTo().WithElement(element).WithXY(300, 400)).
- Release()
-
- err := driver.PerformAppiumTouchActions(actions)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_ActiveElement(t *testing.T) {
- setup(t)
-
- element, err := driver.ActiveElement()
- if err != nil {
- t.Fatal(err)
- }
- _ = element
- // t.Log(element)
-}
-
-func Test_remoteWD_FindElement(t *testing.T) {
- setup(t)
-
- element, err := driver.FindElement(BySelector{Name: "设置"})
- if err != nil {
- t.Fatal(err)
- }
- _ = element
- // t.Log(element)
-}
-
-func Test_remoteWD_FindElements(t *testing.T) {
- setup(t)
-
- elements, err := driver.FindElements(BySelector{ClassName: ElementType{Icon: true}})
- if err != nil {
- t.Fatal(err)
- }
- _ = elements
- t.Log(elements)
-}
-
func Test_remoteWD_Screenshot(t *testing.T) {
setup(t)
@@ -741,434 +415,3 @@ func Test_remoteWD_AccessibleSource(t *testing.T) {
_ = source
fmt.Println(source)
}
-
-func Test_remoteWD_Wait(t *testing.T) {
- setup(t)
-
- var element WebElement
- var err error
-
- by := BySelector{Name: "通知"}
- // driver.AppLaunch()
- exists := func(d WebDriver) (bool, error) {
- element, err = d.FindElement(by)
- if err == nil {
- return true, nil
- }
- return false, nil
- }
- _ = exists
- _ = element
-
- err = driver.AppLaunchUnattached(bundleId)
- if err != nil {
- t.Fatal(err)
- }
- // element, err = driver.FindElement(by)
- err = driver.WaitWithTimeoutAndInterval(exists, time.Second*10, time.Millisecond*10)
- if err != nil {
- t.Fatal(err)
- }
-
- // t.Log(element.Rect())
-}
-
-func Test_remoteWD_Location(t *testing.T) {
- setup(t)
-
- location, err := driver.Location()
- if err != nil {
- t.Fatal(err)
- }
- t.Log(location)
-}
-
-func Test_remoteWD_KeyboardDismiss(t *testing.T) {
- setup(t)
-
- err := driver.KeyboardDismiss()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWD_ExpectNotification(t *testing.T) {
- setup(t)
-
- // bundleId = "com.apple.shortcuts"
- // err := driver.ExpectNotification("shortcuts", NotificationTypePlain, 10)
- // if err != nil {
- // t.Fatal(err)
- // }
-}
-
-func Test_remoteWD_IOHIDEvent(t *testing.T) {
- setup(t)
-
- err := driver.IOHIDEvent(EventPageIDConsumer, EventUsageIDCsmrVolumeDown)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func setupElement(t *testing.T, by BySelector) WebElement {
- setup(t)
- element, err := driver.FindElement(by)
- if err != nil {
- t.Fatal(err)
- }
- return element
-}
-
-func Test_remoteWE_Click(t *testing.T) {
- element := setupElement(t, BySelector{LinkText: NewElementAttribute().WithLabel("设置")})
-
- err := element.Click()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_SendKeys(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{SearchField: true}})
-
- err := element.SendKeys("App Store")
- // err := element.SendKeys("App Store", 3)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_Clear(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{SearchField: true}})
-
- err := element.Clear()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_Tap(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- err := element.Tap(10, 20)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_DoubleTap(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- err := element.DoubleTap()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_TouchAndHold(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- err := element.TouchAndHold(-1)
- // err := element.TouchAndHold(5)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_TwoFingerTap(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- err := element.TwoFingerTap()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_TapWithNumberOfTaps(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- err := element.TapWithNumberOfTaps(3, 3)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_ForceTouch(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- // err := element.ForceTouch(1, -1)
- err := element.ForceTouchFloat(10, 20, 1, -1)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_Drag(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- // err := element.Drag(10, 20, 10, 300, -1)
- err := element.Swipe(10, 20, 10, 300)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_SwipeDirection(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- // err := element.SwipeDirection(DirectionUp, -1)
- err := element.SwipeDirection(DirectionDown, 120)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_Pinch(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- // zoom in
- // err := element.Pinch(2,10)
- // zoom out
- err := element.Pinch(0.9, -4.5)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_PinchToZoomOutByW3CAction(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- err := element.PinchToZoomOutByW3CAction(15)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_Rotate(t *testing.T) {
- element := setupElement(t, BySelector{Name: "touchableView"})
-
- // 90 CW
- // err := element.Rotate(math.Pi / 2)
- // 180 CCW
- err := element.Rotate(math.Pi * -2)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_PickerWheelSelect(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{PickerWheel: true}})
-
- err := element.PickerWheelSelect(PickerWheelOrderNext, 3)
- if err != nil {
- t.Fatal(err)
- }
- err = element.PickerWheelSelect(PickerWheelOrderPrevious)
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_scroll(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
-
- var err error
- // err = element.ScrollElementByName("电池")
- // err = element.ScrollElementByPredicate("type == 'XCUIElementTypeCell' AND name LIKE 'Safari*'")
- err = element.ScrollDirection(DirectionDown, 0.8)
-
- // element, err = driver.FindElement(BySelector{PartialLinkText: NewElementAttribute().WithLabel("Safari")})
- // if err != nil {
- // t.Fatal(err)
- // }
- // err = element.ScrollToVisible()
-
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_FindElement(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
-
- var err error
- element, err = element.FindElement(BySelector{PartialLinkText: NewElementAttribute().WithLabel("Safari")})
- if err != nil {
- t.Fatal(err)
- }
-
- err = element.Click()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_FindElements(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
-
- elements, err := element.FindElements(BySelector{ClassName: ElementType{Cell: true}})
- if err != nil {
- t.Fatal(err)
- }
-
- err = elements[0].Click()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_FindVisibleCells(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
-
- cells, err := element.FindVisibleCells()
- if err != nil {
- t.Fatal(err)
- }
-
- err = cells[0].Click()
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_remoteWE_Rect(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
-
- rect, err := element.Rect()
- if err != nil {
- t.Fatal(err)
- }
- location, err := element.Location()
- if err != nil {
- t.Fatal(err)
- }
- size, err := element.Size()
- if err != nil {
- t.Fatal(err)
- }
- _, _, _ = rect, location, size
- t.Log(rect, location, size)
-}
-
-func Test_remoteWE_Text(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
-
- text, err := element.Text()
- if err != nil {
- t.Fatal(err)
- }
- _ = text
- // t.Log(text)
-}
-
-func Test_remoteWE_Type(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
-
- elemType, err := element.Type()
- if err != nil {
- t.Fatal(err)
- }
- _ = elemType
- // t.Log(elemType)
-}
-
-func Test_remoteWE_IsEnabled(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
-
- enabled, err := element.IsEnabled()
- if err != nil {
- t.Fatal(err)
- }
- _ = enabled
- // t.Log(enabled)
-}
-
-func Test_remoteWE_IsDisplayed(t *testing.T) {
- element := setupElement(t, BySelector{PartialLinkText: NewElementAttribute().WithLabel("Safari")})
-
- displayed, err := element.IsDisplayed()
- if err != nil {
- t.Fatal(err)
- }
- _ = displayed
- // t.Log(displayed)
-}
-
-func Test_remoteWE_IsSelected(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
- // element := setupElement(t, BySelector{Name: "添加到主屏幕"})
- // element := setupElement(t, BySelector{Name: "仅App资源库"})
-
- selected, err := element.IsSelected()
- if err != nil {
- t.Fatal(err)
- }
- _ = selected
- // t.Log(selected)
-}
-
-func Test_remoteWE_IsAccessible(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
-
- accessible, err := element.IsAccessible()
- if err != nil {
- t.Fatal(err)
- }
- _ = accessible
- // t.Log(accessible)
-}
-
-func Test_remoteWE_IsAccessibilityContainer(t *testing.T) {
- // element := setupElement(t, BySelector{ClassName: ElementType{Switch: true}})
- element := setupElement(t, BySelector{ClassName: ElementType{Table: true}})
-
- isAccessibilityContainer, err := element.IsAccessibilityContainer()
- if err != nil {
- t.Fatal(err)
- }
- _ = isAccessibilityContainer
- // t.Log(isAccessibilityContainer)
-}
-
-func Test_remoteWE_GetAttribute(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{StaticText: true}})
-
- value, err := element.GetAttribute(NewElementAttribute().WithValue(""))
- if err != nil {
- t.Fatal(err)
- }
- _ = value
- // t.Log(value)
-}
-
-func Test_remoteWE_Screenshot(t *testing.T) {
- element := setupElement(t, BySelector{ClassName: ElementType{TextView: true}})
-
- screenshot, err := element.Screenshot()
- if err != nil {
- t.Fatal(err)
- }
- _ = screenshot
-
- // img, format, err := image.Decode(screenshot)
- // if err != nil {
- // t.Fatal(err)
- // }
- // userHomeDir, _ := os.UserHomeDir()
- // file, err := os.Create(userHomeDir + "/Desktop/e1." + format)
- // if err != nil {
- // t.Fatal(err)
- // }
- // defer func() { _ = file.Close() }()
- // switch format {
- // case "png":
- // err = png.Encode(file, img)
- // case "jpeg":
- // err = jpeg.Encode(file, img, nil)
- // }
- // if err != nil {
- // t.Fatal(err)
- // }
- // t.Log(file.Name())
-}
diff --git a/hrp/pkg/uixt/tap.go b/hrp/pkg/uixt/tap.go
index 3d4b584a..5e5cdb70 100644
--- a/hrp/pkg/uixt/tap.go
+++ b/hrp/pkg/uixt/tap.go
@@ -97,12 +97,6 @@ func (dExt *DriverExt) Tap(param string, options ...DataOption) error {
}
func (dExt *DriverExt) TapOffset(param string, xOffset, yOffset float64, options ...DataOption) (err error) {
- // click on element, find by name attribute
- ele, err := dExt.FindUIElement(param)
- if err == nil {
- return ele.Click()
- }
-
dataOptions := NewDataOptions(options...)
x, y, width, height, err := dExt.FindUIRectInUIKit(param, options...)
@@ -132,12 +126,6 @@ func (dExt *DriverExt) DoubleTap(param string) (err error) {
}
func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64) (err error) {
- // click on element, find by name attribute
- ele, err := dExt.FindUIElement(param)
- if err == nil {
- return ele.DoubleTap()
- }
-
var x, y, width, height float64
if x, y, width, height, err = dExt.FindUIRectInUIKit(param); err != nil {
return err
@@ -145,24 +133,3 @@ func (dExt *DriverExt) DoubleTapOffset(param string, xOffset, yOffset float64) (
return dExt.Driver.DoubleTapFloat(x+width*xOffset, y+height*yOffset)
}
-
-// TapWithNumber sends one or more taps
-func (dExt *DriverExt) TapWithNumber(param string, numberOfTaps int) (err error) {
- return dExt.TapWithNumberOffset(param, numberOfTaps, 0.5, 0.5)
-}
-
-func (dExt *DriverExt) TapWithNumberOffset(param string, numberOfTaps int, xOffset, yOffset float64) (err error) {
- if numberOfTaps <= 0 {
- numberOfTaps = 1
- }
- var x, y, width, height float64
- if x, y, width, height, err = dExt.FindUIRectInUIKit(param); err != nil {
- return err
- }
-
- x = x + width*xOffset
- y = y + height*yOffset
-
- touchActions := NewTouchActions().Tap(NewTouchActionTap().WithXYFloat(x, y).WithCount(numberOfTaps))
- return dExt.PerformTouchActions(touchActions)
-}
diff --git a/hrp/pkg/uixt/tap_test.go b/hrp/pkg/uixt/tap_test.go
index c5365998..f0cfd287 100644
--- a/hrp/pkg/uixt/tap_test.go
+++ b/hrp/pkg/uixt/tap_test.go
@@ -12,17 +12,14 @@ func init() {
iosDevice, _ = NewIOSDevice()
}
-func TestDriverExt_TapWithNumber(t *testing.T) {
- driverExt, err := iosDevice.NewDriver(nil)
- checkErr(t, err)
-
- pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/flag7.png"
-
- err = driverExt.TapWithNumber(pathSearch, 3)
- checkErr(t, err)
-
- err = driverExt.TapWithNumberOffset(pathSearch, 3, 0.5, 0.75)
- 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 TestDriverExt_TapXY(t *testing.T) {
diff --git a/hrp/pkg/uixt/touch.go b/hrp/pkg/uixt/touch.go
deleted file mode 100644
index fe455507..00000000
--- a/hrp/pkg/uixt/touch.go
+++ /dev/null
@@ -1,33 +0,0 @@
-package uixt
-
-func (dExt *DriverExt) ForceTouch(pathname string, pressure float64, duration ...float64) (err error) {
- return dExt.ForceTouchOffset(pathname, pressure, 0.5, 0.5, duration...)
-}
-
-func (dExt *DriverExt) ForceTouchOffset(pathname string, pressure, xOffset, yOffset float64, duration ...float64) (err error) {
- if len(duration) == 0 {
- duration = []float64{1.0}
- }
- var x, y, width, height float64
- if x, y, width, height, err = dExt.FindUIRectInUIKit(pathname); err != nil {
- return err
- }
-
- return dExt.Driver.ForceTouchFloat(x+width*xOffset, y+height*yOffset, pressure, duration[0])
-}
-
-func (dExt *DriverExt) TouchAndHold(pathname string, duration ...float64) (err error) {
- return dExt.TouchAndHoldOffset(pathname, 0.5, 0.5, duration...)
-}
-
-func (dExt *DriverExt) TouchAndHoldOffset(pathname string, xOffset, yOffset float64, duration ...float64) (err error) {
- if len(duration) == 0 {
- duration = []float64{1.0}
- }
- var x, y, width, height float64
- if x, y, width, height, err = dExt.FindUIRectInUIKit(pathname); err != nil {
- return err
- }
-
- return dExt.Driver.TouchAndHoldFloat(x+width*xOffset, y+height*yOffset, duration[0])
-}
diff --git a/hrp/pkg/uixt/touch_test.go b/hrp/pkg/uixt/touch_test.go
deleted file mode 100644
index aa5515a8..00000000
--- a/hrp/pkg/uixt/touch_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-//go:build localtest
-
-package uixt
-
-import (
- "testing"
-)
-
-func TestDriverExt_ForceTouch(t *testing.T) {
- driverExt, err := iosDevice.NewDriver(nil)
- checkErr(t, err)
-
- pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/IMG_ft.png"
-
- err = driverExt.ForceTouch(pathSearch, 0.5, 3)
- checkErr(t, err)
-
- // err = driverExt.ForceTouchOffset(pathSearch, 0.5, 0.1, 0.9)
- // checkErr(t, err)
-
- // err = driverExt.ForceTouchOffset(pathSearch, 0.2, 1.1, -1)
- // checkErr(t, err)
-}
-
-func TestDriverExt_TouchAndHold(t *testing.T) {
- driverExt, err := iosDevice.NewDriver(nil)
- checkErr(t, err)
-
- pathSearch := "/Users/hero/Documents/temp/2020-05/opencv/IMG_ft.png"
-
- // err = driverExt.TouchAndHold(pathSearch)
- // checkErr(t, err)
-
- // err = driverExt.TouchAndHold(pathSearch, 3)
- // checkErr(t, err)
-
- err = driverExt.TouchAndHoldOffset(pathSearch, 0.8, 0.1)
- checkErr(t, err)
-}
diff --git a/hrp/runner.go b/hrp/runner.go
index aa4665ea..34a50a7b 100644
--- a/hrp/runner.go
+++ b/hrp/runner.go
@@ -428,7 +428,7 @@ func (r *CaseRunner) parseConfig() error {
}
device, err := uixt.NewAndroidDevice(uixt.GetAndroidDeviceOptions(androidDeviceConfig)...)
if err != nil {
- return errors.Wrap(err, "init iOS device failed")
+ return errors.Wrap(err, "init Android device failed")
}
client, err := device.NewDriver(nil)
if err != nil {
@@ -559,7 +559,7 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error {
// check if failfast
if r.caseRunner.hrpRunner.failfast {
- return errors.Wrap(err, "abort running due to failfast setting")
+ return errors.New("abort running due to failfast setting")
}
}
diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go
index 657455c5..5d37b203 100644
--- a/hrp/step_mobile_ui.go
+++ b/hrp/step_mobile_ui.go
@@ -50,14 +50,6 @@ func (s *StepMobile) AppLaunch(bundleId string) *StepMobile {
return s
}
-func (s *StepMobile) AppLaunchUnattached(bundleId string) *StepMobile {
- s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
- Method: uixt.AppLaunchUnattached,
- Params: bundleId,
- })
- return s
-}
-
func (s *StepMobile) AppTerminate(bundleId string) *StepMobile {
s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{
Method: uixt.AppTerminate,
diff --git a/hrp/step_mobile_ui_test.go b/hrp/step_mobile_ui_test.go
index 814ad3ba..a9337da2 100644
--- a/hrp/step_mobile_ui_test.go
+++ b/hrp/step_mobile_ui_test.go
@@ -59,7 +59,7 @@ func TestIOSAppLaunch(t *testing.T) {
NewStep("终止今日头条").
IOS().AppTerminate("com.ss.iphone.article.News"),
NewStep("启动今日头条").
- IOS().AppLaunchUnattached("com.ss.iphone.article.News"),
+ IOS().AppLaunch("com.ss.iphone.article.News"),
},
}
err := NewRunner(t).Run(testCase)
diff --git a/hrp/step_request.go b/hrp/step_request.go
index d56536bd..12a8ffbb 100644
--- a/hrp/step_request.go
+++ b/hrp/step_request.go
@@ -317,7 +317,7 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err
config := r.caseRunner.parsedConfig
rb := newRequestBuilder(parser, config, step.Request)
- rb.req.Method = string(step.Request.Method)
+ rb.req.Method = strings.ToUpper(string(step.Request.Method))
err = rb.prepareUrlParams(stepVariables)
if err != nil {