mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-19 14:57:32 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
71
testing/marionette/client/docs/advanced/stale.rst
Normal file
71
testing/marionette/client/docs/advanced/stale.rst
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
Dealing with Stale Elements
|
||||
===========================
|
||||
.. py:currentmodule:: marionette
|
||||
|
||||
Marionette does not keep a live representation of the DOM saved. All it can do
|
||||
is send commands to the Marionette server which queries the DOM on the client's
|
||||
behalf. References to elements are also not passed from server to client. A
|
||||
unique id is generated for each element that gets referenced and a mapping of
|
||||
id to element object is stored on the server. When commands such as
|
||||
:func:`~HTMLElement.click()` are run, the client sends the element's id along
|
||||
with the command. The server looks up the proper DOM element in its reference
|
||||
table and executes the command on it.
|
||||
|
||||
In practice this means that the DOM can change state and Marionette will never
|
||||
know until it sends another query. For example, look at the following HTML::
|
||||
|
||||
<head>
|
||||
<script type=text/javascript>
|
||||
function addDiv() {
|
||||
var div = document.createElement("div");
|
||||
document.getElementById("container").appendChild(div);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
</div>
|
||||
<input id="button" type=button onclick="addDiv();">
|
||||
</body>
|
||||
|
||||
Care needs to be taken as the DOM is being modified after the page has loaded.
|
||||
The following code has a race condition::
|
||||
|
||||
button = client.find_element('id', 'button')
|
||||
button.click()
|
||||
assert len(client.find_elements('css selector', '#container div')) > 0
|
||||
|
||||
|
||||
Explicit Waiting and Expected Conditions
|
||||
----------------------------------------
|
||||
|
||||
To avoid the above scenario, manual synchronisation is needed. Waits are used
|
||||
to pause program execution until a given condition is true. This is a useful
|
||||
technique to employ when documents load new content or change after
|
||||
``Document.readyState``'s value changes to "complete".
|
||||
|
||||
The :class:`Wait` helper class provided by Marionette avoids some of the
|
||||
caveats of ``time.sleep(n)``. It will return immediately once the provided
|
||||
condition evaluates to true.
|
||||
|
||||
To avoid the race condition in the above example, one could do::
|
||||
|
||||
button = client.find_element('id', 'button')
|
||||
button.click()
|
||||
|
||||
def find_divs():
|
||||
return client.find_elements('css selector', '#container div')
|
||||
|
||||
divs = Wait(client).until(find_divs)
|
||||
assert len(divs) > 0
|
||||
|
||||
This avoids the race condition. Because finding elements is a common condition
|
||||
to wait for, it is built in to Marionette. Instead of the above, you could
|
||||
write::
|
||||
|
||||
button = client.find_element('id', 'button')
|
||||
button.click()
|
||||
assert len(Wait(client).until(expected.elements_present('css selector', '#container div'))) > 0
|
||||
|
||||
For a full list of built-in conditions, see :mod:`~marionette.expected`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue