selenium-webdriver 4.0.0-alpha.7 → 4.0.0-alpha.8
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/.eslintrc.js +27 -0
- package/.prettierrc +4 -0
- package/CHANGES.md +21 -0
- package/README.md +2 -0
- package/chrome.js +69 -79
- package/chromium.js +423 -161
- package/devtools/CDPConnection.js +37 -0
- package/devtools/generator/protocol-dts-generator.js +429 -0
- package/edge.js +57 -58
- package/firefox.js +200 -202
- package/http/index.js +128 -116
- package/http/util.js +56 -64
- package/ie.js +180 -104
- package/index.js +246 -199
- package/io/exec.js +34 -39
- package/io/index.js +141 -151
- package/io/zip.js +75 -65
- package/lib/actions.js +137 -120
- package/lib/atoms/find-elements.js +123 -0
- package/lib/atoms/get-attribute.js +79 -59
- package/lib/atoms/is-displayed.js +72 -72
- package/lib/atoms/make-atoms-module.js +13 -7
- package/lib/by.js +192 -65
- package/lib/capabilities.js +75 -84
- package/lib/command.js +17 -20
- package/lib/error.js +134 -168
- package/lib/http.js +321 -232
- package/lib/input.js +319 -296
- package/lib/logging.js +126 -137
- package/lib/promise.js +60 -66
- package/lib/proxy.js +31 -41
- package/lib/session.js +12 -15
- package/lib/symbols.js +3 -4
- package/lib/until.js +162 -162
- package/lib/webdriver.js +711 -530
- package/net/index.js +30 -36
- package/net/portprober.js +30 -143
- package/opera.js +406 -0
- package/package.json +14 -6
- package/proxy.js +2 -2
- package/remote/index.js +190 -187
- package/safari.js +40 -49
package/lib/http.js
CHANGED
|
@@ -23,21 +23,28 @@
|
|
|
23
23
|
* the {@link Executor} to send commands to the remote end.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
'use strict'
|
|
26
|
+
'use strict'
|
|
27
27
|
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const {
|
|
28
|
+
const path = require('path')
|
|
29
|
+
const cmd = require('./command')
|
|
30
|
+
const error = require('./error')
|
|
31
|
+
const logging = require('./logging')
|
|
32
|
+
const promise = require('./promise')
|
|
33
|
+
const { Session } = require('./session')
|
|
34
|
+
const { WebElement } = require('./webdriver')
|
|
34
35
|
|
|
35
36
|
const getAttribute = requireAtom(
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
'get-attribute.js',
|
|
38
|
+
'//javascript/node/selenium-webdriver/lib/atoms:get-attribute.js'
|
|
39
|
+
)
|
|
38
40
|
const isDisplayed = requireAtom(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
'is-displayed.js',
|
|
42
|
+
'//javascript/node/selenium-webdriver/lib/atoms:is-displayed.js'
|
|
43
|
+
)
|
|
44
|
+
const findElements = requireAtom(
|
|
45
|
+
'find-elements.js',
|
|
46
|
+
'//javascript/node/selenium-webdriver/lib/atoms:find-elements.js'
|
|
47
|
+
)
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
50
|
* @param {string} module
|
|
@@ -46,36 +53,36 @@ const isDisplayed = requireAtom(
|
|
|
46
53
|
*/
|
|
47
54
|
function requireAtom(module, bazelTarget) {
|
|
48
55
|
try {
|
|
49
|
-
return require('./atoms/' + module)
|
|
56
|
+
return require('./atoms/' + module)
|
|
50
57
|
} catch (ex) {
|
|
51
58
|
try {
|
|
52
|
-
const file = bazelTarget.slice(2).replace(':', '/')
|
|
53
|
-
|
|
59
|
+
const file = bazelTarget.slice(2).replace(':', '/')
|
|
60
|
+
console.log(`../../../bazel-bin/${file}`)
|
|
61
|
+
return require(path.resolve(`../../../bazel-bin/${file}`))
|
|
54
62
|
} catch (ex2) {
|
|
55
|
-
console.log(ex2)
|
|
63
|
+
console.log(ex2)
|
|
56
64
|
throw Error(
|
|
57
|
-
`Failed to import atoms module ${module}. If running in dev mode, you`
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
`Failed to import atoms module ${module}. If running in dev mode, you` +
|
|
66
|
+
` need to run \`bazel build ${bazelTarget}\` from the project` +
|
|
67
|
+
`root: ${ex}`
|
|
68
|
+
)
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
71
|
}
|
|
63
72
|
|
|
64
|
-
|
|
65
73
|
/**
|
|
66
74
|
* Converts a headers map to a HTTP header block string.
|
|
67
75
|
* @param {!Map<string, string>} headers The map to convert.
|
|
68
76
|
* @return {string} The headers as a string.
|
|
69
77
|
*/
|
|
70
78
|
function headersToString(headers) {
|
|
71
|
-
let ret = []
|
|
72
|
-
headers.forEach(function(value, name) {
|
|
73
|
-
ret.push(`${name.toLowerCase()}: ${value}`)
|
|
74
|
-
})
|
|
75
|
-
return ret.join('\n')
|
|
79
|
+
let ret = []
|
|
80
|
+
headers.forEach(function (value, name) {
|
|
81
|
+
ret.push(`${name.toLowerCase()}: ${value}`)
|
|
82
|
+
})
|
|
83
|
+
return ret.join('\n')
|
|
76
84
|
}
|
|
77
85
|
|
|
78
|
-
|
|
79
86
|
/**
|
|
80
87
|
* Represents a HTTP request message. This class is a "partial" request and only
|
|
81
88
|
* defines the path on the server to send a request to. It is each client's
|
|
@@ -89,25 +96,25 @@ class Request {
|
|
|
89
96
|
* @param {Object=} opt_data This request's non-serialized JSON payload data.
|
|
90
97
|
*/
|
|
91
98
|
constructor(method, path, opt_data) {
|
|
92
|
-
this.method = /** string */method
|
|
93
|
-
this.path = /** string */path
|
|
94
|
-
this.data = /** Object */opt_data
|
|
95
|
-
this.headers = /** !Map<string, string> */new Map(
|
|
96
|
-
|
|
99
|
+
this.method = /** string */ method
|
|
100
|
+
this.path = /** string */ path
|
|
101
|
+
this.data = /** Object */ opt_data
|
|
102
|
+
this.headers = /** !Map<string, string> */ new Map([
|
|
103
|
+
['Accept', 'application/json; charset=utf-8'],
|
|
104
|
+
])
|
|
97
105
|
}
|
|
98
106
|
|
|
99
107
|
/** @override */
|
|
100
108
|
toString() {
|
|
101
|
-
let ret = `${this.method} ${this.path} HTTP/1.1\n
|
|
102
|
-
ret += headersToString(this.headers) + '\n\n'
|
|
109
|
+
let ret = `${this.method} ${this.path} HTTP/1.1\n`
|
|
110
|
+
ret += headersToString(this.headers) + '\n\n'
|
|
103
111
|
if (this.data) {
|
|
104
|
-
ret += JSON.stringify(this.data)
|
|
112
|
+
ret += JSON.stringify(this.data)
|
|
105
113
|
}
|
|
106
|
-
return ret
|
|
114
|
+
return ret
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
|
|
111
118
|
/**
|
|
112
119
|
* Represents a HTTP response message.
|
|
113
120
|
* @final
|
|
@@ -120,54 +127,54 @@ class Response {
|
|
|
120
127
|
* @param {string} body The response body.
|
|
121
128
|
*/
|
|
122
129
|
constructor(status, headers, body) {
|
|
123
|
-
this.status = /** number */status
|
|
124
|
-
this.body = /** string */body
|
|
125
|
-
this.headers = /** !Map<string, string>*/new Map
|
|
130
|
+
this.status = /** number */ status
|
|
131
|
+
this.body = /** string */ body
|
|
132
|
+
this.headers = /** !Map<string, string>*/ new Map()
|
|
126
133
|
for (let header in headers) {
|
|
127
|
-
this.headers.set(header.toLowerCase(), headers[header])
|
|
134
|
+
this.headers.set(header.toLowerCase(), headers[header])
|
|
128
135
|
}
|
|
129
136
|
}
|
|
130
137
|
|
|
131
138
|
/** @override */
|
|
132
139
|
toString() {
|
|
133
|
-
let ret = `HTTP/1.1 ${this.status}\n${headersToString(this.headers)}\n\n
|
|
140
|
+
let ret = `HTTP/1.1 ${this.status}\n${headersToString(this.headers)}\n\n`
|
|
134
141
|
if (this.body) {
|
|
135
|
-
ret += this.body
|
|
142
|
+
ret += this.body
|
|
136
143
|
}
|
|
137
|
-
return ret
|
|
144
|
+
return ret
|
|
138
145
|
}
|
|
139
146
|
}
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
const DEV_ROOT = '../../../../buck-out/gen/javascript/';
|
|
143
|
-
|
|
144
148
|
/** @enum {!Function} */
|
|
145
149
|
const Atom = {
|
|
146
150
|
GET_ATTRIBUTE: getAttribute,
|
|
147
|
-
IS_DISPLAYED: isDisplayed
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const LOG = logging.getLogger('webdriver.http');
|
|
152
|
-
|
|
151
|
+
IS_DISPLAYED: isDisplayed,
|
|
152
|
+
FIND_ELEMENTS: findElements,
|
|
153
|
+
}
|
|
153
154
|
|
|
154
|
-
|
|
155
|
-
function del(path) { return resource('DELETE', path); }
|
|
156
|
-
function get(path) { return resource('GET', path); }
|
|
157
|
-
function resource(method, path) { return {method: method, path: path}; }
|
|
155
|
+
const LOG = logging.getLogger('webdriver.http')
|
|
158
156
|
|
|
157
|
+
function post(path) {
|
|
158
|
+
return resource('POST', path)
|
|
159
|
+
}
|
|
160
|
+
function del(path) {
|
|
161
|
+
return resource('DELETE', path)
|
|
162
|
+
}
|
|
163
|
+
function get(path) {
|
|
164
|
+
return resource('GET', path)
|
|
165
|
+
}
|
|
166
|
+
function resource(method, path) {
|
|
167
|
+
return { method: method, path: path }
|
|
168
|
+
}
|
|
159
169
|
|
|
160
170
|
/** @typedef {{method: string, path: string}} */
|
|
161
|
-
var CommandSpec
|
|
162
|
-
|
|
171
|
+
var CommandSpec // eslint-disable-line
|
|
163
172
|
|
|
164
173
|
/** @typedef {function(!cmd.Command): !cmd.Command} */
|
|
165
|
-
var CommandTransformer
|
|
166
|
-
|
|
174
|
+
var CommandTransformer // eslint-disable-line
|
|
167
175
|
|
|
168
176
|
class InternalTypeError extends TypeError {}
|
|
169
177
|
|
|
170
|
-
|
|
171
178
|
/**
|
|
172
179
|
* @param {!cmd.Command} command The initial command.
|
|
173
180
|
* @param {Atom} atom The name of the atom to execute.
|
|
@@ -175,94 +182,151 @@ class InternalTypeError extends TypeError {}
|
|
|
175
182
|
*/
|
|
176
183
|
function toExecuteAtomCommand(command, atom, ...params) {
|
|
177
184
|
if (typeof atom !== 'function') {
|
|
178
|
-
throw new InternalTypeError('atom is not a function: ' + typeof atom)
|
|
185
|
+
throw new InternalTypeError('atom is not a function: ' + typeof atom)
|
|
179
186
|
}
|
|
180
187
|
|
|
181
188
|
return new cmd.Command(cmd.Name.EXECUTE_SCRIPT)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
189
|
+
.setParameter('sessionId', command.getParameter('sessionId'))
|
|
190
|
+
.setParameter('script', `return (${atom}).apply(null, arguments)`)
|
|
191
|
+
.setParameter(
|
|
192
|
+
'args',
|
|
193
|
+
params.map((param) => command.getParameter(param))
|
|
194
|
+
)
|
|
185
195
|
}
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
189
197
|
/** @const {!Map<string, CommandSpec>} */
|
|
190
198
|
const COMMAND_MAP = new Map([
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
]
|
|
265
|
-
|
|
199
|
+
[cmd.Name.GET_SERVER_STATUS, get('/status')],
|
|
200
|
+
[cmd.Name.NEW_SESSION, post('/session')],
|
|
201
|
+
[cmd.Name.GET_SESSIONS, get('/sessions')],
|
|
202
|
+
[cmd.Name.QUIT, del('/session/:sessionId')],
|
|
203
|
+
[cmd.Name.CLOSE, del('/session/:sessionId/window')],
|
|
204
|
+
[
|
|
205
|
+
cmd.Name.GET_CURRENT_WINDOW_HANDLE,
|
|
206
|
+
get('/session/:sessionId/window_handle'),
|
|
207
|
+
],
|
|
208
|
+
[cmd.Name.GET_WINDOW_HANDLES, get('/session/:sessionId/window_handles')],
|
|
209
|
+
[cmd.Name.GET_CURRENT_URL, get('/session/:sessionId/url')],
|
|
210
|
+
[cmd.Name.GET, post('/session/:sessionId/url')],
|
|
211
|
+
[cmd.Name.GO_BACK, post('/session/:sessionId/back')],
|
|
212
|
+
[cmd.Name.GO_FORWARD, post('/session/:sessionId/forward')],
|
|
213
|
+
[cmd.Name.REFRESH, post('/session/:sessionId/refresh')],
|
|
214
|
+
[cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
|
|
215
|
+
[cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
|
|
216
|
+
[cmd.Name.DELETE_ALL_COOKIES, del('/session/:sessionId/cookie')],
|
|
217
|
+
[cmd.Name.DELETE_COOKIE, del('/session/:sessionId/cookie/:name')],
|
|
218
|
+
[cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
|
|
219
|
+
[cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
|
|
220
|
+
[cmd.Name.GET_ACTIVE_ELEMENT, post('/session/:sessionId/element/active')],
|
|
221
|
+
[
|
|
222
|
+
cmd.Name.FIND_CHILD_ELEMENT,
|
|
223
|
+
post('/session/:sessionId/element/:id/element'),
|
|
224
|
+
],
|
|
225
|
+
[
|
|
226
|
+
cmd.Name.FIND_CHILD_ELEMENTS,
|
|
227
|
+
post('/session/:sessionId/element/:id/elements'),
|
|
228
|
+
],
|
|
229
|
+
[cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
|
|
230
|
+
[cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
|
|
231
|
+
[
|
|
232
|
+
cmd.Name.SEND_KEYS_TO_ELEMENT,
|
|
233
|
+
post('/session/:sessionId/element/:id/value'),
|
|
234
|
+
],
|
|
235
|
+
[cmd.Name.SUBMIT_ELEMENT, post('/session/:sessionId/element/:id/submit')],
|
|
236
|
+
[cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
|
|
237
|
+
[cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
|
|
238
|
+
[
|
|
239
|
+
cmd.Name.IS_ELEMENT_SELECTED,
|
|
240
|
+
get('/session/:sessionId/element/:id/selected'),
|
|
241
|
+
],
|
|
242
|
+
[cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
|
|
243
|
+
[
|
|
244
|
+
cmd.Name.IS_ELEMENT_DISPLAYED,
|
|
245
|
+
get('/session/:sessionId/element/:id/displayed'),
|
|
246
|
+
],
|
|
247
|
+
[
|
|
248
|
+
cmd.Name.GET_ELEMENT_LOCATION,
|
|
249
|
+
get('/session/:sessionId/element/:id/location'),
|
|
250
|
+
],
|
|
251
|
+
[cmd.Name.GET_ELEMENT_SIZE, get('/session/:sessionId/element/:id/size')],
|
|
252
|
+
[
|
|
253
|
+
cmd.Name.GET_ELEMENT_ATTRIBUTE,
|
|
254
|
+
get('/session/:sessionId/element/:id/attribute/:name'),
|
|
255
|
+
],
|
|
256
|
+
[
|
|
257
|
+
cmd.Name.GET_ELEMENT_PROPERTY,
|
|
258
|
+
get('/session/:sessionId/element/:id/property/:name'),
|
|
259
|
+
],
|
|
260
|
+
[
|
|
261
|
+
cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
|
|
262
|
+
get('/session/:sessionId/element/:id/css/:propertyName'),
|
|
263
|
+
],
|
|
264
|
+
[
|
|
265
|
+
cmd.Name.TAKE_ELEMENT_SCREENSHOT,
|
|
266
|
+
get('/session/:sessionId/element/:id/screenshot'),
|
|
267
|
+
],
|
|
268
|
+
[cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
|
|
269
|
+
[
|
|
270
|
+
cmd.Name.MAXIMIZE_WINDOW,
|
|
271
|
+
post('/session/:sessionId/window/current/maximize'),
|
|
272
|
+
],
|
|
273
|
+
[
|
|
274
|
+
cmd.Name.GET_WINDOW_POSITION,
|
|
275
|
+
get('/session/:sessionId/window/current/position'),
|
|
276
|
+
],
|
|
277
|
+
[
|
|
278
|
+
cmd.Name.SET_WINDOW_POSITION,
|
|
279
|
+
post('/session/:sessionId/window/current/position'),
|
|
280
|
+
],
|
|
281
|
+
[cmd.Name.GET_WINDOW_SIZE, get('/session/:sessionId/window/current/size')],
|
|
282
|
+
[cmd.Name.SET_WINDOW_SIZE, post('/session/:sessionId/window/current/size')],
|
|
283
|
+
[cmd.Name.SWITCH_TO_FRAME, post('/session/:sessionId/frame')],
|
|
284
|
+
[cmd.Name.SWITCH_TO_FRAME_PARENT, post('/session/:sessionId/frame/parent')],
|
|
285
|
+
[cmd.Name.GET_PAGE_SOURCE, get('/session/:sessionId/source')],
|
|
286
|
+
[cmd.Name.GET_TITLE, get('/session/:sessionId/title')],
|
|
287
|
+
[cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute')],
|
|
288
|
+
[cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute_async')],
|
|
289
|
+
[cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
|
|
290
|
+
[cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
|
|
291
|
+
[cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
|
|
292
|
+
[cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/accept_alert')],
|
|
293
|
+
[cmd.Name.DISMISS_ALERT, post('/session/:sessionId/dismiss_alert')],
|
|
294
|
+
[cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert_text')],
|
|
295
|
+
[cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert_text')],
|
|
296
|
+
[cmd.Name.GET_LOG, post('/session/:sessionId/log')],
|
|
297
|
+
[cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/log/types')],
|
|
298
|
+
[cmd.Name.GET_SESSION_LOGS, post('/logs')],
|
|
299
|
+
[cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
|
|
300
|
+
[cmd.Name.LEGACY_ACTION_CLICK, post('/session/:sessionId/click')],
|
|
301
|
+
[
|
|
302
|
+
cmd.Name.LEGACY_ACTION_DOUBLE_CLICK,
|
|
303
|
+
post('/session/:sessionId/doubleclick'),
|
|
304
|
+
],
|
|
305
|
+
[cmd.Name.LEGACY_ACTION_MOUSE_DOWN, post('/session/:sessionId/buttondown')],
|
|
306
|
+
[cmd.Name.LEGACY_ACTION_MOUSE_UP, post('/session/:sessionId/buttonup')],
|
|
307
|
+
[cmd.Name.LEGACY_ACTION_MOUSE_MOVE, post('/session/:sessionId/moveto')],
|
|
308
|
+
[cmd.Name.LEGACY_ACTION_SEND_KEYS, post('/session/:sessionId/keys')],
|
|
309
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_DOWN, post('/session/:sessionId/touch/down')],
|
|
310
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_UP, post('/session/:sessionId/touch/up')],
|
|
311
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_MOVE, post('/session/:sessionId/touch/move')],
|
|
312
|
+
[
|
|
313
|
+
cmd.Name.LEGACY_ACTION_TOUCH_SCROLL,
|
|
314
|
+
post('/session/:sessionId/touch/scroll'),
|
|
315
|
+
],
|
|
316
|
+
[
|
|
317
|
+
cmd.Name.LEGACY_ACTION_TOUCH_LONG_PRESS,
|
|
318
|
+
post('/session/:sessionId/touch/longclick'),
|
|
319
|
+
],
|
|
320
|
+
[cmd.Name.LEGACY_ACTION_TOUCH_FLICK, post('/session/:sessionId/touch/flick')],
|
|
321
|
+
[
|
|
322
|
+
cmd.Name.LEGACY_ACTION_TOUCH_SINGLE_TAP,
|
|
323
|
+
post('/session/:sessionId/touch/click'),
|
|
324
|
+
],
|
|
325
|
+
[
|
|
326
|
+
cmd.Name.LEGACY_ACTION_TOUCH_DOUBLE_TAP,
|
|
327
|
+
post('/session/:sessionId/touch/doubleclick'),
|
|
328
|
+
],
|
|
329
|
+
])
|
|
266
330
|
|
|
267
331
|
/** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
|
|
268
332
|
const W3C_COMMAND_MAP = new Map([
|
|
@@ -306,24 +370,51 @@ const W3C_COMMAND_MAP = new Map([
|
|
|
306
370
|
[cmd.Name.GET_ACTIVE_ELEMENT, get('/session/:sessionId/element/active')],
|
|
307
371
|
[cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
|
|
308
372
|
[cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
|
|
309
|
-
[
|
|
310
|
-
|
|
373
|
+
[
|
|
374
|
+
cmd.Name.FIND_ELEMENTS_RELATIVE,
|
|
375
|
+
(cmd) => {
|
|
376
|
+
return toExecuteAtomCommand(cmd, Atom.FIND_ELEMENTS, 'args')
|
|
377
|
+
},
|
|
378
|
+
],
|
|
379
|
+
[
|
|
380
|
+
cmd.Name.FIND_CHILD_ELEMENT,
|
|
381
|
+
post('/session/:sessionId/element/:id/element'),
|
|
382
|
+
],
|
|
383
|
+
[
|
|
384
|
+
cmd.Name.FIND_CHILD_ELEMENTS,
|
|
385
|
+
post('/session/:sessionId/element/:id/elements'),
|
|
386
|
+
],
|
|
311
387
|
// Element interaction.
|
|
312
388
|
[cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
|
|
313
|
-
[
|
|
314
|
-
|
|
389
|
+
[
|
|
390
|
+
cmd.Name.GET_ELEMENT_PROPERTY,
|
|
391
|
+
get('/session/:sessionId/element/:id/property/:name'),
|
|
392
|
+
],
|
|
393
|
+
[
|
|
394
|
+
cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
|
|
395
|
+
get('/session/:sessionId/element/:id/css/:propertyName'),
|
|
396
|
+
],
|
|
315
397
|
[cmd.Name.GET_ELEMENT_RECT, get('/session/:sessionId/element/:id/rect')],
|
|
316
398
|
[cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
|
|
317
399
|
[cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
|
|
318
|
-
[
|
|
400
|
+
[
|
|
401
|
+
cmd.Name.SEND_KEYS_TO_ELEMENT,
|
|
402
|
+
post('/session/:sessionId/element/:id/value'),
|
|
403
|
+
],
|
|
319
404
|
[cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
|
|
320
405
|
[cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
|
|
321
|
-
[
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
406
|
+
[
|
|
407
|
+
cmd.Name.GET_ELEMENT_ATTRIBUTE,
|
|
408
|
+
(cmd) => {
|
|
409
|
+
return toExecuteAtomCommand(cmd, Atom.GET_ATTRIBUTE, 'id', 'name')
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
[
|
|
413
|
+
cmd.Name.IS_ELEMENT_DISPLAYED,
|
|
414
|
+
(cmd) => {
|
|
415
|
+
return toExecuteAtomCommand(cmd, Atom.IS_DISPLAYED, 'id')
|
|
416
|
+
},
|
|
417
|
+
],
|
|
327
418
|
// Cookie management.
|
|
328
419
|
[cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
|
|
329
420
|
[cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
|
|
@@ -337,12 +428,16 @@ const W3C_COMMAND_MAP = new Map([
|
|
|
337
428
|
[cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert/text')],
|
|
338
429
|
// Screenshots.
|
|
339
430
|
[cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
|
|
340
|
-
[
|
|
431
|
+
[
|
|
432
|
+
cmd.Name.TAKE_ELEMENT_SCREENSHOT,
|
|
433
|
+
get('/session/:sessionId/element/:id/screenshot'),
|
|
434
|
+
],
|
|
435
|
+
// print page.
|
|
436
|
+
[cmd.Name.PRINT_PAGE, post('/session/:sessionId/print')],
|
|
341
437
|
// Log extensions.
|
|
342
438
|
[cmd.Name.GET_LOG, post('/session/:sessionId/se/log')],
|
|
343
439
|
[cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/se/log/types')],
|
|
344
|
-
])
|
|
345
|
-
|
|
440
|
+
])
|
|
346
441
|
|
|
347
442
|
/**
|
|
348
443
|
* Handles sending HTTP messages to a remote end.
|
|
@@ -350,7 +445,6 @@ const W3C_COMMAND_MAP = new Map([
|
|
|
350
445
|
* @interface
|
|
351
446
|
*/
|
|
352
447
|
class Client {
|
|
353
|
-
|
|
354
448
|
/**
|
|
355
449
|
* Sends a request to the server. The client will automatically follow any
|
|
356
450
|
* redirects returned by the server, fulfilling the returned promise with the
|
|
@@ -360,11 +454,9 @@ class Client {
|
|
|
360
454
|
* @return {!Promise<Response>} A promise that will be fulfilled with the
|
|
361
455
|
* server's response.
|
|
362
456
|
*/
|
|
363
|
-
send(httpRequest) {}
|
|
457
|
+
send(httpRequest) {} // eslint-disable-line
|
|
364
458
|
}
|
|
365
459
|
|
|
366
|
-
|
|
367
|
-
|
|
368
460
|
/**
|
|
369
461
|
* @param {Map<string, CommandSpec>} customCommands
|
|
370
462
|
* A map of custom command definitions.
|
|
@@ -374,46 +466,44 @@ class Client {
|
|
|
374
466
|
* command to execute.
|
|
375
467
|
*/
|
|
376
468
|
function buildRequest(customCommands, w3c, command) {
|
|
377
|
-
LOG.finest(() => `Translating command: ${command.getName()}`)
|
|
378
|
-
let spec = customCommands && customCommands.get(command.getName())
|
|
469
|
+
LOG.finest(() => `Translating command: ${command.getName()}`)
|
|
470
|
+
let spec = customCommands && customCommands.get(command.getName())
|
|
379
471
|
if (spec) {
|
|
380
|
-
return toHttpRequest(spec)
|
|
472
|
+
return toHttpRequest(spec)
|
|
381
473
|
}
|
|
382
474
|
|
|
383
475
|
if (w3c) {
|
|
384
|
-
spec = W3C_COMMAND_MAP.get(command.getName())
|
|
476
|
+
spec = W3C_COMMAND_MAP.get(command.getName())
|
|
385
477
|
if (typeof spec === 'function') {
|
|
386
|
-
LOG.finest(() => `Transforming command for W3C: ${command.getName()}`)
|
|
387
|
-
let newCommand = spec(command)
|
|
388
|
-
return buildRequest(customCommands, w3c, newCommand)
|
|
478
|
+
LOG.finest(() => `Transforming command for W3C: ${command.getName()}`)
|
|
479
|
+
let newCommand = spec(command)
|
|
480
|
+
return buildRequest(customCommands, w3c, newCommand)
|
|
389
481
|
} else if (spec) {
|
|
390
|
-
return toHttpRequest(spec)
|
|
482
|
+
return toHttpRequest(spec)
|
|
391
483
|
}
|
|
392
484
|
}
|
|
393
485
|
|
|
394
|
-
spec = COMMAND_MAP.get(command.getName())
|
|
486
|
+
spec = COMMAND_MAP.get(command.getName())
|
|
395
487
|
if (spec) {
|
|
396
|
-
return toHttpRequest(spec)
|
|
488
|
+
return toHttpRequest(spec)
|
|
397
489
|
}
|
|
398
490
|
throw new error.UnknownCommandError(
|
|
399
|
-
|
|
491
|
+
'Unrecognized command: ' + command.getName()
|
|
492
|
+
)
|
|
400
493
|
|
|
401
494
|
/**
|
|
402
495
|
* @param {CommandSpec} resource
|
|
403
496
|
* @return {!Request}
|
|
404
497
|
*/
|
|
405
498
|
function toHttpRequest(resource) {
|
|
406
|
-
LOG.finest(() => `Building HTTP request: ${JSON.stringify(resource)}`)
|
|
407
|
-
let parameters = command.getParameters()
|
|
408
|
-
let path = buildPath(resource.path, parameters)
|
|
409
|
-
return new Request(resource.method, path, parameters)
|
|
499
|
+
LOG.finest(() => `Building HTTP request: ${JSON.stringify(resource)}`)
|
|
500
|
+
let parameters = command.getParameters()
|
|
501
|
+
let path = buildPath(resource.path, parameters)
|
|
502
|
+
return new Request(resource.method, path, parameters)
|
|
410
503
|
}
|
|
411
504
|
}
|
|
412
505
|
|
|
413
|
-
|
|
414
|
-
const CLIENTS =
|
|
415
|
-
/** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */new WeakMap;
|
|
416
|
-
|
|
506
|
+
const CLIENTS = /** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */ new WeakMap()
|
|
417
507
|
|
|
418
508
|
/**
|
|
419
509
|
* A command executor that communicates with the server using JSON over HTTP.
|
|
@@ -435,7 +525,7 @@ class Executor {
|
|
|
435
525
|
* to the client.
|
|
436
526
|
*/
|
|
437
527
|
constructor(client) {
|
|
438
|
-
CLIENTS.set(this, client)
|
|
528
|
+
CLIENTS.set(this, client)
|
|
439
529
|
|
|
440
530
|
/**
|
|
441
531
|
* Whether this executor should use the W3C wire protocol. The executor
|
|
@@ -444,13 +534,13 @@ class Executor {
|
|
|
444
534
|
* `true` to force the executor into W3C mode.
|
|
445
535
|
* @type {boolean}
|
|
446
536
|
*/
|
|
447
|
-
this.w3c = false
|
|
537
|
+
this.w3c = false
|
|
448
538
|
|
|
449
539
|
/** @private {Map<string, CommandSpec>} */
|
|
450
|
-
this.customCommands_ = null
|
|
540
|
+
this.customCommands_ = null
|
|
451
541
|
|
|
452
542
|
/** @private {!logging.Logger} */
|
|
453
|
-
this.log_ = logging.getLogger('webdriver.http.Executor')
|
|
543
|
+
this.log_ = logging.getLogger('webdriver.http.Executor')
|
|
454
544
|
}
|
|
455
545
|
|
|
456
546
|
/**
|
|
@@ -468,64 +558,65 @@ class Executor {
|
|
|
468
558
|
*/
|
|
469
559
|
defineCommand(name, method, path) {
|
|
470
560
|
if (!this.customCommands_) {
|
|
471
|
-
this.customCommands_ = new Map
|
|
561
|
+
this.customCommands_ = new Map()
|
|
472
562
|
}
|
|
473
|
-
this.customCommands_.set(name, {method, path})
|
|
563
|
+
this.customCommands_.set(name, { method, path })
|
|
474
564
|
}
|
|
475
565
|
|
|
476
566
|
/** @override */
|
|
477
567
|
async execute(command) {
|
|
478
|
-
let request = buildRequest(this.customCommands_, this.w3c, command)
|
|
479
|
-
this.log_.finer(() => `>>> ${request.method} ${request.path}`)
|
|
568
|
+
let request = buildRequest(this.customCommands_, this.w3c, command)
|
|
569
|
+
this.log_.finer(() => `>>> ${request.method} ${request.path}`)
|
|
480
570
|
|
|
481
|
-
let client = CLIENTS.get(this)
|
|
571
|
+
let client = CLIENTS.get(this)
|
|
482
572
|
if (promise.isPromise(client)) {
|
|
483
|
-
client = await client
|
|
484
|
-
CLIENTS.set(this, client)
|
|
573
|
+
client = await client
|
|
574
|
+
CLIENTS.set(this, client)
|
|
485
575
|
}
|
|
486
576
|
|
|
487
|
-
let response = await client.send(request)
|
|
488
|
-
this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`)
|
|
577
|
+
let response = await client.send(request)
|
|
578
|
+
this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`)
|
|
489
579
|
|
|
490
|
-
let httpResponse = /** @type {!Response} */(response)
|
|
491
|
-
let {isW3C, value} = parseHttpResponse(command, httpResponse)
|
|
580
|
+
let httpResponse = /** @type {!Response} */ (response)
|
|
581
|
+
let { isW3C, value } = parseHttpResponse(command, httpResponse)
|
|
492
582
|
|
|
493
583
|
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
494
584
|
if (!value || !value.sessionId) {
|
|
495
585
|
throw new error.WebDriverError(
|
|
496
|
-
|
|
586
|
+
`Unable to parse new session response: ${response.body}`
|
|
587
|
+
)
|
|
497
588
|
}
|
|
498
589
|
|
|
499
590
|
// The remote end is a W3C compliant server if there is no `status`
|
|
500
591
|
// field in the response.
|
|
501
592
|
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
502
|
-
this.w3c = this.w3c || isW3C
|
|
593
|
+
this.w3c = this.w3c || isW3C
|
|
503
594
|
}
|
|
504
595
|
|
|
505
596
|
// No implementations use the `capabilities` key yet...
|
|
506
|
-
let capabilities = value.capabilities || value.value
|
|
597
|
+
let capabilities = value.capabilities || value.value
|
|
507
598
|
return new Session(
|
|
508
|
-
|
|
599
|
+
/** @type {{sessionId: string}} */ (value).sessionId,
|
|
600
|
+
capabilities
|
|
601
|
+
)
|
|
509
602
|
}
|
|
510
603
|
|
|
511
|
-
return typeof value === 'undefined' ? null : value
|
|
604
|
+
return typeof value === 'undefined' ? null : value
|
|
512
605
|
}
|
|
513
606
|
}
|
|
514
607
|
|
|
515
|
-
|
|
516
608
|
/**
|
|
517
609
|
* @param {string} str .
|
|
518
610
|
* @return {?} .
|
|
519
611
|
*/
|
|
520
612
|
function tryParse(str) {
|
|
521
613
|
try {
|
|
522
|
-
return JSON.parse(str)
|
|
614
|
+
return JSON.parse(str)
|
|
523
615
|
} catch (ignored) {
|
|
524
616
|
// Do nothing.
|
|
525
617
|
}
|
|
526
618
|
}
|
|
527
619
|
|
|
528
|
-
|
|
529
620
|
/**
|
|
530
621
|
* Callback used to parse {@link Response} objects from a
|
|
531
622
|
* {@link HttpClient}.
|
|
@@ -541,51 +632,49 @@ function tryParse(str) {
|
|
|
541
632
|
function parseHttpResponse(command, httpResponse) {
|
|
542
633
|
if (httpResponse.status < 200) {
|
|
543
634
|
// This should never happen, but throw the raw response so users report it.
|
|
544
|
-
throw new error.WebDriverError(
|
|
545
|
-
`Unexpected HTTP response:\n${httpResponse}`);
|
|
635
|
+
throw new error.WebDriverError(`Unexpected HTTP response:\n${httpResponse}`)
|
|
546
636
|
}
|
|
547
637
|
|
|
548
|
-
let parsed = tryParse(httpResponse.body)
|
|
638
|
+
let parsed = tryParse(httpResponse.body)
|
|
549
639
|
if (parsed && typeof parsed === 'object') {
|
|
550
|
-
let value = parsed.value
|
|
640
|
+
let value = parsed.value
|
|
551
641
|
let isW3C =
|
|
552
|
-
|
|
553
|
-
|
|
642
|
+
value !== null &&
|
|
643
|
+
typeof value === 'object' &&
|
|
644
|
+
typeof parsed.status === 'undefined'
|
|
554
645
|
|
|
555
646
|
if (!isW3C) {
|
|
556
|
-
error.checkLegacyResponse(parsed)
|
|
647
|
+
error.checkLegacyResponse(parsed)
|
|
557
648
|
|
|
558
649
|
// Adjust legacy new session responses to look like W3C to simplify
|
|
559
650
|
// later processing.
|
|
560
651
|
if (command.getName() === cmd.Name.NEW_SESSION) {
|
|
561
|
-
value = parsed
|
|
652
|
+
value = parsed
|
|
562
653
|
}
|
|
563
|
-
|
|
564
654
|
} else if (httpResponse.status > 399) {
|
|
565
|
-
error.throwDecodedError(value)
|
|
655
|
+
error.throwDecodedError(value)
|
|
566
656
|
}
|
|
567
657
|
|
|
568
|
-
return {isW3C, value}
|
|
658
|
+
return { isW3C, value }
|
|
569
659
|
}
|
|
570
660
|
|
|
571
661
|
if (parsed !== undefined) {
|
|
572
|
-
return {isW3C: false, value: parsed}
|
|
662
|
+
return { isW3C: false, value: parsed }
|
|
573
663
|
}
|
|
574
664
|
|
|
575
|
-
let value = httpResponse.body.replace(/\r\n/g, '\n')
|
|
665
|
+
let value = httpResponse.body.replace(/\r\n/g, '\n')
|
|
576
666
|
|
|
577
667
|
// 404 represents an unknown command; anything else > 399 is a generic unknown
|
|
578
668
|
// error.
|
|
579
669
|
if (httpResponse.status == 404) {
|
|
580
|
-
throw new error.UnsupportedOperationError(command.getName() + ': ' + value)
|
|
670
|
+
throw new error.UnsupportedOperationError(command.getName() + ': ' + value)
|
|
581
671
|
} else if (httpResponse.status >= 400) {
|
|
582
|
-
throw new error.WebDriverError(value)
|
|
672
|
+
throw new error.WebDriverError(value)
|
|
583
673
|
}
|
|
584
674
|
|
|
585
|
-
return {isW3C: false, value: value || null}
|
|
675
|
+
return { isW3C: false, value: value || null }
|
|
586
676
|
}
|
|
587
677
|
|
|
588
|
-
|
|
589
678
|
/**
|
|
590
679
|
* Builds a fully qualified path using the given set of command parameters. Each
|
|
591
680
|
* path segment prefixed with ':' will be replaced by the value of the
|
|
@@ -596,33 +685,33 @@ function parseHttpResponse(command, httpResponse) {
|
|
|
596
685
|
* @return {string} The modified path.
|
|
597
686
|
*/
|
|
598
687
|
function buildPath(path, parameters) {
|
|
599
|
-
let pathParameters = path.match(/\/:(\w+)\b/g)
|
|
688
|
+
let pathParameters = path.match(/\/:(\w+)\b/g)
|
|
600
689
|
if (pathParameters) {
|
|
601
690
|
for (let i = 0; i < pathParameters.length; ++i) {
|
|
602
|
-
let key = pathParameters[i].substring(2)
|
|
691
|
+
let key = pathParameters[i].substring(2) // Trim the /:
|
|
603
692
|
if (key in parameters) {
|
|
604
|
-
let value = parameters[key]
|
|
693
|
+
let value = parameters[key]
|
|
605
694
|
if (WebElement.isId(value)) {
|
|
606
695
|
// When inserting a WebElement into the URL, only use its ID value,
|
|
607
696
|
// not the full JSON.
|
|
608
|
-
value = WebElement.extractId(value)
|
|
697
|
+
value = WebElement.extractId(value)
|
|
609
698
|
}
|
|
610
|
-
path = path.replace(pathParameters[i], '/' + value)
|
|
611
|
-
delete parameters[key]
|
|
699
|
+
path = path.replace(pathParameters[i], '/' + value)
|
|
700
|
+
delete parameters[key]
|
|
612
701
|
} else {
|
|
613
702
|
throw new error.InvalidArgumentError(
|
|
614
|
-
|
|
703
|
+
'Missing required parameter: ' + key
|
|
704
|
+
)
|
|
615
705
|
}
|
|
616
706
|
}
|
|
617
707
|
}
|
|
618
|
-
return path
|
|
708
|
+
return path
|
|
619
709
|
}
|
|
620
710
|
|
|
621
|
-
|
|
622
711
|
// PUBLIC API
|
|
623
712
|
|
|
624
|
-
exports.Executor = Executor
|
|
625
|
-
exports.Client = Client
|
|
626
|
-
exports.Request = Request
|
|
627
|
-
exports.Response = Response
|
|
628
|
-
exports.buildPath = buildPath
|
|
713
|
+
exports.Executor = Executor
|
|
714
|
+
exports.Client = Client
|
|
715
|
+
exports.Request = Request
|
|
716
|
+
exports.Response = Response
|
|
717
|
+
exports.buildPath = buildPath // Exported for testing.
|