selenium-webdriver 3.3.0 → 4.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.md +273 -0
- package/NOTICE +1 -1
- package/README.md +28 -36
- package/chrome.js +221 -146
- package/edge.js +43 -118
- package/example/chrome_android.js +8 -9
- package/example/chrome_mobile_emulation.js +9 -10
- package/example/firefox_channels.js +23 -19
- package/example/google_search.js +3 -3
- package/example/google_search_test.js +39 -27
- package/example/headless.js +55 -0
- package/example/logging.js +40 -7
- package/firefox.js +769 -0
- package/http/index.js +41 -4
- package/http/util.js +11 -6
- package/ie.js +57 -98
- package/index.js +182 -115
- package/io/index.js +59 -0
- package/io/zip.js +214 -0
- package/jasmine.json +11 -0
- package/lib/actions.js +68 -63
- package/lib/atoms/get-attribute.js +9 -0
- package/lib/atoms/is-displayed.js +73 -78
- package/lib/by.js +9 -1
- package/lib/capabilities.js +303 -201
- package/lib/command.js +28 -50
- package/lib/error.js +113 -42
- package/lib/http.js +167 -135
- package/lib/input.js +1044 -8
- package/lib/promise.js +222 -3331
- package/lib/proxy.js +134 -39
- package/lib/test/bootstrap_jasmine.js +28 -0
- package/lib/test/data/actions/click.html +24 -0
- package/lib/test/data/actions/drag.html +77 -0
- package/lib/test/data/actions/record_click.html +21 -0
- package/lib/test/data/chrome/download.html +2 -0
- package/lib/test/data/click_tests/disabled_element.html +12 -0
- package/lib/test/data/content-editable.html +10 -0
- package/lib/test/data/firefox/webextension.xpi +0 -0
- package/lib/test/data/inputs.html +42 -0
- package/lib/test/data/key_logger.html +34 -0
- package/lib/test/data/nestedElements.html +10 -1
- package/lib/test/data/scrolling_tests/page_with_partially_hidden_element.html +14 -0
- package/lib/test/data/selectPage.html +12 -0
- package/lib/test/data/simpleTest.html +5 -0
- package/lib/test/data/single_text_input.html +12 -0
- package/lib/test/data/upload_invisible.html +45 -0
- package/lib/test/fileserver.js +23 -19
- package/lib/test/index.js +30 -244
- package/lib/test/resources.js +0 -1
- package/lib/webdriver.js +770 -939
- package/net/index.js +21 -25
- package/net/portprober.js +51 -68
- package/package.json +6 -4
- package/remote/index.js +18 -30
- package/safari.js +42 -129
- package/test/actions_test.js +173 -18
- package/test/builder_test.js +106 -0
- package/test/chrome/devtools_test.js +93 -0
- package/test/chrome/options_test.js +48 -154
- package/test/chrome/service_test.js +7 -7
- package/test/cookie_test.js +77 -63
- package/test/element_finding_test.js +206 -201
- package/test/execute_script_test.js +115 -114
- package/test/fingerprint_test.js +16 -15
- package/test/firefox_test.js +251 -0
- package/test/http/http_test.js +18 -1
- package/test/http/util_test.js +2 -2
- package/test/{io_test.js → io/io_test.js} +40 -1
- package/test/io/zip_test.js +127 -0
- package/test/lib/by_test.js +21 -0
- package/test/lib/error_test.js +55 -8
- package/test/lib/http_test.js +18 -130
- package/test/lib/input_test.js +1379 -0
- package/test/lib/promise_test.js +473 -908
- package/test/lib/webdriver_test.js +357 -825
- package/test/logging_test.js +37 -43
- package/test/page_loading_test.js +70 -86
- package/test/proxy_test.js +64 -72
- package/test/rect_test.js +14 -25
- package/test/remote_test.js +13 -32
- package/test/safari_test.js +7 -71
- package/test/stale_element_test.js +20 -23
- package/test/tag_name_test.js +10 -9
- package/test/upload_test.js +27 -35
- package/test/window_test.js +72 -85
- package/testing/index.js +386 -315
- package/.npmignore +0 -2
- package/example/async_await_test.js +0 -68
- package/example/google_search_generator.js +0 -50
- package/example/parallel_flows.js +0 -54
- package/firefox/binary.js +0 -344
- package/firefox/extension.js +0 -187
- package/firefox/index.js +0 -722
- package/firefox/profile.js +0 -430
- package/lib/atoms/getAttribute.js +0 -12
- package/lib/events.js +0 -210
- package/lib/firefox/webdriver.json +0 -70
- package/lib/firefox/webdriver.xpi +0 -0
- package/lib/test/data/firefox/jetpack-sample.xpi +0 -0
- package/lib/test/data/firefox/sample.xpi +0 -0
- package/lib/test/promise.js +0 -79
- package/opera.js +0 -405
- package/phantomjs.js +0 -282
- package/test/firefox/extension_test.js +0 -96
- package/test/firefox/firefox_test.js +0 -226
- package/test/firefox/profile_test.js +0 -185
- package/test/lib/events_test.js +0 -177
- package/test/lib/promise_aplus_test.js +0 -78
- package/test/lib/promise_error_test.js +0 -884
- package/test/lib/promise_flow_test.js +0 -2288
- package/test/lib/promise_generator_test.js +0 -310
- package/test/phantomjs/execute_phantomjs_test.js +0 -59
- package/test/session_test.js +0 -54
- package/test/testing/assert_test.js +0 -373
- package/test/testing/index_test.js +0 -224
- package/testing/assert.js +0 -378
package/lib/http.js
CHANGED
|
@@ -29,13 +29,13 @@ const cmd = require('./command');
|
|
|
29
29
|
const error = require('./error');
|
|
30
30
|
const logging = require('./logging');
|
|
31
31
|
const promise = require('./promise');
|
|
32
|
-
const Session = require('./session')
|
|
33
|
-
const WebElement = require('./webdriver')
|
|
32
|
+
const {Session} = require('./session');
|
|
33
|
+
const {WebElement} = require('./webdriver');
|
|
34
34
|
|
|
35
|
-
const {getAttribute, isDisplayed} = (function() {
|
|
35
|
+
const {getAttribute, isDisplayed} = /** @suppress {undefinedVars|uselessCode} */(function() {
|
|
36
36
|
try {
|
|
37
37
|
return {
|
|
38
|
-
getAttribute: require('./atoms/
|
|
38
|
+
getAttribute: require('./atoms/get-attribute.js'),
|
|
39
39
|
isDisplayed: require('./atoms/is-displayed.js')
|
|
40
40
|
};
|
|
41
41
|
} catch (ex) {
|
|
@@ -145,7 +145,7 @@ function resource(method, path) { return {method: method, path: path}; }
|
|
|
145
145
|
var CommandSpec;
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
/** @typedef {function(!cmd.Command): !
|
|
148
|
+
/** @typedef {function(!cmd.Command): !cmd.Command} */
|
|
149
149
|
var CommandTransformer;
|
|
150
150
|
|
|
151
151
|
|
|
@@ -155,21 +155,17 @@ class InternalTypeError extends TypeError {}
|
|
|
155
155
|
/**
|
|
156
156
|
* @param {!cmd.Command} command The initial command.
|
|
157
157
|
* @param {Atom} atom The name of the atom to execute.
|
|
158
|
-
* @return {!
|
|
158
|
+
* @return {!cmd.Command} The transformed command to execute.
|
|
159
159
|
*/
|
|
160
160
|
function toExecuteAtomCommand(command, atom, ...params) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
161
|
+
if (typeof atom !== 'function') {
|
|
162
|
+
throw new InternalTypeError('atom is not a function: ' + typeof atom);
|
|
163
|
+
}
|
|
166
164
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
resolve(newCmd);
|
|
172
|
-
});
|
|
165
|
+
return new cmd.Command(cmd.Name.EXECUTE_SCRIPT)
|
|
166
|
+
.setParameter('sessionId', command.getParameter('sessionId'))
|
|
167
|
+
.setParameter('script', `return (${atom}).apply(null, arguments)`)
|
|
168
|
+
.setParameter('args', params.map(param => command.getParameter(param)));
|
|
173
169
|
}
|
|
174
170
|
|
|
175
171
|
|
|
@@ -179,7 +175,6 @@ const COMMAND_MAP = new Map([
|
|
|
179
175
|
[cmd.Name.GET_SERVER_STATUS, get('/status')],
|
|
180
176
|
[cmd.Name.NEW_SESSION, post('/session')],
|
|
181
177
|
[cmd.Name.GET_SESSIONS, get('/sessions')],
|
|
182
|
-
[cmd.Name.DESCRIBE_SESSION, get('/session/:sessionId')],
|
|
183
178
|
[cmd.Name.QUIT, del('/session/:sessionId')],
|
|
184
179
|
[cmd.Name.CLOSE, del('/session/:sessionId/window')],
|
|
185
180
|
[cmd.Name.GET_CURRENT_WINDOW_HANDLE, get('/session/:sessionId/window_handle')],
|
|
@@ -211,7 +206,6 @@ const COMMAND_MAP = new Map([
|
|
|
211
206
|
[cmd.Name.GET_ELEMENT_SIZE, get('/session/:sessionId/element/:id/size')],
|
|
212
207
|
[cmd.Name.GET_ELEMENT_ATTRIBUTE, get('/session/:sessionId/element/:id/attribute/:name')],
|
|
213
208
|
[cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, get('/session/:sessionId/element/:id/css/:propertyName')],
|
|
214
|
-
[cmd.Name.ELEMENT_EQUALS, get('/session/:sessionId/element/:id/equals/:other')],
|
|
215
209
|
[cmd.Name.TAKE_ELEMENT_SCREENSHOT, get('/session/:sessionId/element/:id/screenshot')],
|
|
216
210
|
[cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
|
|
217
211
|
[cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/current/maximize')],
|
|
@@ -225,50 +219,105 @@ const COMMAND_MAP = new Map([
|
|
|
225
219
|
[cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute')],
|
|
226
220
|
[cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute_async')],
|
|
227
221
|
[cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
|
|
222
|
+
[cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
|
|
228
223
|
[cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
|
|
229
|
-
[cmd.Name.MOVE_TO, post('/session/:sessionId/moveto')],
|
|
230
|
-
[cmd.Name.CLICK, post('/session/:sessionId/click')],
|
|
231
|
-
[cmd.Name.DOUBLE_CLICK, post('/session/:sessionId/doubleclick')],
|
|
232
|
-
[cmd.Name.MOUSE_DOWN, post('/session/:sessionId/buttondown')],
|
|
233
|
-
[cmd.Name.MOUSE_UP, post('/session/:sessionId/buttonup')],
|
|
234
|
-
[cmd.Name.MOVE_TO, post('/session/:sessionId/moveto')],
|
|
235
|
-
[cmd.Name.SEND_KEYS_TO_ACTIVE_ELEMENT, post('/session/:sessionId/keys')],
|
|
236
|
-
[cmd.Name.TOUCH_SINGLE_TAP, post('/session/:sessionId/touch/click')],
|
|
237
|
-
[cmd.Name.TOUCH_DOUBLE_TAP, post('/session/:sessionId/touch/doubleclick')],
|
|
238
|
-
[cmd.Name.TOUCH_DOWN, post('/session/:sessionId/touch/down')],
|
|
239
|
-
[cmd.Name.TOUCH_UP, post('/session/:sessionId/touch/up')],
|
|
240
|
-
[cmd.Name.TOUCH_MOVE, post('/session/:sessionId/touch/move')],
|
|
241
|
-
[cmd.Name.TOUCH_SCROLL, post('/session/:sessionId/touch/scroll')],
|
|
242
|
-
[cmd.Name.TOUCH_LONG_PRESS, post('/session/:sessionId/touch/longclick')],
|
|
243
|
-
[cmd.Name.TOUCH_FLICK, post('/session/:sessionId/touch/flick')],
|
|
244
224
|
[cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/accept_alert')],
|
|
245
225
|
[cmd.Name.DISMISS_ALERT, post('/session/:sessionId/dismiss_alert')],
|
|
246
226
|
[cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert_text')],
|
|
247
227
|
[cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert_text')],
|
|
248
|
-
[cmd.Name.SET_ALERT_CREDENTIALS, post('/session/:sessionId/alert/credentials')],
|
|
249
228
|
[cmd.Name.GET_LOG, post('/session/:sessionId/log')],
|
|
250
229
|
[cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')],
|
|
251
230
|
[cmd.Name.GET_SESSION_LOGS, post('/logs')],
|
|
252
231
|
[cmd.Name.UPLOAD_FILE, post('/session/:sessionId/file')],
|
|
232
|
+
[cmd.Name.LEGACY_ACTION_CLICK, post('/session/:sessionId/click')],
|
|
233
|
+
[cmd.Name.LEGACY_ACTION_DOUBLE_CLICK, post('/session/:sessionId/doubleclick')],
|
|
234
|
+
[cmd.Name.LEGACY_ACTION_MOUSE_DOWN, post('/session/:sessionId/buttondown')],
|
|
235
|
+
[cmd.Name.LEGACY_ACTION_MOUSE_UP, post('/session/:sessionId/buttonup')],
|
|
236
|
+
[cmd.Name.LEGACY_ACTION_MOUSE_MOVE, post('/session/:sessionId/moveto')],
|
|
237
|
+
[cmd.Name.LEGACY_ACTION_SEND_KEYS, post('/session/:sessionId/keys')],
|
|
238
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_DOWN, post('/session/:sessionId/touch/down')],
|
|
239
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_UP, post('/session/:sessionId/touch/up')],
|
|
240
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_MOVE, post('/session/:sessionId/touch/move')],
|
|
241
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_SCROLL, post('/session/:sessionId/touch/scroll')],
|
|
242
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_LONG_PRESS, post('/session/:sessionId/touch/longclick')],
|
|
243
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_FLICK, post('/session/:sessionId/touch/flick')],
|
|
244
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_SINGLE_TAP, post('/session/:sessionId/touch/click')],
|
|
245
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_DOUBLE_TAP, post('/session/:sessionId/touch/doubleclick')],
|
|
253
246
|
]);
|
|
254
247
|
|
|
255
248
|
|
|
256
249
|
/** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
|
|
257
250
|
const W3C_COMMAND_MAP = new Map([
|
|
251
|
+
// Server status.
|
|
252
|
+
[cmd.Name.GET_SERVER_STATUS, get('/status')],
|
|
253
|
+
// Session management.
|
|
254
|
+
[cmd.Name.NEW_SESSION, post('/session')],
|
|
255
|
+
[cmd.Name.QUIT, del('/session/:sessionId')],
|
|
256
|
+
[cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
|
|
257
|
+
[cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
|
|
258
|
+
// Navigation.
|
|
259
|
+
[cmd.Name.GET_CURRENT_URL, get('/session/:sessionId/url')],
|
|
260
|
+
[cmd.Name.GET, post('/session/:sessionId/url')],
|
|
261
|
+
[cmd.Name.GO_BACK, post('/session/:sessionId/back')],
|
|
262
|
+
[cmd.Name.GO_FORWARD, post('/session/:sessionId/forward')],
|
|
263
|
+
[cmd.Name.REFRESH, post('/session/:sessionId/refresh')],
|
|
264
|
+
// Page inspection.
|
|
265
|
+
[cmd.Name.GET_PAGE_SOURCE, get('/session/:sessionId/source')],
|
|
266
|
+
[cmd.Name.GET_TITLE, get('/session/:sessionId/title')],
|
|
267
|
+
// Script execution.
|
|
268
|
+
[cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute/sync')],
|
|
269
|
+
[cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute/async')],
|
|
270
|
+
// Frame selection.
|
|
271
|
+
[cmd.Name.SWITCH_TO_FRAME, post('/session/:sessionId/frame')],
|
|
272
|
+
[cmd.Name.SWITCH_TO_FRAME_PARENT, post('/session/:sessionId/frame/parent')],
|
|
273
|
+
// Window management.
|
|
274
|
+
[cmd.Name.GET_CURRENT_WINDOW_HANDLE, get('/session/:sessionId/window')],
|
|
275
|
+
[cmd.Name.CLOSE, del('/session/:sessionId/window')],
|
|
276
|
+
[cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
|
|
277
|
+
[cmd.Name.GET_WINDOW_HANDLES, get('/session/:sessionId/window/handles')],
|
|
278
|
+
[cmd.Name.GET_WINDOW_RECT, get('/session/:sessionId/window/rect')],
|
|
279
|
+
[cmd.Name.SET_WINDOW_RECT, post('/session/:sessionId/window/rect')],
|
|
280
|
+
[cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/maximize')],
|
|
281
|
+
[cmd.Name.MINIMIZE_WINDOW, post('/session/:sessionId/window/minimize')],
|
|
282
|
+
[cmd.Name.FULLSCREEN_WINDOW, post('/session/:sessionId/window/fullscreen')],
|
|
283
|
+
// Actions.
|
|
284
|
+
[cmd.Name.ACTIONS, post('/session/:sessionId/actions')],
|
|
285
|
+
[cmd.Name.CLEAR_ACTIONS, del('/session/:sessionId/actions')],
|
|
286
|
+
// Locating elements.
|
|
258
287
|
[cmd.Name.GET_ACTIVE_ELEMENT, get('/session/:sessionId/element/active')],
|
|
288
|
+
[cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
|
|
289
|
+
[cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
|
|
290
|
+
[cmd.Name.FIND_CHILD_ELEMENT, post('/session/:sessionId/element/:id/element')],
|
|
291
|
+
[cmd.Name.FIND_CHILD_ELEMENTS, post('/session/:sessionId/element/:id/elements')],
|
|
292
|
+
// Element interaction.
|
|
293
|
+
[cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
|
|
294
|
+
[cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, get('/session/:sessionId/element/:id/css/:propertyName')],
|
|
295
|
+
[cmd.Name.GET_ELEMENT_RECT, get('/session/:sessionId/element/:id/rect')],
|
|
296
|
+
[cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
|
|
297
|
+
[cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
|
|
298
|
+
[cmd.Name.SEND_KEYS_TO_ELEMENT, post('/session/:sessionId/element/:id/value')],
|
|
299
|
+
[cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
|
|
300
|
+
[cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
|
|
259
301
|
[cmd.Name.GET_ELEMENT_ATTRIBUTE, (cmd) => {
|
|
260
302
|
return toExecuteAtomCommand(cmd, Atom.GET_ATTRIBUTE, 'id', 'name');
|
|
261
303
|
}],
|
|
262
|
-
[cmd.Name.GET_ELEMENT_LOCATION, get('/session/:sessionId/element/:id/rect')],
|
|
263
|
-
[cmd.Name.GET_ELEMENT_SIZE, get('/session/:sessionId/element/:id/rect')],
|
|
264
304
|
[cmd.Name.IS_ELEMENT_DISPLAYED, (cmd) => {
|
|
265
305
|
return toExecuteAtomCommand(cmd, Atom.IS_DISPLAYED, 'id');
|
|
266
306
|
}],
|
|
267
|
-
|
|
268
|
-
[cmd.Name.
|
|
269
|
-
[cmd.Name.
|
|
270
|
-
[cmd.Name.
|
|
271
|
-
[cmd.Name.
|
|
307
|
+
// Cookie management.
|
|
308
|
+
[cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
|
|
309
|
+
[cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
|
|
310
|
+
[cmd.Name.DELETE_ALL_COOKIES, del('/session/:sessionId/cookie')],
|
|
311
|
+
[cmd.Name.GET_COOKIE, get('/session/:sessionId/cookie/:name')],
|
|
312
|
+
[cmd.Name.DELETE_COOKIE, del('/session/:sessionId/cookie/:name')],
|
|
313
|
+
// Alert management.
|
|
314
|
+
[cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/alert/accept')],
|
|
315
|
+
[cmd.Name.DISMISS_ALERT, post('/session/:sessionId/alert/dismiss')],
|
|
316
|
+
[cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert/text')],
|
|
317
|
+
[cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert/text')],
|
|
318
|
+
// Screenshots.
|
|
319
|
+
[cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
|
|
320
|
+
[cmd.Name.TAKE_ELEMENT_SCREENSHOT, get('/session/:sessionId/element/:id/screenshot')],
|
|
272
321
|
]);
|
|
273
322
|
|
|
274
323
|
|
|
@@ -292,35 +341,13 @@ class Client {
|
|
|
292
341
|
}
|
|
293
342
|
|
|
294
343
|
|
|
295
|
-
const CLIENTS =
|
|
296
|
-
/** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */new WeakMap;
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
/**
|
|
300
|
-
* Sends a request using the given executor.
|
|
301
|
-
* @param {!Executor} executor
|
|
302
|
-
* @param {!Request} request
|
|
303
|
-
* @return {!Promise<Response>}
|
|
304
|
-
*/
|
|
305
|
-
function doSend(executor, request) {
|
|
306
|
-
const client = CLIENTS.get(executor);
|
|
307
|
-
if (promise.isPromise(client)) {
|
|
308
|
-
return client.then(client => {
|
|
309
|
-
CLIENTS.set(executor, client);
|
|
310
|
-
return client.send(request);
|
|
311
|
-
});
|
|
312
|
-
} else {
|
|
313
|
-
return client.send(request);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
344
|
|
|
318
345
|
/**
|
|
319
346
|
* @param {Map<string, CommandSpec>} customCommands
|
|
320
347
|
* A map of custom command definitions.
|
|
321
348
|
* @param {boolean} w3c Whether to use W3C command mappings.
|
|
322
349
|
* @param {!cmd.Command} command The command to resolve.
|
|
323
|
-
* @return {!
|
|
350
|
+
* @return {!Request} A promise that will resolve with the
|
|
324
351
|
* command to execute.
|
|
325
352
|
*/
|
|
326
353
|
function buildRequest(customCommands, w3c, command) {
|
|
@@ -334,8 +361,8 @@ function buildRequest(customCommands, w3c, command) {
|
|
|
334
361
|
spec = W3C_COMMAND_MAP.get(command.getName());
|
|
335
362
|
if (typeof spec === 'function') {
|
|
336
363
|
LOG.finest(() => `Transforming command for W3C: ${command.getName()}`);
|
|
337
|
-
|
|
338
|
-
|
|
364
|
+
let newCommand = spec(command);
|
|
365
|
+
return buildRequest(customCommands, w3c, newCommand);
|
|
339
366
|
} else if (spec) {
|
|
340
367
|
return toHttpRequest(spec);
|
|
341
368
|
}
|
|
@@ -345,23 +372,26 @@ function buildRequest(customCommands, w3c, command) {
|
|
|
345
372
|
if (spec) {
|
|
346
373
|
return toHttpRequest(spec);
|
|
347
374
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
'Unrecognized command: ' + command.getName()));
|
|
375
|
+
throw new error.UnknownCommandError(
|
|
376
|
+
'Unrecognized command: ' + command.getName());
|
|
351
377
|
|
|
352
378
|
/**
|
|
353
379
|
* @param {CommandSpec} resource
|
|
354
|
-
* @return {!
|
|
380
|
+
* @return {!Request}
|
|
355
381
|
*/
|
|
356
382
|
function toHttpRequest(resource) {
|
|
357
383
|
LOG.finest(() => `Building HTTP request: ${JSON.stringify(resource)}`);
|
|
358
384
|
let parameters = command.getParameters();
|
|
359
385
|
let path = buildPath(resource.path, parameters);
|
|
360
|
-
return
|
|
386
|
+
return new Request(resource.method, path, parameters);
|
|
361
387
|
}
|
|
362
388
|
}
|
|
363
389
|
|
|
364
390
|
|
|
391
|
+
const CLIENTS =
|
|
392
|
+
/** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */new WeakMap;
|
|
393
|
+
|
|
394
|
+
|
|
365
395
|
/**
|
|
366
396
|
* A command executor that communicates with the server using JSON over HTTP.
|
|
367
397
|
*
|
|
@@ -421,43 +451,41 @@ class Executor {
|
|
|
421
451
|
}
|
|
422
452
|
|
|
423
453
|
/** @override */
|
|
424
|
-
execute(command) {
|
|
454
|
+
async execute(command) {
|
|
425
455
|
let request = buildRequest(this.customCommands_, this.w3c, command);
|
|
426
|
-
|
|
427
|
-
this.log_.finer(() => `>>> ${request.method} ${request.path}`);
|
|
428
|
-
return doSend(this, request).then(response => {
|
|
429
|
-
this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`);
|
|
430
|
-
|
|
431
|
-
let parsed =
|
|
432
|
-
parseHttpResponse(
|
|
433
|
-
command, /** @type {!Response} */ (response), this.w3c);
|
|
434
|
-
|
|
435
|
-
if (command.getName() === cmd.Name.NEW_SESSION
|
|
436
|
-
|| command.getName() === cmd.Name.DESCRIBE_SESSION) {
|
|
437
|
-
if (!parsed || !parsed['sessionId']) {
|
|
438
|
-
throw new error.WebDriverError(
|
|
439
|
-
'Unable to parse new session response: ' + response.body);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// The remote end is a W3C compliant server if there is no `status`
|
|
443
|
-
// field in the response. This is not applicable for the DESCRIBE_SESSION
|
|
444
|
-
// command, which is not defined in the W3C spec.
|
|
445
|
-
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
446
|
-
this.w3c = this.w3c || !('status' in parsed);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
return new Session(parsed['sessionId'], parsed['value']);
|
|
450
|
-
}
|
|
456
|
+
this.log_.finer(() => `>>> ${request.method} ${request.path}`);
|
|
451
457
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
458
|
+
let client = CLIENTS.get(this);
|
|
459
|
+
if (promise.isPromise(client)) {
|
|
460
|
+
client = await client;
|
|
461
|
+
CLIENTS.set(this, client);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
let response = await client.send(request);
|
|
465
|
+
this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`);
|
|
466
|
+
|
|
467
|
+
let httpResponse = /** @type {!Response} */(response);
|
|
468
|
+
let {isW3C, value} = parseHttpResponse(command, httpResponse);
|
|
469
|
+
|
|
470
|
+
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
471
|
+
if (!value || !value.sessionId) {
|
|
472
|
+
throw new error.WebDriverError(
|
|
473
|
+
`Unable to parse new session response: ${response.body}`);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// The remote end is a W3C compliant server if there is no `status`
|
|
477
|
+
// field in the response.
|
|
478
|
+
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
479
|
+
this.w3c = this.w3c || isW3C;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// No implementations use the `capabilities` key yet...
|
|
483
|
+
let capabilities = value.capabilities || value.value;
|
|
484
|
+
return new Session(
|
|
485
|
+
/** @type {{sessionId: string}} */(value).sessionId, capabilities);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return typeof value === 'undefined' ? null : value;
|
|
461
489
|
}
|
|
462
490
|
}
|
|
463
491
|
|
|
@@ -481,40 +509,44 @@ function tryParse(str) {
|
|
|
481
509
|
*
|
|
482
510
|
* @param {!cmd.Command} command The command the response is for.
|
|
483
511
|
* @param {!Response} httpResponse The HTTP response to parse.
|
|
484
|
-
* @
|
|
485
|
-
*
|
|
486
|
-
*
|
|
512
|
+
* @return {{isW3C: boolean, value: ?}} An object describing the parsed
|
|
513
|
+
* response. This object will have two fields: `isW3C` indicates whether
|
|
514
|
+
* the response looks like it came from a remote end that conforms with the
|
|
515
|
+
* W3C WebDriver spec, and `value`, the actual response value.
|
|
487
516
|
* @throws {WebDriverError} If the HTTP response is an error.
|
|
488
517
|
*/
|
|
489
|
-
function parseHttpResponse(command, httpResponse
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
throw new error.WebDriverError(
|
|
496
|
-
`Unexpected HTTP response:\n${httpResponse}`);
|
|
497
|
-
}
|
|
518
|
+
function parseHttpResponse(command, httpResponse) {
|
|
519
|
+
if (httpResponse.status < 200) {
|
|
520
|
+
// This should never happen, but throw the raw response so users report it.
|
|
521
|
+
throw new error.WebDriverError(
|
|
522
|
+
`Unexpected HTTP response:\n${httpResponse}`);
|
|
523
|
+
}
|
|
498
524
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
525
|
+
let parsed = tryParse(httpResponse.body);
|
|
526
|
+
if (parsed && typeof parsed === 'object') {
|
|
527
|
+
let value = parsed.value;
|
|
528
|
+
let isW3C =
|
|
529
|
+
value !== null && typeof value === 'object'
|
|
530
|
+
&& typeof parsed.status === 'undefined';
|
|
531
|
+
|
|
532
|
+
if (!isW3C) {
|
|
533
|
+
error.checkLegacyResponse(parsed);
|
|
534
|
+
|
|
535
|
+
// Adjust legacy new session responses to look like W3C to simplify
|
|
536
|
+
// later processing.
|
|
537
|
+
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
538
|
+
value = parsed;
|
|
502
539
|
}
|
|
503
|
-
return parsed;
|
|
504
|
-
}
|
|
505
540
|
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
// is what puts the executor into W3C mode.
|
|
509
|
-
if (httpResponse.status > 399
|
|
510
|
-
&& (command.getName() == cmd.Name.NEW_SESSION
|
|
511
|
-
|| command.getName() === cmd.Name.DESCRIBE_SESSION)
|
|
512
|
-
&& error.isErrorResponse(parsed)) {
|
|
513
|
-
error.throwDecodedError(parsed);
|
|
541
|
+
} else if (httpResponse.status > 399) {
|
|
542
|
+
error.throwDecodedError(value);
|
|
514
543
|
}
|
|
515
544
|
|
|
516
|
-
|
|
517
|
-
|
|
545
|
+
return {isW3C, value};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
if (parsed !== undefined) {
|
|
549
|
+
return {isW3C: false, value: parsed};
|
|
518
550
|
}
|
|
519
551
|
|
|
520
552
|
let value = httpResponse.body.replace(/\r\n/g, '\n');
|
|
@@ -527,7 +559,7 @@ function parseHttpResponse(command, httpResponse, w3c) {
|
|
|
527
559
|
throw new error.WebDriverError(value);
|
|
528
560
|
}
|
|
529
561
|
|
|
530
|
-
return value || null;
|
|
562
|
+
return {isW3C: false, value: value || null};
|
|
531
563
|
}
|
|
532
564
|
|
|
533
565
|
|