Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/javascript/selenium-webdriver/test/lib/http_test.js
2885 views
1
// Licensed to the Software Freedom Conservancy (SFC) under one
2
// or more contributor license agreements. See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership. The SFC licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License. You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied. See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
'use strict'
19
20
const assert = require('node:assert'),
21
sinon = require('sinon')
22
23
const Capabilities = require('selenium-webdriver/lib/capabilities').Capabilities,
24
Command = require('selenium-webdriver/lib/command').Command,
25
CommandName = require('selenium-webdriver/lib/command').Name,
26
error = require('selenium-webdriver/lib/error'),
27
http = require('selenium-webdriver/lib/http'),
28
Session = require('selenium-webdriver/lib/session').Session,
29
WebElement = require('selenium-webdriver/lib/webdriver').WebElement
30
31
describe('http', function () {
32
describe('buildPath', function () {
33
it('properly replaces path segments with command parameters', function () {
34
const parameters = { sessionId: 'foo', url: 'http://www.google.com' }
35
const finalPath = http.buildPath('/session/:sessionId/url', parameters)
36
assert.strictEqual(finalPath, '/session/foo/url')
37
assert.deepStrictEqual(parameters, { url: 'http://www.google.com' })
38
})
39
40
it('handles web element references', function () {
41
const parameters = { sessionId: 'foo', id: WebElement.buildId('bar') }
42
43
const finalPath = http.buildPath('/session/:sessionId/element/:id/click', parameters)
44
assert.strictEqual(finalPath, '/session/foo/element/bar/click')
45
assert.deepStrictEqual(parameters, {})
46
})
47
48
it('throws if missing a parameter', function () {
49
assert.throws(
50
() => http.buildPath('/session/:sessionId', {}),
51
function (err) {
52
return err instanceof error.InvalidArgumentError && 'Missing required parameter: sessionId' === err.message
53
},
54
)
55
56
assert.throws(
57
() =>
58
http.buildPath('/session/:sessionId/element/:id', {
59
sessionId: 'foo',
60
}),
61
function (err) {
62
return err instanceof error.InvalidArgumentError && 'Missing required parameter: id' === err.message
63
},
64
)
65
})
66
67
it('does not match on segments that do not start with a colon', function () {
68
assert.strictEqual(http.buildPath('/session/foo:bar/baz', {}), '/session/foo:bar/baz')
69
})
70
})
71
72
describe('Executor', function () {
73
let executor
74
let client
75
let send
76
77
beforeEach(function setUp() {
78
client = new http.Client()
79
send = sinon.stub(client, 'send')
80
executor = new http.Executor(client)
81
})
82
83
describe('command routing', function () {
84
it('rejects unrecognized commands', function () {
85
return executor.execute(new Command('fake-name')).then(assert.fail, (err) => {
86
if (err instanceof error.UnknownCommandError && 'Unrecognized command: fake-name' === err.message) {
87
return
88
}
89
throw err
90
})
91
})
92
93
it('rejects promise if client fails to send request', function () {
94
let error = new Error('boom')
95
send.returns(Promise.reject(error))
96
return assertFailsToSend(new Command(CommandName.NEW_SESSION)).then(function (e) {
97
assert.strictEqual(error, e)
98
assertSent('POST', '/session', {}, [['Accept', 'application/json; charset=utf-8']])
99
})
100
})
101
102
it('can execute commands with no URL parameters', function () {
103
const resp = JSON.stringify({ sessionId: 'abc123' })
104
send.returns(Promise.resolve(new http.Response(200, {}, resp)))
105
106
let command = new Command(CommandName.NEW_SESSION)
107
return assertSendsSuccessfully(command).then(function (_response) {
108
assertSent('POST', '/session', {}, [['Accept', 'application/json; charset=utf-8']])
109
})
110
})
111
112
it('rejects commands missing URL parameters', async function () {
113
let command = new Command(CommandName.FIND_CHILD_ELEMENT)
114
.setParameter('sessionId', 's123')
115
// Let this be missing: setParameter('id', {'ELEMENT': 'e456'}).
116
.setParameter('using', 'id')
117
.setParameter('value', 'foo')
118
119
try {
120
await executor.execute(command)
121
return Promise.reject(Error('should have thrown'))
122
} catch (err) {
123
assert.strictEqual(err.constructor, error.InvalidArgumentError)
124
assert.strictEqual(err.message, 'Missing required parameter: id')
125
}
126
assert.ok(!send.called)
127
})
128
129
it('replaces URL parameters with command parameters', function () {
130
const command = new Command(CommandName.GET)
131
.setParameter('sessionId', 's123')
132
.setParameter('url', 'http://www.google.com')
133
134
send.returns(Promise.resolve(new http.Response(200, {}, '')))
135
136
return assertSendsSuccessfully(command).then(function (_response) {
137
assertSent('POST', '/session/s123/url', { url: 'http://www.google.com' }, [
138
['Accept', 'application/json; charset=utf-8'],
139
])
140
})
141
})
142
143
describe('uses correct URL', function () {
144
beforeEach(() => (executor = new http.Executor(client)))
145
146
describe('in W3C mode', function () {
147
test(CommandName.MAXIMIZE_WINDOW, { sessionId: 's123' }, true, 'POST', '/session/s123/window/maximize')
148
149
// This is consistent b/w legacy and W3C, just making sure.
150
test(
151
CommandName.GET,
152
{ sessionId: 's123', url: 'http://www.example.com' },
153
true,
154
'POST',
155
'/session/s123/url',
156
{ url: 'http://www.example.com' },
157
)
158
})
159
160
function test(command, parameters, w3c, expectedMethod, expectedUrl, opt_expectedParams) {
161
it(`command=${command}`, function () {
162
const resp = JSON.stringify({ sessionId: 'abc123' })
163
send.returns(Promise.resolve(new http.Response(200, {}, resp)))
164
165
let cmd = new Command(command).setParameters(parameters)
166
executor.w3c = w3c
167
return executor.execute(cmd).then(function () {
168
assertSent(expectedMethod, expectedUrl, opt_expectedParams || {}, [
169
['Accept', 'application/json; charset=utf-8'],
170
])
171
})
172
})
173
}
174
})
175
})
176
177
describe('response parsing', function () {
178
it('extracts value from JSON response', function () {
179
const responseObj = {
180
status: error.ErrorCode.SUCCESS,
181
value: 'http://www.google.com',
182
}
183
184
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
185
186
send.returns(Promise.resolve(new http.Response(200, {}, JSON.stringify(responseObj))))
187
188
return executor.execute(command).then(function (response) {
189
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
190
assert.strictEqual(response, 'http://www.google.com')
191
})
192
})
193
194
describe('extracts Session from NEW_SESSION response', function () {
195
beforeEach(() => (executor = new http.Executor(client)))
196
197
const command = new Command(CommandName.NEW_SESSION)
198
199
describe('fails if server returns invalid response', function () {
200
describe('(empty response)', function () {
201
test(true)
202
test(false)
203
204
function test(w3c) {
205
it('w3c === ' + w3c, function () {
206
send.returns(Promise.resolve(new http.Response(200, {}, '')))
207
executor.w3c = w3c
208
return executor.execute(command).then(
209
() => assert.fail('expected to fail'),
210
(e) => {
211
if (!e.message.startsWith('Unable to parse')) {
212
throw e
213
}
214
},
215
)
216
})
217
}
218
})
219
220
describe('(no session ID)', function () {
221
test(true)
222
test(false)
223
224
function test(w3c) {
225
it('w3c === ' + w3c, function () {
226
let resp = { value: { name: 'Bob' } }
227
send.returns(Promise.resolve(new http.Response(200, {}, JSON.stringify(resp))))
228
executor.w3c = w3c
229
return executor.execute(command).then(
230
() => assert.fail('expected to fail'),
231
(e) => {
232
if (!e.message.startsWith('Unable to parse')) {
233
throw e
234
}
235
},
236
)
237
})
238
}
239
})
240
})
241
242
it('handles legacy response', function () {
243
const rawResponse = {
244
sessionId: 's123',
245
status: 0,
246
value: { name: 'Bob' },
247
}
248
249
send.returns(Promise.resolve(new http.Response(200, {}, JSON.stringify(rawResponse))))
250
251
assert.ok(!executor.w3c)
252
return executor.execute(command).then(function (response) {
253
assert.ok(response instanceof Session)
254
assert.strictEqual(response.getId(), 's123')
255
256
let caps = response.getCapabilities()
257
assert.ok(caps instanceof Capabilities)
258
assert.strictEqual(caps.get('name'), 'Bob')
259
260
assert.ok(!executor.w3c)
261
})
262
})
263
264
it('auto-upgrades on W3C response', function () {
265
let rawResponse = {
266
value: {
267
sessionId: 's123',
268
value: {
269
name: 'Bob',
270
},
271
},
272
}
273
274
send.returns(Promise.resolve(new http.Response(200, {}, JSON.stringify(rawResponse))))
275
276
assert.ok(!executor.w3c)
277
return executor.execute(command).then(function (response) {
278
assert.ok(response instanceof Session)
279
assert.strictEqual(response.getId(), 's123')
280
281
let caps = response.getCapabilities()
282
assert.ok(caps instanceof Capabilities)
283
assert.strictEqual(caps.get('name'), 'Bob')
284
285
assert.ok(executor.w3c)
286
})
287
})
288
289
it('if w3c, does not downgrade on legacy response', function () {
290
const rawResponse = { sessionId: 's123', status: 0, value: null }
291
292
send.returns(Promise.resolve(new http.Response(200, {}, JSON.stringify(rawResponse))))
293
294
executor.w3c = true
295
return executor.execute(command).then(function (response) {
296
assert.ok(response instanceof Session)
297
assert.strictEqual(response.getId(), 's123')
298
assert.strictEqual(response.getCapabilities().size, 0)
299
assert.ok(executor.w3c, 'should never downgrade')
300
})
301
})
302
303
it('handles legacy new session failures', function () {
304
let rawResponse = {
305
status: error.ErrorCode.NO_SUCH_ELEMENT,
306
value: { message: 'hi' },
307
}
308
309
send.returns(Promise.resolve(new http.Response(500, {}, JSON.stringify(rawResponse))))
310
311
return executor.execute(command).then(
312
() => assert.fail('should have failed'),
313
(e) => {
314
assert.ok(e instanceof error.NoSuchElementError)
315
assert.strictEqual(e.message, 'hi')
316
},
317
)
318
})
319
320
it('handles w3c new session failures', function () {
321
let rawResponse = {
322
value: { error: 'no such element', message: 'oops' },
323
}
324
325
send.returns(Promise.resolve(new http.Response(500, {}, JSON.stringify(rawResponse))))
326
327
return executor.execute(command).then(
328
() => assert.fail('should have failed'),
329
(e) => {
330
assert.ok(e instanceof error.NoSuchElementError)
331
assert.strictEqual(e.message, 'oops')
332
},
333
)
334
})
335
})
336
337
it('handles JSON null', function () {
338
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
339
340
send.returns(Promise.resolve(new http.Response(200, {}, 'null')))
341
342
return executor.execute(command).then(function (response) {
343
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
344
assert.strictEqual(response, null)
345
})
346
})
347
348
describe('falsy values', function () {
349
test(0)
350
test(false)
351
test('')
352
353
function test(value) {
354
it(`value=${value}`, function () {
355
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
356
357
send.returns(Promise.resolve(new http.Response(200, {}, JSON.stringify({ status: 0, value: value }))))
358
359
return executor.execute(command).then(function (response) {
360
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
361
assert.strictEqual(response, value)
362
})
363
})
364
}
365
})
366
367
it('handles non-object JSON', function () {
368
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
369
370
send.returns(Promise.resolve(new http.Response(200, {}, '123')))
371
372
return executor.execute(command).then(function (response) {
373
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
374
assert.strictEqual(response, 123)
375
})
376
})
377
378
it('returns body text when 2xx but not JSON', function () {
379
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
380
381
send.returns(Promise.resolve(new http.Response(200, {}, 'hello, world\r\ngoodbye, world!')))
382
383
return executor.execute(command).then(function (response) {
384
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
385
assert.strictEqual(response, 'hello, world\ngoodbye, world!')
386
})
387
})
388
389
it('returns body text when 2xx but invalid JSON', function () {
390
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
391
392
send.returns(Promise.resolve(new http.Response(200, {}, '[')))
393
394
return executor.execute(command).then(function (response) {
395
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
396
assert.strictEqual(response, '[')
397
})
398
})
399
400
it('returns null if no body text and 2xx', function () {
401
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
402
403
send.returns(Promise.resolve(new http.Response(200, {}, '')))
404
405
return executor.execute(command).then(function (response) {
406
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
407
assert.strictEqual(response, null)
408
})
409
})
410
411
it('returns normalized body text when 2xx but not JSON', function () {
412
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
413
414
send.returns(Promise.resolve(new http.Response(200, {}, '\r\n\n\n\r\n')))
415
416
return executor.execute(command).then(function (response) {
417
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
418
assert.strictEqual(response, '\n\n\n\n')
419
})
420
})
421
422
it('throws UnsupportedOperationError for 404 and body not JSON', function () {
423
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
424
425
send.returns(Promise.resolve(new http.Response(404, {}, 'hello, world\r\ngoodbye, world!')))
426
427
return executor
428
.execute(command)
429
.then(
430
() => assert.fail('should have failed'),
431
checkError(error.UnsupportedOperationError, 'getCurrentUrl: hello, world\ngoodbye, world!'),
432
)
433
})
434
435
it('throws WebDriverError for generic 4xx when body not JSON', function () {
436
const command = new Command(CommandName.GET_CURRENT_URL).setParameter('sessionId', 's123')
437
438
send.returns(Promise.resolve(new http.Response(500, {}, 'hello, world\r\ngoodbye, world!')))
439
440
return executor
441
.execute(command)
442
.then(
443
() => assert.fail('should have failed'),
444
checkError(error.WebDriverError, 'hello, world\ngoodbye, world!'),
445
)
446
.then(function () {
447
assertSent('GET', '/session/s123/url', {}, [['Accept', 'application/json; charset=utf-8']])
448
})
449
})
450
})
451
452
it('canDefineNewCommands', function () {
453
executor.defineCommand('greet', 'GET', '/person/:name')
454
455
const command = new Command('greet').setParameter('name', 'Bob')
456
457
send.returns(Promise.resolve(new http.Response(200, {}, '')))
458
459
return assertSendsSuccessfully(command).then(function (_response) {
460
assertSent('GET', '/person/Bob', {}, [['Accept', 'application/json; charset=utf-8']])
461
})
462
})
463
464
it('canRedefineStandardCommands', function () {
465
executor.defineCommand(CommandName.GO_BACK, 'POST', '/custom/back')
466
467
const command = new Command(CommandName.GO_BACK).setParameter('times', 3)
468
469
send.returns(Promise.resolve(new http.Response(200, {}, '')))
470
471
return assertSendsSuccessfully(command).then(function (_response) {
472
assertSent('POST', '/custom/back', { times: 3 }, [['Accept', 'application/json; charset=utf-8']])
473
})
474
})
475
476
it('accepts promised http clients', function () {
477
executor = new http.Executor(Promise.resolve(client))
478
479
const resp = JSON.stringify({ sessionId: 'abc123' })
480
send.returns(Promise.resolve(new http.Response(200, {}, resp)))
481
482
let command = new Command(CommandName.NEW_SESSION)
483
return executor.execute(command).then((_response) => {
484
assertSent('POST', '/session', {}, [['Accept', 'application/json; charset=utf-8']])
485
})
486
})
487
488
function entries(map) {
489
let entries = []
490
for (let e of map.entries()) {
491
entries.push(e)
492
}
493
return entries
494
}
495
496
function checkError(type, message) {
497
return function (e) {
498
if (e instanceof type) {
499
assert.strictEqual(e.message, message)
500
} else {
501
throw e
502
}
503
}
504
}
505
506
function assertSent(method, path, data, headers) {
507
assert.ok(
508
send.calledWith(
509
sinon.match(function (value) {
510
assert.strictEqual(value.method, method)
511
assert.strictEqual(value.path, path)
512
assert.deepStrictEqual(value.data, data)
513
assert.deepStrictEqual(entries(value.headers), headers)
514
return true
515
}),
516
),
517
)
518
}
519
520
function assertSendsSuccessfully(command) {
521
return executor.execute(command).then(function (response) {
522
return response
523
})
524
}
525
526
function assertFailsToSend(command, _opt_onError) {
527
return executor.execute(command).then(
528
() => {
529
throw Error('should have failed')
530
},
531
(e) => {
532
return e
533
},
534
)
535
}
536
})
537
})
538
539