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