selenium-webdriver 4.18.1 → 4.20.0
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 +946 -743
- package/README.md +71 -72
- package/bidi/addInterceptParameters.js +65 -45
- package/bidi/argumentValue.js +1 -0
- package/bidi/browser.js +94 -0
- package/bidi/browsingContext.js +214 -32
- package/bidi/browsingContextInspector.js +51 -0
- package/bidi/browsingContextTypes.js +31 -0
- package/bidi/captureScreenshotParameters.js +96 -0
- package/bidi/clipRectangle.js +127 -0
- package/bidi/continueRequestParameters.js +122 -0
- package/bidi/continueResponseParameters.js +129 -0
- package/bidi/cookieFilter.js +139 -0
- package/bidi/createContextParameters.js +73 -0
- package/bidi/evaluateResult.js +17 -0
- package/bidi/index.js +25 -26
- package/bidi/input.js +47 -3
- package/bidi/interceptPhase.js +5 -1
- package/bidi/logEntries.js +66 -0
- package/bidi/network.js +176 -23
- package/bidi/networkTypes.js +394 -17
- package/bidi/partialCookie.js +115 -0
- package/bidi/partitionDescriptor.js +102 -0
- package/bidi/partitionKey.js +53 -0
- package/bidi/protocolType.js +20 -0
- package/bidi/protocolValue.js +140 -9
- package/bidi/provideResponseParameters.js +123 -0
- package/bidi/realmInfo.js +27 -0
- package/bidi/resultOwnership.js +4 -0
- package/bidi/scriptManager.js +131 -5
- package/bidi/scriptTypes.js +36 -0
- package/bidi/storage.js +201 -0
- package/bidi/urlPattern.js +36 -3
- package/bin/linux/selenium-manager +0 -0
- package/bin/macos/selenium-manager +0 -0
- package/bin/windows/selenium-manager.exe +0 -0
- package/chromium.js +13 -9
- package/common/driverFinder.js +36 -4
- package/common/seleniumManager.js +7 -39
- package/devtools/CDPConnection.js +1 -0
- package/devtools/networkinterceptor.js +1 -0
- package/eslint.config.js +107 -0
- package/firefox.js +20 -6
- package/http/index.js +6 -6
- package/http/util.js +1 -0
- package/ie.js +8 -4
- package/index.js +11 -0
- package/io/exec.js +1 -1
- package/io/index.js +3 -2
- package/io/zip.js +1 -1
- package/lib/atoms/find-elements.js +26 -26
- package/lib/atoms/is-displayed.js +24 -97
- package/lib/capabilities.js +5 -5
- package/lib/http.js +8 -4
- package/lib/input.js +0 -1
- package/lib/pinnedScript.js +1 -1
- package/lib/select.js +8 -8
- package/lib/until.js +3 -3
- package/lib/util.js +1 -0
- package/lib/virtual_authenticator.js +8 -8
- package/lib/webdriver.js +8 -3
- package/net/index.js +1 -1
- package/net/portprober.js +1 -1
- package/package.json +21 -13
- package/remote/index.js +1 -1
- package/remote/util.js +2 -2
- package/safari.js +3 -3
- package/testing/index.js +17 -8
package/lib/capabilities.js
CHANGED
|
@@ -246,6 +246,11 @@ class Capabilities {
|
|
|
246
246
|
this.map_ = new Map(other)
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
/** @return {number} The number of capabilities set. */
|
|
250
|
+
get size() {
|
|
251
|
+
return this.map_.size
|
|
252
|
+
}
|
|
253
|
+
|
|
249
254
|
/**
|
|
250
255
|
* @return {!Capabilities} A basic set of capabilities for Chrome.
|
|
251
256
|
*/
|
|
@@ -315,11 +320,6 @@ class Capabilities {
|
|
|
315
320
|
return this.map_.keys()
|
|
316
321
|
}
|
|
317
322
|
|
|
318
|
-
/** @return {number} The number of capabilities set. */
|
|
319
|
-
get size() {
|
|
320
|
-
return this.map_.size
|
|
321
|
-
}
|
|
322
|
-
|
|
323
323
|
/**
|
|
324
324
|
* Merges another set of capabilities into this instance.
|
|
325
325
|
* @param {!(Capabilities|Map<String, ?>|Object<string, ?>)} other The other
|
package/lib/http.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
'use strict'
|
|
27
27
|
|
|
28
|
-
const path = require('path')
|
|
28
|
+
const path = require('node:path')
|
|
29
29
|
const cmd = require('./command')
|
|
30
30
|
const error = require('./error')
|
|
31
31
|
const logging = require('./logging')
|
|
@@ -147,21 +147,24 @@ const Atom = {
|
|
|
147
147
|
function post(path) {
|
|
148
148
|
return resource('POST', path)
|
|
149
149
|
}
|
|
150
|
+
|
|
150
151
|
function del(path) {
|
|
151
152
|
return resource('DELETE', path)
|
|
152
153
|
}
|
|
154
|
+
|
|
153
155
|
function get(path) {
|
|
154
156
|
return resource('GET', path)
|
|
155
157
|
}
|
|
158
|
+
|
|
156
159
|
function resource(method, path) {
|
|
157
160
|
return { method: method, path: path }
|
|
158
161
|
}
|
|
159
162
|
|
|
160
163
|
/** @typedef {{method: string, path: string}} */
|
|
161
|
-
var CommandSpec
|
|
164
|
+
var CommandSpec
|
|
162
165
|
|
|
163
166
|
/** @typedef {function(!cmd.Command): !cmd.Command} */
|
|
164
|
-
var CommandTransformer
|
|
167
|
+
var CommandTransformer
|
|
165
168
|
|
|
166
169
|
class InternalTypeError extends TypeError {}
|
|
167
170
|
|
|
@@ -334,7 +337,7 @@ class Client {
|
|
|
334
337
|
* @return {!Promise<Response>} A promise that will be fulfilled with the
|
|
335
338
|
* server's response.
|
|
336
339
|
*/
|
|
337
|
-
send(httpRequest) {}
|
|
340
|
+
send(httpRequest) {}
|
|
338
341
|
}
|
|
339
342
|
|
|
340
343
|
/**
|
|
@@ -469,6 +472,7 @@ class Executor {
|
|
|
469
472
|
function tryParse(str) {
|
|
470
473
|
try {
|
|
471
474
|
return JSON.parse(str)
|
|
475
|
+
/*eslint no-unused-vars: "off"*/
|
|
472
476
|
} catch (ignored) {
|
|
473
477
|
// Do nothing.
|
|
474
478
|
}
|
package/lib/input.js
CHANGED
package/lib/pinnedScript.js
CHANGED
package/lib/select.js
CHANGED
|
@@ -51,23 +51,23 @@ class ISelect {
|
|
|
51
51
|
* @return {!Promise<boolean>} Whether this select element supports selecting multiple options at the same time? This
|
|
52
52
|
* is done by checking the value of the "multiple" attribute.
|
|
53
53
|
*/
|
|
54
|
-
isMultiple() {}
|
|
54
|
+
isMultiple() {}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* @return {!Promise<!Array<!WebElement>>} All options belonging to this select tag
|
|
58
58
|
*/
|
|
59
|
-
getOptions() {}
|
|
59
|
+
getOptions() {}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* @return {!Promise<!Array<!WebElement>>} All selected options belonging to this select tag
|
|
63
63
|
*/
|
|
64
|
-
getAllSelectedOptions() {}
|
|
64
|
+
getAllSelectedOptions() {}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @return {!Promise<!WebElement>} The first selected option in this select tag (or the currently selected option in a
|
|
68
68
|
* normal select)
|
|
69
69
|
*/
|
|
70
|
-
getFirstSelectedOption() {}
|
|
70
|
+
getFirstSelectedOption() {}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Select all options that display text matching the argument. That is, when given "Bar" this
|
|
@@ -105,7 +105,7 @@ class ISelect {
|
|
|
105
105
|
*
|
|
106
106
|
* @return {Promise<void>}
|
|
107
107
|
*/
|
|
108
|
-
deselectAll() {}
|
|
108
|
+
deselectAll() {}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
111
|
* Deselect all options that display text matching the argument. That is, when given "Bar" this
|
|
@@ -161,9 +161,9 @@ class Select {
|
|
|
161
161
|
*
|
|
162
162
|
* <example>
|
|
163
163
|
<select id="selectbox">
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
<option value="1">Option 1</option>
|
|
165
|
+
<option value="2">Option 2</option>
|
|
166
|
+
<option value="3">Option 3</option>
|
|
167
167
|
</select>
|
|
168
168
|
const selectBox = await driver.findElement(By.id("selectbox"));
|
|
169
169
|
await selectObject.selectByIndex(1);
|
package/lib/until.js
CHANGED
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
|
|
44
44
|
const by = require('./by')
|
|
45
45
|
const error = require('./error')
|
|
46
|
-
const webdriver = require('./webdriver')
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const webdriver = require('./webdriver')
|
|
47
|
+
const Condition = webdriver.Condition
|
|
48
|
+
const WebElementCondition = webdriver.WebElementCondition
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Creates a condition that will wait until the input driver is able to switch
|
package/lib/util.js
CHANGED
|
@@ -38,6 +38,7 @@ function isPromise(value) {
|
|
|
38
38
|
// Use array notation so the Closure compiler does not obfuscate away our
|
|
39
39
|
// contract.
|
|
40
40
|
return (typeof value === 'object' || typeof value === 'function') && typeof value['then'] === 'function'
|
|
41
|
+
/*eslint no-unused-vars: "off"*/
|
|
41
42
|
} catch (ex) {
|
|
42
43
|
return false
|
|
43
44
|
}
|
|
@@ -128,6 +128,14 @@ class Credential {
|
|
|
128
128
|
this._signCount = signCount
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
static createResidentCredential(id, rpId, userHandle, privateKey, signCount) {
|
|
132
|
+
return new Credential(id, true, rpId, userHandle, privateKey, signCount)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static createNonResidentCredential(id, rpId, privateKey, signCount) {
|
|
136
|
+
return new Credential(id, false, rpId, null, privateKey, signCount)
|
|
137
|
+
}
|
|
138
|
+
|
|
131
139
|
id() {
|
|
132
140
|
return this._id
|
|
133
141
|
}
|
|
@@ -169,10 +177,6 @@ class Credential {
|
|
|
169
177
|
return new Credential(id, true, rpId, userHandle, privateKey, signCount)
|
|
170
178
|
}
|
|
171
179
|
|
|
172
|
-
static createResidentCredential(id, rpId, userHandle, privateKey, signCount) {
|
|
173
|
-
return new Credential(id, true, rpId, userHandle, privateKey, signCount)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
180
|
/**
|
|
177
181
|
* Creates a non-resident (i.e. stateless) credential.
|
|
178
182
|
* @param id Unique base64 encoded string.
|
|
@@ -186,10 +190,6 @@ class Credential {
|
|
|
186
190
|
return new Credential(id, false, rpId, null, privateKey, signCount)
|
|
187
191
|
}
|
|
188
192
|
|
|
189
|
-
static createNonResidentCredential(id, rpId, privateKey, signCount) {
|
|
190
|
-
return new Credential(id, false, rpId, null, privateKey, signCount)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
193
|
toDict() {
|
|
194
194
|
let credentialData = {
|
|
195
195
|
credentialId: Buffer.from(this._id).toString('base64url'),
|
package/lib/webdriver.js
CHANGED
|
@@ -32,9 +32,9 @@ const Symbols = require('./symbols')
|
|
|
32
32
|
const cdp = require('../devtools/CDPConnection')
|
|
33
33
|
const WebSocket = require('ws')
|
|
34
34
|
const http = require('../http/index')
|
|
35
|
-
const fs = require('fs')
|
|
35
|
+
const fs = require('node:fs')
|
|
36
36
|
const { Capabilities } = require('./capabilities')
|
|
37
|
-
const path = require('path')
|
|
37
|
+
const path = require('node:path')
|
|
38
38
|
const { NoSuchElementError } = require('./error')
|
|
39
39
|
const cdpTargets = ['page', 'browser']
|
|
40
40
|
const { Credential } = require('./virtual_authenticator')
|
|
@@ -461,7 +461,7 @@ class IWebDriver {
|
|
|
461
461
|
condition, // eslint-disable-line
|
|
462
462
|
timeout = undefined, // eslint-disable-line
|
|
463
463
|
message = undefined, // eslint-disable-line
|
|
464
|
-
pollTimeout = undefined // eslint-disable-line
|
|
464
|
+
pollTimeout = undefined, // eslint-disable-line
|
|
465
465
|
) {}
|
|
466
466
|
|
|
467
467
|
/**
|
|
@@ -887,6 +887,7 @@ class WebDriver {
|
|
|
887
887
|
}
|
|
888
888
|
|
|
889
889
|
const driver = this
|
|
890
|
+
|
|
890
891
|
function evaluateCondition() {
|
|
891
892
|
return new Promise((resolve, reject) => {
|
|
892
893
|
try {
|
|
@@ -1379,6 +1380,7 @@ class WebDriver {
|
|
|
1379
1380
|
null,
|
|
1380
1381
|
)
|
|
1381
1382
|
}
|
|
1383
|
+
|
|
1382
1384
|
/**
|
|
1383
1385
|
*
|
|
1384
1386
|
* @param connection
|
|
@@ -1966,6 +1968,7 @@ class Options {
|
|
|
1966
1968
|
let cmd = new command.Command(command.Name.SET_TIMEOUT)
|
|
1967
1969
|
|
|
1968
1970
|
let valid = false
|
|
1971
|
+
|
|
1969
1972
|
function setParam(key, value) {
|
|
1970
1973
|
if (value === null || typeof value === 'number') {
|
|
1971
1974
|
valid = true
|
|
@@ -1974,6 +1977,7 @@ class Options {
|
|
|
1974
1977
|
throw TypeError('invalid timeouts configuration:' + ` expected "${key}" to be a number, got ${typeof value}`)
|
|
1975
1978
|
}
|
|
1976
1979
|
}
|
|
1980
|
+
|
|
1977
1981
|
setParam('implicit', implicit)
|
|
1978
1982
|
setParam('pageLoad', pageLoad)
|
|
1979
1983
|
setParam('script', script)
|
|
@@ -2854,6 +2858,7 @@ class WebElement {
|
|
|
2854
2858
|
getAccessibleName() {
|
|
2855
2859
|
return this.execute_(new command.Command(command.Name.GET_COMPUTED_LABEL))
|
|
2856
2860
|
}
|
|
2861
|
+
|
|
2857
2862
|
/**
|
|
2858
2863
|
* Returns an object describing an element's location, in pixels relative to
|
|
2859
2864
|
* the document element, and the element's size in pixels.
|
package/net/index.js
CHANGED
package/net/portprober.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "selenium-webdriver",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.20.0",
|
|
4
4
|
"description": "The official WebDriver JavaScript bindings from the Selenium project",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"keywords": [
|
|
@@ -24,30 +24,38 @@
|
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"jszip": "^3.10.1",
|
|
27
|
-
"tmp": "^0.2.
|
|
28
|
-
"ws": ">=8.
|
|
27
|
+
"tmp": "^0.2.3",
|
|
28
|
+
"ws": ">=8.16.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"
|
|
32
|
-
"eslint
|
|
31
|
+
"@bazel/runfiles": "^5.8.1",
|
|
32
|
+
"@eslint/js": "^9.1.1",
|
|
33
|
+
"eslint": "^9.1.0",
|
|
34
|
+
"eslint-config-prettier": "^9.1.0",
|
|
35
|
+
"eslint-plugin-mocha": "^10.4.3",
|
|
36
|
+
"eslint-plugin-n": "^17.2.1",
|
|
33
37
|
"eslint-plugin-no-only-tests": "^3.1.0",
|
|
34
|
-
"eslint-plugin-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"mocha": "^10.
|
|
38
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
39
|
+
"express": "^4.19.2",
|
|
40
|
+
"globals": "^15.0.0",
|
|
41
|
+
"mocha": "^10.4.0",
|
|
42
|
+
"mocha-junit-reporter": "^2.2.1",
|
|
38
43
|
"multer": "^1.4.5-lts.1",
|
|
39
|
-
"prettier": "^3.
|
|
44
|
+
"prettier": "^3.2.5",
|
|
40
45
|
"serve-index": "^1.9.1",
|
|
41
|
-
"sinon": "^
|
|
46
|
+
"sinon": "^17.0.1"
|
|
42
47
|
},
|
|
43
48
|
"scripts": {
|
|
44
|
-
"lint": "eslint
|
|
45
|
-
"lint:fix": "eslint
|
|
49
|
+
"lint": "eslint .",
|
|
50
|
+
"lint:fix": "eslint . --fix",
|
|
46
51
|
"test": "npm run lint && mocha -t 600000 --recursive test",
|
|
47
52
|
"test-jasmine": "bazel test //javascript/node/selenium-webdriver:tests"
|
|
48
53
|
},
|
|
49
54
|
"mocha": {
|
|
50
55
|
"recursive": true,
|
|
51
56
|
"timeout": 600000
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"registry": "https://registry.npmjs.org/"
|
|
52
60
|
}
|
|
53
61
|
}
|
package/remote/index.js
CHANGED
package/remote/util.js
CHANGED
package/safari.js
CHANGED
|
@@ -25,10 +25,10 @@ const http = require('./http')
|
|
|
25
25
|
const remote = require('./remote')
|
|
26
26
|
const webdriver = require('./lib/webdriver')
|
|
27
27
|
const { Browser, Capabilities } = require('./lib/capabilities')
|
|
28
|
-
const {
|
|
28
|
+
const { getBinaryPaths } = require('./common/driverFinder')
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
|
-
* Creates {@link
|
|
31
|
+
* Creates {@link remote.DriverService} instances that manage
|
|
32
32
|
* a [safaridriver] server in a child process.
|
|
33
33
|
*
|
|
34
34
|
* [safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
|
|
@@ -123,7 +123,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
123
123
|
|
|
124
124
|
let service = new ServiceBuilder(exe).build()
|
|
125
125
|
if (!service.getExecutable()) {
|
|
126
|
-
service.setExecutable(
|
|
126
|
+
service.setExecutable(getBinaryPaths(caps).driverPath)
|
|
127
127
|
}
|
|
128
128
|
let executor = new http.Executor(service.start().then((url) => new http.HttpClient(url)))
|
|
129
129
|
|
package/testing/index.js
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
'use strict'
|
|
34
34
|
|
|
35
|
-
const { isatty } = require('tty')
|
|
35
|
+
const { isatty } = require('node:tty')
|
|
36
36
|
const chrome = require('../chrome')
|
|
37
37
|
const edge = require('../edge')
|
|
38
38
|
const firefox = require('../firefox')
|
|
@@ -41,7 +41,7 @@ const remote = require('../remote')
|
|
|
41
41
|
const safari = require('../safari')
|
|
42
42
|
const { Browser } = require('../lib/capabilities')
|
|
43
43
|
const { Builder } = require('../index')
|
|
44
|
-
const {
|
|
44
|
+
const { getBinaryPaths } = require('../common/driverFinder')
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Describes a browser targeted by a {@linkplain suite test suite}.
|
|
@@ -72,15 +72,19 @@ TargetBrowser.prototype.platform
|
|
|
72
72
|
function color(c, s) {
|
|
73
73
|
return isatty(process.stdout) ? `\u001b[${c}m${s}\u001b[0m` : s
|
|
74
74
|
}
|
|
75
|
+
|
|
75
76
|
function green(s) {
|
|
76
77
|
return color(32, s)
|
|
77
78
|
}
|
|
79
|
+
|
|
78
80
|
function cyan(s) {
|
|
79
81
|
return color(36, s)
|
|
80
82
|
}
|
|
83
|
+
|
|
81
84
|
function info(msg) {
|
|
82
85
|
console.info(`${green('[INFO]')} ${msg}`)
|
|
83
86
|
}
|
|
87
|
+
|
|
84
88
|
function warn(msg) {
|
|
85
89
|
console.warn(`${cyan('[WARNING]')} ${msg}`)
|
|
86
90
|
}
|
|
@@ -118,15 +122,15 @@ function getAvailableBrowsers() {
|
|
|
118
122
|
info(`Searching for WebDriver executables installed on the current system...`)
|
|
119
123
|
|
|
120
124
|
let targets = [
|
|
121
|
-
[
|
|
122
|
-
[
|
|
123
|
-
[
|
|
125
|
+
[getBinaryPaths(new chrome.Options()), Browser.CHROME],
|
|
126
|
+
[getBinaryPaths(new edge.Options()), Browser.EDGE],
|
|
127
|
+
[getBinaryPaths(new firefox.Options()), Browser.FIREFOX],
|
|
124
128
|
]
|
|
125
129
|
if (process.platform === 'win32') {
|
|
126
|
-
targets.push([
|
|
130
|
+
targets.push([getBinaryPaths(new ie.Options()), Browser.INTERNET_EXPLORER])
|
|
127
131
|
}
|
|
128
132
|
if (process.platform === 'darwin') {
|
|
129
|
-
targets.push([
|
|
133
|
+
targets.push([getBinaryPaths(new safari.Options()), Browser.SAFARI])
|
|
130
134
|
}
|
|
131
135
|
|
|
132
136
|
let availableBrowsers = []
|
|
@@ -298,6 +302,11 @@ class Environment {
|
|
|
298
302
|
builder.setCapability('moz:debuggerAddress', true)
|
|
299
303
|
}
|
|
300
304
|
|
|
305
|
+
// Enable BiDi for supporting browsers.
|
|
306
|
+
if (browser.name === Browser.FIREFOX || browser.name === Browser.CHROME || browser.name === Browser.EDGE) {
|
|
307
|
+
builder.setCapability('webSocketUrl', true)
|
|
308
|
+
}
|
|
309
|
+
|
|
301
310
|
if (typeof urlOrServer === 'string') {
|
|
302
311
|
builder.usingServer(urlOrServer)
|
|
303
312
|
} else if (urlOrServer) {
|
|
@@ -396,7 +405,7 @@ function suite(fn, options = undefined) {
|
|
|
396
405
|
seleniumServer = new remote.SeleniumServer(seleniumJar)
|
|
397
406
|
|
|
398
407
|
const startTimeout = 65 * 1000
|
|
399
|
-
|
|
408
|
+
|
|
400
409
|
function startSelenium() {
|
|
401
410
|
if (typeof this.timeout === 'function') {
|
|
402
411
|
this.timeout(startTimeout) // For mocha.
|