Skip to content
This repository has been archived by the owner on Jan 13, 2023. It is now read-only.

Commit

Permalink
support appium
Browse files Browse the repository at this point in the history
  • Loading branch information
codeskyblue committed Jun 2, 2018
1 parent f94790f commit 4fd7904
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 75 deletions.
45 changes: 44 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ Make [WebDriverAgent](https://github.com/facebook/WebDriverAgent) more powerful.
Limited in Mac

# Features
- [x] Launch iproxy when start
- [x] Launch iproxy when start. Listen on `0.0.0.0` instead of `localhost`
- [x] Create http proxy for WDA server
- [x] add udid into `GET /status`
- [x] forward all url starts with `/origin/<url>` to `/<url>`
- [x] Add the missing Index page
- [x] Support Package management API
- [x] Support launch WDA
- [x] iOS device remote control
- [x] Support Appium Desktop (Beta)

# Installl
```
Expand All @@ -36,6 +37,48 @@ $ wdaproxy -W ../WebDriverAgent

For more run `wdaproxy -h`

Strong recommended use [python facebook-wda](https://github.com/openatx/facebook-wda) to write tests.
But if you have to use appium. Just keep reading.

## Simulate appium server
Since WDA implements WebDriver protocol.
Even through many API not implemented. But it still works. `wdaproxy` implemented a few api listed bellow.

- wdaproxy "/wd/hubs/sessions"
- wdaproxy "/wd/hubs/session/$sessionId/window/current/size"

Launch wdaproxy with command `wdaproxy -p 8100 -u $UDID`

Here is sample `Python-Appium-Client` code.

```python
from appium import webdriver
import time

driver = webdriver.Remote("http://127.0.0.1:8100/wd/hub", {"bundleId": "com.apple.Preferences"})

def wait_element(xpath, timeout=10):
print("Wait ELEMENT", xpath)
deadline = time.time() + timeout
while time.time() <= deadline:
el = driver.find_element_by_xpath(xpath)
if el:
return el
time.sleep(.2)
raise RuntimeError("Element for " + xpath + " not found")

wait_element('//XCUIElementTypeCell[@name="蓝牙"]').click()
```

Not working well code

```python
driver.background_app(3)
driver.implicitly_wait(30)
driver.get_window_rect()
# many a lot.
```

# Extended API
Package install

Expand Down
2 changes: 1 addition & 1 deletion goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ brew:
name: homebrew-tap
folder: Formula
dependencies:
- libimobiledevice
- usbmuxd
23 changes: 12 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var (
yosemiteGroup string
debug bool

rt = mux.NewRouter()
rt = mux.NewRouter()
udidNames = map[string]string{}
)

Expand Down Expand Up @@ -98,12 +98,12 @@ func main() {
flag.StringVarP(&pWda, "wda", "W", "", "WebDriverAgent project directory [optional]")
flag.BoolVarP(&debug, "debug", "d", false, "Open debug mode")

flag.StringVarP(&yosemiteServer, "yosemite-server", "S",
os.Getenv("YOSEMITE_SERVER"),
"server center(not open source yet")
flag.StringVarP(&yosemiteGroup, "yosemite-group", "G",
"everyone",
"server center group")
// flag.StringVarP(&yosemiteServer, "yosemite-server", "S",
// os.Getenv("YOSEMITE_SERVER"),
// "server center(not open source yet")
// flag.StringVarP(&yosemiteGroup, "yosemite-group", "G",
// "everyone",
// "server center group")
flag.Parse()
if udid == "" {
udid = getUdid()
Expand All @@ -119,9 +119,9 @@ func main() {
log.Fatal(err)
}

if yosemiteServer != "" {
mockIOSProvider()
}
// if yosemiteServer != "" {
// mockIOSProvider()
// }

errC := make(chan error)
freePort, err := freeport.Get()
Expand All @@ -133,6 +133,7 @@ func main() {
go func() {
log.Printf("launch tcp-proxy, listen on %d", lisPort)
targetURL, _ := url.Parse("http://127.0.0.1:" + strconv.Itoa(freePort))
rt.HandleFunc("/wd/hub/{path:.*}", NewAppiumProxyHandlerFunc(targetURL))
rt.HandleFunc("/{path:.*}", NewReverseProxyHandlerFunc(targetURL))
errC <- http.Serve(lis, accesslog.NewLoggingHandler(rt, HTTPLogger{}))
}()
Expand All @@ -159,7 +160,7 @@ func main() {
"-verbose",
"-project", "WebDriverAgent.xcodeproj",
"-scheme", "WebDriverAgentRunner",
"-destination", "id="+udid, "test")
"-destination", "id="+udid, "test-without-building") // test-without-building
c.Dir, _ = filepath.Abs(pWda)
// Test Suite 'All tests' started at 2017-02-27 15:55:35.263
// Test Suite 'WebDriverAgentRunner.xctest' started at 2017-02-27 15:55:35.266
Expand Down
154 changes: 94 additions & 60 deletions revproxy.go
Original file line number Diff line number Diff line change
@@ -1,60 +1,94 @@
package main

import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"
// "fmt"
)

type transport struct {
http.RoundTripper
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
// rewrite url
if strings.HasPrefix(req.RequestURI, "/origin/") {
req.URL.Path = req.RequestURI[len("/origin"):]
return t.RoundTripper.RoundTrip(req)
}

// request
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}

// rewrite body
if req.RequestURI == "/status" {
jsonResp := &statusResp{}
err = json.NewDecoder(resp.Body).Decode(jsonResp)
if err != nil {
return nil, err
}
resp.Body.Close()
jsonResp.Value["device"] = map[string]interface{}{
"udid": udid,
"name": udidNames[udid],
}
data, _ := json.Marshal(jsonResp)
// update body and fix length
resp.Body = ioutil.NopCloser(bytes.NewReader(data))
resp.ContentLength = int64(len(data))
resp.Header.Set("Content-Length", strconv.Itoa(len(data)))
return resp, nil
}
return resp, nil
}

func NewReverseProxyHandlerFunc(targetURL *url.URL) http.HandlerFunc {
httpProxy := httputil.NewSingleHostReverseProxy(targetURL)
httpProxy.Transport = &transport{http.DefaultTransport}
return func(rw http.ResponseWriter, r *http.Request) {
httpProxy.ServeHTTP(rw, r)
}
}
package main

import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"strings"

"github.com/gorilla/mux"
)

type transport struct {
http.RoundTripper
}

func (t *transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
// rewrite url
if strings.HasPrefix(req.RequestURI, "/origin/") {
req.URL.Path = req.RequestURI[len("/origin"):]
return t.RoundTripper.RoundTrip(req)
}

// request
resp, err = t.RoundTripper.RoundTrip(req)
if err != nil {
return nil, err
}

// rewrite body
if req.URL.Path == "/status" {
jsonResp := &statusResp{}
err = json.NewDecoder(resp.Body).Decode(jsonResp)
if err != nil {
return nil, err
}
resp.Body.Close()
jsonResp.Value["device"] = map[string]interface{}{
"udid": udid,
"name": udidNames[udid],
}
data, _ := json.Marshal(jsonResp)
// update body and fix length
resp.Body = ioutil.NopCloser(bytes.NewReader(data))
resp.ContentLength = int64(len(data))
resp.Header.Set("Content-Length", strconv.Itoa(len(data)))
return resp, nil
}
return resp, nil
}

func NewReverseProxyHandlerFunc(targetURL *url.URL) http.HandlerFunc {
httpProxy := httputil.NewSingleHostReverseProxy(targetURL)
httpProxy.Transport = &transport{http.DefaultTransport}
return func(rw http.ResponseWriter, r *http.Request) {
httpProxy.ServeHTTP(rw, r)
}
}

type fakeProxy struct {
}

func (p *fakeProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Println("FAKE", r.RequestURI)
log.Println("P", r.URL.Path)
io.WriteString(w, "Fake")
}

func NewAppiumProxyHandlerFunc(targetURL *url.URL) http.HandlerFunc {
httpProxy := httputil.NewSingleHostReverseProxy(targetURL)
rt := mux.NewRouter()
rt.HandleFunc("/wd/hub/sessions", func(w http.ResponseWriter, r *http.Request) {
data, _ := json.MarshalIndent(map[string]interface{}{
"status": 0,
"value": []string{},
"sessionId": nil,
}, "", " ")
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Length", strconv.Itoa(len(data)))
w.Write(data)
})
rt.HandleFunc("/wd/hub/session/{sessionId}/window/current/size", func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "/current/size", "/size", -1)
r.URL.Path = r.URL.Path[len("/wd/hub"):]
httpProxy.ServeHTTP(w, r)
})
rt.Handle("/wd/hub/{subpath:.*}", http.StripPrefix("/wd/hub", httpProxy))
return rt.ServeHTTP
}
4 changes: 2 additions & 2 deletions web/assets/packages.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<h1>Packages</h1>
<div id="app">
<li v-for="v in pkgs">
{{v.name}}
{{v.name}} <input v-model="v.bundleId">
<button :disabled="v.busy" v-on:click="uninstall(v)">
Uninstall
<span v-if="v.busy">ing...</span>
Expand Down Expand Up @@ -79,4 +79,4 @@ <h1>Packages</h1>
})
</script>

</html>
</html>

0 comments on commit 4fd7904

Please sign in to comment.