You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

285 lines
7.9 KiB

  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='8000'):
  19. self.port = port
  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.find(xpath='//input[@data-fieldname="{0}"]'.format(fieldname))
  71. elem[0].send_keys(text)
  72. def set_select(self, fieldname, text):
  73. elem = self.find(xpath='//select[@data-fieldname="{0}"]'.format(fieldname))
  74. elem[0].send_keys(text)
  75. def set_text_editor(self, fieldname, text):
  76. elem = self.find(xpath='//div[@data-fieldname="{0}"]//div[@contenteditable="true"]'.format(fieldname))
  77. elem[0].send_keys(text)
  78. def find(self, selector=None, everywhere=False, xpath=None):
  79. if xpath:
  80. return self.driver.find_elements_by_xpath(xpath)
  81. else:
  82. if self.cur_route and not everywhere:
  83. selector = self.cur_route + " " + selector
  84. return self.driver.find_elements_by_css_selector(selector)
  85. def wait_for(self, selector=None, everywhere=False, timeout=20, xpath=None, for_invisible=False):
  86. if self.cur_route and not everywhere:
  87. selector = self.cur_route + " " + selector
  88. time.sleep(0.5)
  89. if selector:
  90. _by = By.CSS_SELECTOR
  91. if xpath:
  92. _by = By.XPATH
  93. selector = xpath
  94. try:
  95. if not for_invisible:
  96. elem = self.get_wait(timeout).until(
  97. EC.presence_of_element_located((_by, selector)))
  98. else:
  99. elem = self.get_wait(timeout).until(
  100. EC.invisibility_of_element_located((_by, selector)))
  101. return elem
  102. except Exception as e:
  103. # body = self.driver.find_element_by_id('body_div')
  104. # print(body.get_attribute('innerHTML'))
  105. self.print_console()
  106. raise e
  107. def wait_for_invisible(self, selector=None, everywhere=False, timeout=20, xpath=None):
  108. self.wait_for(selector, everywhere, timeout, xpath, True)
  109. def get_console(self):
  110. out = []
  111. for entry in self.driver.get_log('browser'):
  112. source, line_no, message = entry.get('message').split(' ', 2)
  113. if message[0] in ('"', "'"):
  114. # message is a quoted/escaped string
  115. message = literal_eval(message)
  116. out.append(source + ' ' + line_no)
  117. out.append(message)
  118. out.append('-'*40)
  119. return out
  120. def print_console(self):
  121. for line in self.get_console():
  122. print(line)
  123. def get_wait(self, timeout=20):
  124. return WebDriverWait(self.driver, timeout)
  125. def scroll_to(self, selector):
  126. self.execute_script("frappe.ui.scroll('{0}')".format(selector))
  127. def set_route(self, *args):
  128. self.execute_script('frappe.set_route({0})'\
  129. .format(', '.join(['"{0}"'.format(r) for r in args])))
  130. self.wait_for(xpath='//div[@data-page-route="{0}"]'.format('/'.join(args)), timeout=4)
  131. def click(self, css_selector, xpath=None):
  132. self.wait_till_clickable(css_selector, xpath).click()
  133. def click_primary_action(self):
  134. selector = ".page-actions .primary-action"
  135. #self.scroll_to(selector)
  136. self.wait_till_clickable(selector).click()
  137. self.wait_for_ajax()
  138. def click_secondary_action(self):
  139. selector = ".page-actions .btn-secondary"
  140. #self.scroll_to(selector)
  141. self.wait_till_clickable(selector).click()
  142. self.wait_for_ajax()
  143. def click_modal_primary_action(self):
  144. self.get_visible_modal().find_element_by_css_selector('.btn-primary').click()
  145. def get_visible_modal(self):
  146. return self.get_visible_element('.modal-content')
  147. def get_visible_element(self, selector=None, xpath=None):
  148. for elem in self.find(selector=selector, xpath=xpath):
  149. if elem.is_displayed():
  150. return elem
  151. def wait_till_clickable(self, selector=None, xpath=None):
  152. if self.cur_route:
  153. selector = self.cur_route + " " + selector
  154. by = By.CSS_SELECTOR
  155. if xpath:
  156. by = By.XPATH
  157. selector = xpath
  158. return self.get_wait().until(EC.element_to_be_clickable(
  159. (by, selector)))
  160. def execute_script(self, js):
  161. self.driver.execute_script(js)
  162. def wait_for_ajax(self, freeze = False):
  163. self.wait_for('body[data-ajax-state="complete"]', True)
  164. if freeze:
  165. self.wait_for_invisible(".freeze-message-container")
  166. # def go_to_module(module_name, item=None):
  167. # global cur_route
  168. #
  169. # # desktop
  170. # find(".navbar-home", True)[0].click()
  171. # cur_route = None
  172. # wait("#page-desktop")
  173. #
  174. # page = "Module/" + module_name
  175. # m = find('#page-desktop [data-link="{0}"] .app-icon'.format(page))
  176. # if not m:
  177. # page = "List/" + module_name
  178. # m = find('#page-desktop [data-link="{0}"] .app-icon'.format(page))
  179. # if not m:
  180. # raise Exception("Module {0} not found".format(module_name))
  181. #
  182. # m[0].click()
  183. # wait_for_page(page)
  184. #
  185. # if item:
  186. # elem = find('[data-label="{0}"]'.format(item))[0]
  187. # elem.click()
  188. # page = elem.get_attribute("data-route")
  189. # wait_for_page(page)
  190. #
  191. # def new_doc(module, doctype):
  192. # go_to_module(module, doctype)
  193. # primary_action()
  194. # wait_for_page("Form/" + doctype)
  195. #
  196. # def add_child(fieldname):
  197. # find('[data-fieldname="{0}"] .grid-add-row'.format(fieldname))[0].click()
  198. # wait('[data-fieldname="{0}"] .form-grid'.format(fieldname))
  199. #
  200. # def done_add_child(fieldname):
  201. # selector = '[data-fieldname="{0}"] .grid-row-open .btn-success'.format(fieldname)
  202. # scroll_to(selector)
  203. # wait_till_clickable(selector).click()
  204. #
  205. # def set_field(fieldname, value, fieldtype="input"):
  206. # _driver.switch_to.window(_driver.current_window_handle)
  207. # selector = '{0}[data-fieldname="{1}"]'.format(fieldtype, fieldname)
  208. # set_input(selector, value, key=Keys.TAB)
  209. # wait_for_ajax()
  210. #
  211. # def set_select(fieldname, value):
  212. # select = Select(find('select[data-fieldname="{0}"]'.format(fieldname))[0])
  213. # select.select_by_value(value)
  214. # wait_for_ajax()
  215. #
  216. #
  217. # def wait_for_page(name):
  218. # global cur_route
  219. # cur_route = None
  220. # route = '[data-page-route="{0}"]'.format(name)
  221. # wait_for_ajax()
  222. # elem = wait(route)
  223. # wait_for_ajax()
  224. # cur_route = route
  225. # return elem
  226. #
  227. #
  228. # def wait_till_visible(selector):
  229. # if cur_route:
  230. # selector = cur_route + " " + selector
  231. # return get_wait().until(EC.visibility_of_element_located((By.CSS_SELECTOR, selector)))
  232. #
  233. #
  234. # def wait_for_state(state):
  235. # return wait(cur_route + '[data-state="{0}"]'.format(state), True)
  236. #
  237. #