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