1# testdriver.js Automation
2
3testdriver.js provides a means to automate tests that cannot be
4written purely using web platform APIs. Outside of automation
5contexts, it allows human operators to provide expected input
6manually (for operations which may be described in simple terms).
7
8It is currently supported only for [testharness.js](testharness)
9tests.
10
11## API
12
13testdriver.js exposes its API through the `test_driver` variable in
14the global scope.
15
16### Actions
17Usage:
18```
19let actions = new test_driver.Actions()
20   .action1()
21   .action2();
22actions.send()
23```
24
25Test authors are encouraged to use the builder API to generate the
26sequence of actions. The builder API can be accessed via the `new
27test_driver.Actions()` object, and actions are defined in
28[testdriver-actions.js](https://github.com/web-platform-tests/wpt/blob/master/resources/testdriver-actions.js)
29
30The `actions.send()` function causes the sequence of actions to be
31sent to the browser. It is based on the [WebDriver
32API](https://w3c.github.io/webdriver/#actions).  The action can be a
33keyboard action, a pointer action or a pause. It returns a promise
34that resolves after the actions have been sent, or rejects if an error
35was thrown.
36
37
38Example:
39
40```js
41let text_box = document.getElementById("text");
42
43let actions = new test_driver.Actions()
44    .pointerMove(0, 0, {origin: text_box})
45    .pointerDown()
46    .pointerUp()
47    .addTick()
48    .keyDown("p")
49    .keyUp("p");
50
51actions.send();
52```
53
54Calling into `send()` is going to dispatch the action sequence (via
55`test_driver.action_sequence`) and also returns a promise which should
56be handled however is appropriate in the test. The other functions in
57the `Actions()` object are going to modify the state of the object by
58adding a new action in the sequence and returning the same object. So
59the functions can be easily chained, as shown in the example
60above. Here is a list of helper functions in the `Actions` class:
61
62```
63pointerDown: Create a pointerDown event for the current default pointer source
64pointerUp: Create a pointerUp event for the current default pointer source
65pointerMove: Create a move event for the current default pointer source
66keyDown: Create a keyDown event for the current default key source
67keyUp: Create a keyUp event for the current default key source
68pause: Add a pause to the current tick
69addTick: Insert a new actions tick
70setPointer: Set the current default pointer source (By detault the pointerType is mouse)
71addPointer: Add a new pointer input source with the given name
72setKeyboard: Set the current default key source
73addKeyboard: Add a new key input source with the given name
74```
75
76This works with elements in other frames/windows as long as they are
77same-origin with the test, and the test does not depend on the
78window.name property remaining unset on the target window.
79
80### bless
81
82Usage: `test_driver.bless(intent, action)`
83 * _intent_: a string describing the motivation for this invocation
84 * _action_: an optional function
85
86This function simulates [activation][activation], allowing tests to
87perform privileged operations that require user interaction. For
88example, sandboxed iframes with
89`allow-top-navigation-by-user-activation` may only navigate their
90parent's browsing context under these circumstances. The _intent_
91string is presented to human operators when the test is not run in
92automation.
93
94This method returns a promise which is resolved with the result of
95invoking the _action_ function. If no such function is provided, the
96promise is resolved with the value `undefined`.
97
98Example:
99
100```js
101var mediaElement = document.createElement('video');
102
103test_driver.bless('initiate media playback', function () {
104  mediaElement.play();
105});
106```
107
108### click
109
110Usage: `test_driver.click(element)`
111 * _element_: a DOM Element object
112
113This function causes a click to occur on the target element (an
114`Element` object), potentially scrolling the document to make it
115possible to click it. It returns a promise that resolves after the
116click has occurred or rejects if the element cannot be clicked (for
117example, it is obscured by an element on top of it).
118
119This works with elements in other frames/windows as long as they are
120same-origin with the test, and the test does not depend on the
121window.name property remaining unset on the target window.
122
123Note that if the element to be clicked does not have a unique ID, the
124document must not have any DOM mutations made between the function
125being called and the promise settling.
126
127## delete_all_cookies
128
129Usage: `test_driver.delete_all_cookies(context=null)`
130 * _context_: an optional WindowProxy for the browsing context in which to
131              perform the call.
132
133This function deletes all cookies for the current browsing context.
134
135### send_keys
136
137Usage: `test_driver.send_keys(element, keys)`
138 * _element_: a DOM Element object
139 * _keys_: string to send to the element
140
141This function causes the string _keys_ to be sent to the target
142element (an `Element` object), potentially scrolling the document to
143make it possible to send keys. It returns a promise that resolves
144after the keys have been sent, or rejects if the keys cannot be sent
145to the element.
146
147This works with elements in other frames/windows as long as they are
148same-origin with the test, and the test does not depend on the
149window.name property remaining unset on the target window.
150
151Note that if the element that the keys need to be sent to does not have
152a unique ID, the document must not have any DOM mutations made
153between the function being called and the promise settling.
154
155To send special keys, one must send the respective key's codepoint. Since this uses the WebDriver protocol, you can find a [list for code points to special keys in the spec](https://w3c.github.io/webdriver/#keyboard-actions).
156For example, to send the tab key you would send "\uE004".
157
158[activation]: https://html.spec.whatwg.org/multipage/interaction.html#activation
159
160### set_permission
161
162Usage: `test_driver.set_permission(descriptor, state, one_realm=false, context=null)`
163 * _descriptor_: a
164   [PermissionDescriptor](https://w3c.github.io/permissions/#dictdef-permissiondescriptor)
165   or derived object
166 * _state_: a
167   [PermissionState](https://w3c.github.io/permissions/#enumdef-permissionstate)
168   value
169 * _one_realm_: a boolean that indicates whether the permission settings
170   apply to only one realm
171 * context: a WindowProxy for the browsing context in which to perform the call
172
173This function causes permission requests and queries for the status of a
174certain permission type (e.g. "push", or "background-fetch") to always
175return _state_. It returns a promise that resolves after the permission has
176been set to be overridden with _state_.
177
178Example:
179
180``` js
181await test_driver.set_permission({ name: "background-fetch" }, "denied");
182await test_driver.set_permission({ name: "push", userVisibleOnly: true }, "granted", true);
183```
184
185## Using testdriver in Other Browsing Contexts
186
187Testdriver can be used in browsing contexts (i.e. windows or frames)
188from which it's possible to get a reference to the top-level test
189context. There are two basic approaches depending on whether the
190context in which testdriver is used is same-origin with the test
191context, or different origin.
192
193For same-origin contexts, the context can be passed directly into the
194testdriver API calls. For functions that take an element argument this
195is done implicitly using the owner document of the element. For
196functions that don't take an element, this is done via an explicit
197context argument, which takes a WindowProxy object.
198
199Example:
200```
201let win = window.open("example.html")
202win.onload = () => {
203  await test_driver.set_permission({ name: "background-fetch" }, "denied", win);
204}
205```
206
207For the actions API, the context can be set using the `setContext`
208method on the builder:
209
210```
211let actions = new test_driver.Actions()
212    .setContext(frames[0])
213    .keyDown("p")
214    .keyUp("p");
215actions.send();
216```
217
218Note that if an action uses an element reference, the context will be
219derived from that element, and must match any explictly set
220context. Using elements in multiple contexts in a single action chain
221is not supported.
222
223
224For cross-origin cases, passing in the context id doesn't work because
225of limitations in the WebDriver protocol used to implement testdriver
226in a cross-browser fashion. Instead one may include the testdriver
227scripts directly in the relevant document, and use the
228`set_test_context` API to specify the browsing context containing
229testharness.js. Commands are then sent via postMessage to the test
230context. For convenience there is also a `message_test` function that
231can be used to send arbitary messages to the test window. For example,
232in an auxillary browsing context:
233
234
235```
236testdriver.set_test_context(window.opener)
237await testdriver.click(document.getElementsByTagName("button")[0])
238testdriver.message_test("click complete")
239```
240
241The requirement to have a handle to the test window does mean it's
242currently not possible to write tests where such handles can't be
243obtained e.g. in the case of `rel=noopener`.
244