25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

selenium_testdriver.py 8.1 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals, print_function
  4. from selenium import webdriver
  5. from selenium.webdriver.common.by import By
  6. from selenium.webdriver.support.ui import WebDriverWait
  7. #from selenium.webdriver.support.select import Select
  8. from selenium.webdriver.support import expected_conditions as EC
  9. #from selenium.common.exceptions import TimeoutException
  10. from selenium.webdriver.chrome.options import Options
  11. from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
  12. import time
  13. import signal
  14. import os, sys
  15. import frappe
  16. from ast import literal_eval
  17. class TestDriver(object):
  18. def __init__(self, port=None):
  19. self.port = port or frappe.get_site_config().webserver_port or '8000'
  20. chrome_options = Options()
  21. capabilities = DesiredCapabilities.CHROME
  22. if os.environ.get('CI'):
  23. self.host = 'localhost'
  24. else:
  25. self.host = frappe.local.site
  26. # enable browser logging
  27. capabilities['loggingPrefs'] = {'browser':'ALL'}
  28. chrome_options.add_argument('--no-sandbox')
  29. chrome_options.add_argument('--start-maximized')
  30. self.driver = webdriver.Chrome(chrome_options=chrome_options,
  31. desired_capabilities=capabilities, port=9515)
  32. self.driver.set_window_size(1080,800)
  33. self.cur_route = None
  34. self.logged_in = False
  35. @property
  36. def localhost(self):
  37. return "http://{host}:{port}".format(host=self.host, port=self.port)
  38. def get(self, url):
  39. return self.driver.get(os.path.join(self.localhost, url))
  40. def start(self):
  41. def signal_handler(signal, frame):
  42. self.close()
  43. sys.exit(0)
  44. signal.signal(signal.SIGINT, signal_handler)
  45. def refresh(self):
  46. self.driver.refresh()
  47. def close(self):
  48. if self.driver:
  49. self.driver.quit()
  50. self.driver = None
  51. def login(self, wait_for_id="#page-desktop"):
  52. if self.logged_in:
  53. return
  54. self.get('login')
  55. self.wait_for("#login_email")
  56. self.set_input("#login_email", "Administrator")
  57. self.set_input("#login_password", "admin")
  58. self.click('.btn-login')
  59. self.wait_for(wait_for_id)
  60. self.logged_in = True
  61. def set_input(self, selector, text, key=None, xpath=None):
  62. elem = self.find(selector, xpath=xpath)[0]
  63. elem.clear()
  64. elem.send_keys(text)
  65. if key:
  66. time.sleep(0.5)
  67. elem.send_keys(key)
  68. time.sleep(0.2)
  69. def set_field(self, fieldname, text):
  70. elem = self.wait_for(xpath='//input[@data-fieldname="{0}"]'.format(fieldname))
  71. time.sleep(0.2)
  72. elem.send_keys(text)
  73. def set_select(self, fieldname, text):
  74. elem = self.wait_for(xpath='//select[@data-fieldname="{0}"]'.format(fieldname))
  75. time.sleep(0.2)
  76. elem.send_keys(text)
  77. def set_text_editor(self, fieldname, text):
  78. elem = self.wait_for(xpath='//div[@data-fieldname="{0}"]//div[@contenteditable="true"]'.format(fieldname))
  79. time.sleep(0.2)
  80. elem.send_keys(text)
  81. def find(self, selector=None, everywhere=False, xpath=None):
  82. if xpath:
  83. return self.driver.find_elements_by_xpath(xpath)
  84. else:
  85. if self.cur_route and not everywhere:
  86. selector = self.cur_route + " " + selector
  87. return self.driver.find_elements_by_css_selector(selector)
  88. def wait_for(self, selector=None, everywhere=False, timeout=20, xpath=None, for_invisible=False):
  89. if self.cur_route and not everywhere:
  90. selector = self.cur_route + " " + selector
  91. time.sleep(0.5)
  92. if selector:
  93. _by = By.CSS_SELECTOR
  94. if xpath:
  95. _by = By.XPATH
  96. selector = xpath
  97. try:
  98. if not for_invisible:
  99. elem = self.get_wait(timeout).until(
  100. EC.presence_of_element_located((_by, selector)))
  101. else:
  102. elem = self.get_wait(timeout).until(
  103. EC.invisibility_of_element_located((_by, selector)))
  104. return elem
  105. except Exception as e:
  106. # body = self.driver.find_element_by_id('body_div')
  107. # print(body.get_attribute('innerHTML'))
  108. self.print_console()
  109. raise e
  110. def wait_for_invisible(self, selector=None, everywhere=False, timeout=20, xpath=None):
  111. self.wait_for(selector, everywhere, timeout, xpath, True)
  112. def get_console(self):
  113. out = []
  114. for entry in self.driver.get_log('browser'):
  115. source, line_no, message = entry.get('message').split(' ', 2)
  116. if message and message[0] in ('"', "'"):
  117. # message is a quoted/escaped string
  118. message = literal_eval(message)
  119. out.append(source + ' ' + line_no)
  120. out.append(message)
  121. out.append('-'*40)
  122. return out
  123. def print_console(self):
  124. for line in self.get_console():
  125. print(line)
  126. def get_wait(self, timeout=20):
  127. return WebDriverWait(self.driver, timeout)
  128. def scroll_to(self, selector):
  129. self.execute_script("frappe.ui.scroll('{0}')".format(selector))
  130. def set_route(self, *args):
  131. self.execute_script('frappe.set_route({0})'\
  132. .format(', '.join(['"{0}"'.format(r) for r in args])))
  133. self.wait_for(xpath='//div[@data-page-route="{0}"]'.format('/'.join(args)), timeout=4)
  134. def click(self, css_selector, xpath=None):
  135. element = self.wait_till_clickable(css_selector, xpath)
  136. self.scroll_to(css_selector)
  137. time.sleep(0.5)
  138. element.click()
  139. return element
  140. def click_primary_action(self):
  141. selector = ".page-actions .primary-action"
  142. #self.scroll_to(selector)
  143. self.wait_till_clickable(selector).click()
  144. self.wait_for_ajax()
  145. def click_secondary_action(self):
  146. selector = ".page-actions .btn-secondary"
  147. #self.scroll_to(selector)
  148. self.wait_till_clickable(selector).click()
  149. self.wait_for_ajax()
  150. def click_modal_primary_action(self):
  151. self.get_visible_modal().find_element_by_css_selector('.btn-primary').click()
  152. def get_visible_modal(self):
  153. return self.get_visible_element('.modal-content')
  154. def get_visible_element(self, selector=None, xpath=None):
  155. for elem in self.find(selector=selector, xpath=xpath):
  156. if elem.is_displayed():
  157. return elem
  158. def wait_till_clickable(self, selector=None, xpath=None):
  159. if self.cur_route:
  160. selector = self.cur_route + " " + selector
  161. by = By.CSS_SELECTOR
  162. if xpath:
  163. by = By.XPATH
  164. selector = xpath
  165. return self.get_wait().until(EC.element_to_be_clickable(
  166. (by, selector)))
  167. def execute_script(self, js):
  168. self.driver.execute_script(js)
  169. def wait_for_ajax(self, freeze = False):
  170. self.wait_for('body[data-ajax-state="complete"]', True)
  171. if freeze:
  172. self.wait_for_invisible(".freeze-message-container")
  173. # def go_to_module(module_name, item=None):
  174. # global cur_route
  175. #
  176. # # desktop
  177. # find(".navbar-home", True)[0].click()
  178. # cur_route = None
  179. # wait("#page-desktop")
  180. #
  181. # page = "Module/" + module_name
  182. # m = find('#page-desktop [data-link="{0}"] .app-icon'.format(page))
  183. # if not m:
  184. # page = "List/" + module_name
  185. # m = find('#page-desktop [data-link="{0}"] .app-icon'.format(page))
  186. # if not m:
  187. # raise Exception("Module {0} not found".format(module_name))
  188. #
  189. # m[0].click()
  190. # wait_for_page(page)
  191. #
  192. # if item:
  193. # elem = find('[data-label="{0}"]'.format(item))[0]
  194. # elem.click()
  195. # page = elem.get_attribute("data-route")
  196. # wait_for_page(page)
  197. #
  198. # def new_doc(module, doctype):
  199. # go_to_module(module, doctype)
  200. # primary_action()
  201. # wait_for_page("Form/" + doctype)
  202. #
  203. # def add_child(fieldname):
  204. # find('[data-fieldname="{0}"] .grid-add-row'.format(fieldname))[0].click()
  205. # wait('[data-fieldname="{0}"] .form-grid'.format(fieldname))
  206. #
  207. # def done_add_child(fieldname):
  208. # selector = '[data-fieldname="{0}"] .grid-row-open .btn-success'.format(fieldname)
  209. # scroll_to(selector)
  210. # wait_till_clickable(selector).click()
  211. #
  212. # def set_field(fieldname, value, fieldtype="input"):
  213. # _driver.switch_to.window(_driver.current_window_handle)
  214. # selector = '{0}[data-fieldname="{1}"]'.format(fieldtype, fieldname)
  215. # set_input(selector, value, key=Keys.TAB)
  216. # wait_for_ajax()
  217. #
  218. # def set_select(fieldname, value):
  219. # select = Select(find('select[data-fieldname="{0}"]'.format(fieldname))[0])
  220. # select.select_by_value(value)
  221. # wait_for_ajax()
  222. #
  223. #
  224. # def wait_for_page(name):
  225. # global cur_route
  226. # cur_route = None
  227. # route = '[data-page-route="{0}"]'.format(name)
  228. # wait_for_ajax()
  229. # elem = wait(route)
  230. # wait_for_ajax()
  231. # cur_route = route
  232. # return elem
  233. #
  234. #
  235. # def wait_till_visible(selector):
  236. # if cur_route:
  237. # selector = cur_route + " " + selector
  238. # return get_wait().until(EC.visibility_of_element_located((By.CSS_SELECTOR, selector)))
  239. #
  240. #
  241. # def wait_for_state(state):
  242. # return wait(cur_route + '[data-state="{0}"]'.format(state), True)
  243. #
  244. #