1# Copyright 2008-2009 WebDriver committers 2# Copyright 2008-2009 Google Inc. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16 17"""WebElement implementation.""" 18import os 19import zipfile 20from StringIO import StringIO 21import base64 22 23 24from command import Command 25from selenium.common.exceptions import WebDriverException 26from selenium.common.exceptions import InvalidSelectorException 27from selenium.webdriver.common.by import By 28from selenium.webdriver.common.keys import Keys 29 30 31class WebElement(object): 32 """Represents an HTML element. 33 34 Generally, all interesting operations to do with interacting with a page 35 will be performed through this interface.""" 36 def __init__(self, parent, id_): 37 self._parent = parent 38 self._id = id_ 39 40 @property 41 def tag_name(self): 42 """Gets this element's tagName property.""" 43 return self._execute(Command.GET_ELEMENT_TAG_NAME)['value'] 44 45 @property 46 def text(self): 47 """Gets the text of the element.""" 48 return self._execute(Command.GET_ELEMENT_TEXT)['value'] 49 50 def click(self): 51 """Clicks the element.""" 52 self._execute(Command.CLICK_ELEMENT) 53 54 def submit(self): 55 """Submits a form.""" 56 self._execute(Command.SUBMIT_ELEMENT) 57 58 def clear(self): 59 """Clears the text if it's a text entry element.""" 60 self._execute(Command.CLEAR_ELEMENT) 61 62 def get_attribute(self, name): 63 """Gets the attribute value.""" 64 resp = self._execute(Command.GET_ELEMENT_ATTRIBUTE, {'name': name}) 65 attributeValue = '' 66 if resp['value'] is None: 67 attributeValue = None 68 else: 69 attributeValue = unicode(resp['value']) 70 if type(resp['value']) is bool: 71 attributeValue = attributeValue.lower() 72 73 return attributeValue 74 75 def is_selected(self): 76 """Whether the element is selected.""" 77 return self._execute(Command.IS_ELEMENT_SELECTED)['value'] 78 79 def is_enabled(self): 80 """Whether the element is enabled.""" 81 return self._execute(Command.IS_ELEMENT_ENABLED)['value'] 82 83 def find_element_by_id(self, id_): 84 """Finds element by id.""" 85 return self.find_element(by=By.ID, value=id_) 86 87 def find_elements_by_id(self, id_): 88 return self.find_elements(by=By.ID, value=id_) 89 90 def find_element_by_name(self, name): 91 """Find element by name.""" 92 return self.find_element(by=By.NAME, value=name) 93 94 def find_elements_by_name(self, name): 95 return self.find_elements(by=By.NAME, value=name) 96 97 def find_element_by_link_text(self, link_text): 98 """Finds element by link text.""" 99 return self.find_element(by=By.LINK_TEXT, value=link_text) 100 101 def find_elements_by_link_text(self, link_text): 102 return self.find_elements(by=By.LINK_TEXT, value=link_text) 103 104 def find_element_by_partial_link_text(self, link_text): 105 return self.find_element(by=By.PARTIAL_LINK_TEXT, value=link_text) 106 107 def find_elements_by_partial_link_text(self, link_text): 108 return self.find_elements(by=By.PARTIAL_LINK_TEXT, value=link_text) 109 110 def find_element_by_tag_name(self, name): 111 return self.find_element(by=By.TAG_NAME, value=name) 112 113 def find_elements_by_tag_name(self, name): 114 return self.find_elements(by=By.TAG_NAME, value=name) 115 116 def find_element_by_xpath(self, xpath): 117 """Finds element by xpath.""" 118 return self.find_element(by=By.XPATH, value=xpath) 119 120 def find_elements_by_xpath(self, xpath): 121 """Finds elements within the elements by xpath.""" 122 return self.find_elements(by=By.XPATH, value=xpath) 123 124 def find_element_by_class_name(self, name): 125 """Finds an element by their class name.""" 126 return self.find_element(by=By.CLASS_NAME, value=name) 127 128 def find_elements_by_class_name(self, name): 129 """Finds elements by their class name.""" 130 return self.find_elements(by=By.CLASS_NAME, value=name) 131 132 def find_element_by_css_selector(self, css_selector): 133 """Find and return an element by CSS selector.""" 134 return self.find_element(by=By.CSS_SELECTOR, value=css_selector) 135 136 def find_elements_by_css_selector(self, css_selector): 137 """Find and return list of multiple elements by CSS selector.""" 138 return self.find_elements(by=By.CSS_SELECTOR, value=css_selector) 139 140 def send_keys(self, *value): 141 """Simulates typing into the element.""" 142 # transfer file to another machine only if remote driver is used 143 # the same behaviour as for java binding 144 parent_class = self.parent.__class__ 145 fqcn = parent_class.__module__ + "." + parent_class.__name__ 146 is_remote = fqcn == "selenium.webdriver.remote.webdriver.WebDriver" 147 if is_remote: 148 local_file = LocalFileDetector.is_local_file(*value) 149 if local_file is not None: 150 value = self._upload(local_file) 151 152 typing = [] 153 for val in value: 154 if isinstance(val, Keys): 155 typing.append(val) 156 elif isinstance(val, int): 157 val = str(val) 158 for i in range(len(val)): 159 typing.append(val[i]) 160 else: 161 for i in range(len(val)): 162 typing.append(val[i]) 163 self._execute(Command.SEND_KEYS_TO_ELEMENT, {'value': typing}) 164 165 # RenderedWebElement Items 166 def is_displayed(self): 167 """Whether the element would be visible to a user""" 168 return self._execute(Command.IS_ELEMENT_DISPLAYED)['value'] 169 170 @property 171 def location_once_scrolled_into_view(self): 172 """CONSIDERED LIABLE TO CHANGE WITHOUT WARNING. Use this to discover where on the screen an 173 element is so that we can click it. This method should cause the element to be scrolled 174 into view. 175 176 Returns the top lefthand corner location on the screen, or None if the element is not visible""" 177 return self._execute(Command.GET_ELEMENT_LOCATION_ONCE_SCROLLED_INTO_VIEW)['value'] 178 179 @property 180 def size(self): 181 """ Returns the size of the element """ 182 size = self._execute(Command.GET_ELEMENT_SIZE)['value'] 183 new_size = {} 184 new_size["height"] = size["height"] 185 new_size["width"] = size["width"] 186 return new_size 187 188 def value_of_css_property(self, property_name): 189 """ Returns the value of a CSS property """ 190 return self._execute(Command.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, 191 {'propertyName': property_name})['value'] 192 193 @property 194 def location(self): 195 """ Returns the location of the element in the renderable canvas""" 196 return self._execute(Command.GET_ELEMENT_LOCATION)['value'] 197 198 @property 199 def parent(self): 200 return self._parent 201 202 @property 203 def id(self): 204 return self._id 205 206 def __eq__(self, element): 207 return self._id == element.id 208 209 # Private Methods 210 def _execute(self, command, params=None): 211 """Executes a command against the underlying HTML element. 212 213 Args: 214 command: The name of the command to _execute as a string. 215 params: A dictionary of named parameters to send with the command. 216 217 Returns: 218 The command's JSON response loaded into a dictionary object. 219 """ 220 if not params: 221 params = {} 222 params['id'] = self._id 223 return self._parent.execute(command, params) 224 225 def find_element(self, by=By.ID, value=None): 226 if isinstance(by, tuple) or isinstance(value, int) or value==None: 227 raise InvalidSelectorException("Invalid locator values passed in") 228 229 return self._execute(Command.FIND_CHILD_ELEMENT, 230 {"using": by, "value": value})['value'] 231 232 def find_elements(self, by=By.ID, value=None): 233 if isinstance(by, tuple) or isinstance(value, int) or value==None: 234 raise InvalidSelectorException("Invalid locator values passed in") 235 236 return self._execute(Command.FIND_CHILD_ELEMENTS, 237 {"using": by, "value": value})['value'] 238 239 def _upload(self, filename): 240 fp = StringIO() 241 zipped = zipfile.ZipFile(fp, 'w', zipfile.ZIP_DEFLATED) 242 zipped.write(filename, os.path.split(filename)[1]) 243 zipped.close() 244 try: 245 return self._execute(Command.UPLOAD_FILE, 246 {'file': base64.encodestring(fp.getvalue())})['value'] 247 except WebDriverException as e: 248 if "Unrecognized command: POST" in e.__str__(): 249 return filename 250 elif "Command not found: POST " in e.__str__(): 251 return filename 252 elif '{"status":405,"value":["GET","HEAD","DELETE"]}' in e.__str__(): 253 return filename 254 else: 255 raise e 256 257class LocalFileDetector(object): 258 259 @classmethod 260 def is_local_file(cls, *keys): 261 file_path = '' 262 typing = [] 263 for val in keys: 264 if isinstance(val, Keys): 265 typing.append(val) 266 elif isinstance(val, int): 267 val = str(val) 268 for i in range(len(val)): 269 typing.append(val[i]) 270 else: 271 for i in range(len(val)): 272 typing.append(val[i]) 273 file_path = ''.join(typing) 274 275 if file_path is '': 276 return None 277 278 try: 279 if os.path.exists(file_path): 280 return file_path 281 except: 282 pass 283 return None 284 285