webview.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. # coding: utf-8
  2. #
  3. # Not implemented yet.
  4. #
  5. import json
  6. import logging
  7. import string
  8. from pprint import pprint
  9. import adbutils
  10. import pychrome
  11. import requests
  12. logger = logging.getLogger(__name__)
  13. class WebviewDriver():
  14. def __init__(self, url):
  15. self._url = url
  16. self._browser = pychrome.Browser(self._url)
  17. @property
  18. def browser(self):
  19. """ new Browser all the time to clear history data """
  20. return self._browser
  21. def get_active_tab_list(self):
  22. tabs = []
  23. for tab in self.browser.list_tab():
  24. logger.debug("tab: %s", tab)
  25. tab.start()
  26. t = BrowserTab(tab)
  27. if t.is_activate():
  28. tabs.append(t)
  29. else:
  30. tab.stop()
  31. return tabs
  32. def get_activate_tab(self):
  33. pass
  34. class BrowserTab():
  35. def __init__(self, tab):
  36. self._tab = tab
  37. # I donot know why should call, Runtime.enable() ..., as I know, chromedriver call that.
  38. # self._call("Runtime.enable")
  39. # self._call("Page.enable")
  40. self._evaluate("_C = {}")
  41. def is_activate(self):
  42. """ is page activate """
  43. height = self._evaluate("window.innerHeight")
  44. hidden = self._evaluate("document.hidden")
  45. return not hidden and height > 0
  46. def close(self):
  47. self._tab.stop()
  48. def _evaluate(self, expression, **kwargs):
  49. if kwargs:
  50. d = {}
  51. for k, v in kwargs.items():
  52. d[k] = json.dumps(v)
  53. t = string.Template(expression)
  54. expression = t.substitute(d)
  55. return self._call("Runtime.evaluate", expression=expression)
  56. def _call(self, method, **kwargs):
  57. logger.debug("call: %s, kwargs: %s", method, kwargs)
  58. response = self._tab.call_method(method, **kwargs)
  59. logger.debug("response: %s", response)
  60. return response.get('result', {}).get('value')
  61. def current_url(self):
  62. return self._evaluate("window.location.href")
  63. def set_current_url(self, url: str):
  64. return self._evaluate("""(function(url) {
  65. window.location.href = ${url}
  66. })""", url=url)
  67. def find_element_by_xpath(self, xpath: str):
  68. self._evaluate('''(function(xpath){
  69. var obj = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
  70. var button = obj.iterateNext();
  71. _C[1] = button;
  72. })($xpath)
  73. ''')
  74. def coord_by_xpath(self, xpath: str):
  75. coord = self._evaluate('''(function(xpath){
  76. var obj = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
  77. var button = obj.iterateNext();
  78. var rect = button.getBoundingClientRect()
  79. // [rect.left, rect.top, rect.right, rect.bottom]
  80. var x = (rect.left + rect.right)/2
  81. var y = (rect.top + rect.bottom)/2;
  82. return JSON.stringify([x, y])
  83. })(${xpath})''', xpath=xpath)
  84. return json.loads(coord)
  85. def click(self, x, y, duration=0.2, tap_count=1):
  86. mills = int(1000*duration) # convert to ms
  87. self._call("Input.synthesizeTapGesture", x=x, y=y, duration=mills, tapCount=tap_count)
  88. def click_by_xpath(self, xpath):
  89. x, y = self.coord_by_xpath(xpath)
  90. self.click(x, y)
  91. def clear_text_by_xpath(self, xpath):
  92. self._evaluate("""(function(xpath){
  93. var obj = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
  94. var button = obj.iterateNext();
  95. button.value = ""
  96. })($xpath)""", xpath=xpath)
  97. def send_keys(self, text):
  98. """
  99. Input text
  100. Refs:
  101. https://github.com/Tencent/FAutoTest/blob/58766fcb98d135ebb6be88893d10c789a1a50e18/fastAutoTest/core/h5/h5PageOperator.py#L40
  102. http://compatibility.remotedebug.org/Input/Chrome%20(CDP%201.2)/commands/dispatchKeyEvent
  103. """
  104. for c in text:
  105. self._call("Input.dispatchKeyEvent", type="char", text=c)
  106. def screenshot(self):
  107. """ always stuck """
  108. raise NotImplementedError()
  109. from contextlib import contextmanager
  110. from selenium import webdriver
  111. @contextmanager
  112. def driver(package_name):
  113. serial = adbutils.adb.device().serial
  114. capabilities = {
  115. "androidDeviceSerial": serial,
  116. "androidPackage": package_name,
  117. "androidUseRunningApp": True,
  118. }
  119. dr = webdriver.Remote("http://localhost:9515", {
  120. "chromeOptions": capabilities
  121. })
  122. try:
  123. yield dr
  124. finally:
  125. dr.quit()
  126. def chromedriver():
  127. package_name = "io.appium.android.apis"
  128. package_name = "com.xueqiu.android"
  129. with driver(package_name) as dr:
  130. print(dr.current_url)
  131. elem = dr.find_element_by_xpath('//*[@id="phone-number"]')
  132. elem.click()
  133. elem.send_keys("123456")
  134. #dr.save_screenshot("s.png"
  135. def test_self_driver():
  136. d = adbutils.adb.device()
  137. package_name = "com.xueqiu.android"
  138. # package_name = "io.appium.android.apis"
  139. d.forward("tcp:7912", "tcp:7912")
  140. ret = requests.get(f"http://localhost:7912/proc/{package_name}/webview").json()
  141. for data in ret:
  142. pprint(data)
  143. lport = d.forward_port("localabstract:"+data["socketPath"])
  144. wd = WebviewDriver(f"http://localhost:{lport}")
  145. tabs = wd.get_active_tab_list()
  146. pprint(tabs)
  147. for tab in tabs:
  148. print(tab.current_url())
  149. tab.click_by_xpath('//*[@id="phone-number"]')
  150. tab.clear_text_by_xpath('//*[@id="phone-number"]')
  151. tab.send_keys("123456789")
  152. break
  153. def runtest():
  154. import uiautomator2 as u2
  155. d = u2.connect_usb()
  156. pprint(d.request_agent("/webviews").json())
  157. port = d.adb_device.forward_port("localabstract:chrome_devtools_remote")
  158. wd = WebviewDriver(f"http://localhost:{port}")
  159. tabs = wd.get_active_tab_list()
  160. pprint(tabs)
  161. def main():
  162. import argparse
  163. parser = argparse.ArgumentParser()
  164. parser.add_argument("-t", "--test", action="store_true", help="run test_self_driver")
  165. args = parser.parse_args()
  166. # WebviewDriver()
  167. import uiautomator2 as u2
  168. d = u2.connect_usb()
  169. assert d.adb_device, "must connect with usb"
  170. for socket_path in d.request_agent("/webviews").json():
  171. port = d.adb_device.forward_port("localabstract:"+socket_path)
  172. data = requests.get(f"http://localhost:{port}/json/version").json()
  173. import pprint
  174. pprint.pprint(data)
  175. if __name__ == "__main__":
  176. main()
  177. # if args.test:
  178. # print("---- test ----")
  179. # test_self_driver()
  180. # else:
  181. # chromedriver()