1import imghdr
2import struct
3from base64 import decodebytes
4
5from webdriver import Element, NoSuchAlertException, WebDriverException
6
7
8# WebDriver specification ID: dfn-error-response-data
9errors = {
10    "element click intercepted": 400,
11    "element not selectable": 400,
12    "element not interactable": 400,
13    "insecure certificate": 400,
14    "invalid argument": 400,
15    "invalid cookie domain": 400,
16    "invalid coordinates": 400,
17    "invalid element state": 400,
18    "invalid selector": 400,
19    "invalid session id": 404,
20    "javascript error": 500,
21    "move target out of bounds": 500,
22    "no such alert": 404,
23    "no such cookie": 404,
24    "no such element": 404,
25    "no such frame": 404,
26    "no such window": 404,
27    "script timeout": 500,
28    "session not created": 500,
29    "stale element reference": 404,
30    "timeout": 500,
31    "unable to set cookie": 500,
32    "unable to capture screen": 500,
33    "unexpected alert open": 500,
34    "unknown command": 404,
35    "unknown error": 500,
36    "unknown method": 405,
37    "unsupported operation": 500,
38}
39
40
41def assert_error(response, error_code):
42    """
43    Verify that the provided webdriver.Response instance described
44    a valid error response as defined by `dfn-send-an-error` and
45    the provided error code.
46
47    :param response: ``webdriver.Response`` instance.
48    :param error_code: String value of the expected error code
49    """
50    assert response.status == errors[error_code]
51    assert "value" in response.body
52    assert response.body["value"]["error"] == error_code
53    assert isinstance(response.body["value"]["message"], str)
54    assert isinstance(response.body["value"]["stacktrace"], str)
55    assert_response_headers(response.headers)
56
57
58def assert_success(response, value=None):
59    """
60    Verify that the provided webdriver.Response instance described
61    a valid success response as defined by `dfn-send-a-response` and
62    the provided response value.
63
64    :param response: ``webdriver.Response`` instance.
65    :param value: Expected value of the response body, if any.
66    """
67    assert response.status == 200, str(response.error)
68
69    if value is not None:
70        assert response.body["value"] == value
71
72    assert_response_headers(response.headers)
73    return response.body.get("value")
74
75
76def assert_response_headers(headers):
77    """
78    Method to assert response headers for WebDriver requests
79
80    :param headers: dict with header data
81    """
82    assert 'cache-control' in headers
83    assert 'no-cache' == headers['cache-control']
84    assert 'content-type' in headers
85    assert 'application/json; charset=utf-8' == headers['content-type']
86
87
88def assert_dialog_handled(session, expected_text, expected_retval):
89    # If there were any existing dialogs prior to the creation of this
90    # fixture's dialog, then the "Get Alert Text" command will return
91    # successfully. In that case, the text must be different than that
92    # of this fixture's dialog.
93    try:
94        assert session.alert.text != expected_text, (
95            "User prompt with text '{}' was not handled.".format(expected_text))
96
97    except NoSuchAlertException:
98        # If dialog has been closed and no other one is open, check its return value
99        prompt_retval = session.execute_script(" return window.dialog_return_value;")
100        assert prompt_retval == expected_retval
101
102
103def assert_files_uploaded(session, element, files):
104
105    def get_file_contents(file_index):
106        return session.execute_async_script("""
107            let files = arguments[0].files;
108            let index = arguments[1];
109            let resolve = arguments[2];
110
111            var reader = new FileReader();
112            reader.onload = function(event) {
113              resolve(reader.result);
114            };
115            reader.readAsText(files[index]);
116        """, (element, file_index))
117
118    def get_uploaded_file_names():
119        return session.execute_script("""
120            let fileList = arguments[0].files;
121            let files = [];
122
123            for (var i = 0; i < fileList.length; i++) {
124              files.push(fileList[i].name);
125            }
126
127            return files;
128        """, args=(element,))
129
130    expected_file_names = [str(f.basename) for f in files]
131    assert get_uploaded_file_names() == expected_file_names
132
133    for index, f in enumerate(files):
134        assert get_file_contents(index) == f.read()
135
136
137def assert_is_active_element(session, element):
138    """Verify that element reference is the active element."""
139    from_js = session.execute_script("return document.activeElement")
140
141    if element is None:
142        assert from_js is None
143    else:
144        assert_same_element(session, element, from_js)
145
146
147def assert_same_element(session, a, b):
148    """Verify that two element references describe the same element."""
149    if isinstance(a, dict):
150        assert Element.identifier in a, "Actual value does not describe an element"
151        a_id = a[Element.identifier]
152    elif isinstance(a, Element):
153        a_id = a.id
154    else:
155        raise AssertionError("Actual value is not a dictionary or web element")
156
157    if isinstance(b, dict):
158        assert Element.identifier in b, "Expected value does not describe an element"
159        b_id = b[Element.identifier]
160    elif isinstance(b, Element):
161        b_id = b.id
162    else:
163        raise AssertionError("Expected value is not a dictionary or web element")
164
165    if a_id == b_id:
166        return
167
168    message = ("Expected element references to describe the same element, " +
169               "but they did not.")
170
171    # Attempt to provide more information, accounting for possible errors such
172    # as stale element references or not visible elements.
173    try:
174        a_markup = session.execute_script("return arguments[0].outerHTML;", args=(a,))
175        b_markup = session.execute_script("return arguments[0].outerHTML;", args=(b,))
176        message += " Actual: `%s`. Expected: `%s`." % (a_markup, b_markup)
177    except WebDriverException:
178        pass
179
180    raise AssertionError(message)
181
182
183def assert_in_events(session, expected_events):
184    actual_events = session.execute_script("return window.events")
185    for expected_event in expected_events:
186        assert expected_event in actual_events
187
188
189def assert_events_equal(session, expected_events):
190    actual_events = session.execute_script("return window.events")
191    assert actual_events == expected_events
192
193
194def assert_element_has_focus(target_element):
195    session = target_element.session
196
197    active_element = session.execute_script("return document.activeElement")
198    active_tag = active_element.property("localName")
199    target_tag = target_element.property("localName")
200
201    assert active_element == target_element, (
202        "Focussed element is <%s>, not <%s>" % (active_tag, target_tag))
203
204
205def assert_move_to_coordinates(point, target, events):
206    for e in events:
207        if e["type"] != "mousemove":
208            assert e["pageX"] == point["x"]
209            assert e["pageY"] == point["y"]
210            assert e["target"] == target
211
212
213def assert_png(screenshot):
214    """Test that screenshot is a Base64 encoded PNG file."""
215    image = decodebytes(screenshot.encode())
216    mime_type = imghdr.what("", image)
217    assert mime_type == "png", "Expected image to be PNG, but it was {}".format(mime_type)
218