selenium
This commit is contained in:
1240
node_modules/selenium-webdriver/CHANGES.md
generated
vendored
Normal file
1240
node_modules/selenium-webdriver/CHANGES.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
202
node_modules/selenium-webdriver/LICENSE
generated
vendored
Normal file
202
node_modules/selenium-webdriver/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2023 Software Freedom Conservancy (SFC)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
2
node_modules/selenium-webdriver/NOTICE
generated
vendored
Normal file
2
node_modules/selenium-webdriver/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
Copyright 2011-2023 Software Freedom Conservancy
|
||||
Copyright 2004-2011 Selenium committers
|
||||
231
node_modules/selenium-webdriver/README.md
generated
vendored
Normal file
231
node_modules/selenium-webdriver/README.md
generated
vendored
Normal file
@@ -0,0 +1,231 @@
|
||||
# selenium-webdriver
|
||||
|
||||
Selenium is a browser automation library. Most often used for testing
|
||||
web-applications, Selenium may be used for any task that requires automating
|
||||
interaction with the browser.
|
||||
|
||||
## Installation
|
||||
|
||||
Selenium may be installed via npm with
|
||||
|
||||
npm install selenium-webdriver
|
||||
|
||||
You will need to download additional components to work with each of the major
|
||||
browsers. The drivers for Chrome, Firefox, and Microsoft's IE and Edge web
|
||||
browsers are all standalone executables that should be placed on your system
|
||||
[PATH]. Apple's safaridriver (v10 and above) can be found at the
|
||||
following path – /usr/bin/safaridriver. To enable automation on safari,
|
||||
you need to run command `safaridriver --enable`.
|
||||
|
||||
|
||||
| Browser | Component |
|
||||
|:-------------------|:------------------------------------|
|
||||
| Chrome | [chromedriver(.exe)][chrome] |
|
||||
| Internet Explorer | [IEDriverServer.exe][release] |
|
||||
| Edge | [MicrosoftWebDriver.msi][edge] |
|
||||
| Firefox | [geckodriver(.exe)][geckodriver] |
|
||||
| Opera | [operadriver(.exe)][operadriver] |
|
||||
| Safari | [safaridriver] |
|
||||
|
||||
## Usage
|
||||
|
||||
The sample below and others are included in the `example` directory. You may
|
||||
also find the tests for selenium-webdriver informative.
|
||||
|
||||
```javascript
|
||||
const {Builder, Browser, By, Key, until} = require('selenium-webdriver');
|
||||
|
||||
(async function example() {
|
||||
let driver = await new Builder().forBrowser(Browser.FIREFOX).build();
|
||||
try {
|
||||
await driver.get('https://www.google.com/ncr');
|
||||
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN);
|
||||
await driver.wait(until.titleIs('webdriver - Google Search'), 1000);
|
||||
} finally {
|
||||
await driver.quit();
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
### Using the Builder API
|
||||
|
||||
The `Builder` class is your one-stop shop for configuring new WebDriver
|
||||
instances. Rather than clutter your code with branches for the various browsers,
|
||||
the builder lets you set all options in one flow. When you call
|
||||
`Builder#build()`, all options irrelevant to the selected browser are dropped:
|
||||
|
||||
```javascript
|
||||
const webdriver = require('selenium-webdriver');
|
||||
const chrome = require('selenium-webdriver/chrome');
|
||||
const firefox = require('selenium-webdriver/firefox');
|
||||
|
||||
let driver = new webdriver.Builder()
|
||||
.forBrowser(webdriver.Browser.FIREFOX)
|
||||
.setChromeOptions(/* ... */)
|
||||
.setFirefoxOptions(/* ... */)
|
||||
.build();
|
||||
```
|
||||
|
||||
Why would you want to configure options irrelevant to the target browser? The
|
||||
`Builder`'s API defines your _default_ configuration. You can change the target
|
||||
browser at runtime through the `SELENIUM_BROWSER` environment variable. For
|
||||
example, the `example/google_search.js` script is configured to run against
|
||||
Firefox. You can run the example against other browsers just by changing the
|
||||
runtime environment
|
||||
|
||||
# cd node_modules/selenium-webdriver
|
||||
node example/google_search
|
||||
SELENIUM_BROWSER=chrome node example/google_search
|
||||
SELENIUM_BROWSER=safari node example/google_search
|
||||
|
||||
### The Standalone Selenium Server
|
||||
|
||||
The standalone Selenium Server acts as a proxy between your script and the
|
||||
browser-specific drivers. The server may be used when running locally, but it's
|
||||
not recommend as it introduces an extra hop for each request and will slow
|
||||
things down. The server is required, however, to use a browser on a remote host
|
||||
(most browser drivers, like the IEDriverServer, do not accept remote
|
||||
connections).
|
||||
|
||||
To use the Selenium Server, you will need to install the
|
||||
[JDK](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and
|
||||
download the latest server from [Selenium][release]. Once downloaded, run the
|
||||
server with
|
||||
|
||||
java -jar selenium-server-4.4.0.jar standalone
|
||||
|
||||
You may configure your tests to run against a remote server through the Builder
|
||||
API:
|
||||
|
||||
```javascript
|
||||
let driver = new webdriver.Builder()
|
||||
.forBrowser(webdriver.Browser.FIREFOX)
|
||||
.usingServer('http://localhost:4444/wd/hub')
|
||||
.build();
|
||||
```
|
||||
Or change the Builder's configuration at runtime with the `SELENIUM_REMOTE_URL`
|
||||
environment variable:
|
||||
|
||||
SELENIUM_REMOTE_URL="http://localhost:4444/wd/hub" node script.js
|
||||
|
||||
You can experiment with these options using the `example/google_search.js`
|
||||
script provided with `selenium-webdriver`.
|
||||
|
||||
## Documentation
|
||||
|
||||
API documentation is available online from the [Selenium project][api].
|
||||
Additional resources include
|
||||
|
||||
- the #selenium channel on freenode IRC
|
||||
- the [selenium-users@googlegroups.com][users] list
|
||||
- [SeleniumHQ](https://selenium.dev/documentation/) documentation
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are accepted either through [GitHub][gh] pull requests or patches
|
||||
via the [Selenium issue tracker][issues].
|
||||
|
||||
## Node Support Policy
|
||||
|
||||
Each version of selenium-webdriver will support the latest _semver-minor_
|
||||
version of the [LTS] and stable Node releases. All _semver-major_ &
|
||||
_semver-minor_ versions between the LTS and stable release will have "best
|
||||
effort" support. Following a Selenium release, any _semver-minor_ Node releases
|
||||
will also have "best effort" support. Releases older than the latest LTS,
|
||||
_semver-major_ releases, and all unstable release branches (e.g. "v.Next")
|
||||
are considered strictly unsupported.
|
||||
|
||||
For example, suppose the current LTS and stable releases are v14.20.0 and v18.8.0,
|
||||
respectively. Then a Selenium release would have the following support levels:
|
||||
|
||||
| Version | Support |
|
||||
|:----------:|:---------------:|
|
||||
| <= 14.19 | _unsupported_ |
|
||||
| 14.20.0 | supported |
|
||||
| 18.0-7 | best effort |
|
||||
| 18.8.0 | supported |
|
||||
| >= 18.8.0 | best effort |
|
||||
| v.Next | _unsupported_ |
|
||||
|
||||
### Support Level Definitions
|
||||
|
||||
- _supported:_ A selenium-webdriver release will be API compatible with the
|
||||
platform API, without the use of runtime flags.
|
||||
|
||||
- _best effort:_ Bugs will be investigated as time permits. API compatibility is
|
||||
only guaranteed where required by a _supported_ release. This effectively
|
||||
means the adoption of new JS features, such as ES2015 modules, will depend
|
||||
on what is supported in Node's LTS.
|
||||
|
||||
- _unsupported:_ Bug submissions will be closed as will-not-fix and API
|
||||
compatibility is not guaranteed.
|
||||
|
||||
### Projected Support Schedule
|
||||
|
||||
If Node releases a new [LTS] each October and a new major version every 6
|
||||
months, the support window for selenium-webdriver will be roughly:
|
||||
|
||||
| Release | Status | END-OF-LIFE |
|
||||
|:---------:|:----------------:|:------------:|
|
||||
| v14.x | Maintenance LTS | 2023-04-30 |
|
||||
| v16.x | Active LTS | 2023-09-11 |
|
||||
| v18.x | Current | 2025-04-30 |
|
||||
| v19.x | Pending | 2023-06-01 |
|
||||
| v20 | Pending | 2026-04-30 |
|
||||
|
||||
## Issues
|
||||
|
||||
Please report any issues using the [Selenium issue tracker][issues]. When using
|
||||
the issue tracker
|
||||
|
||||
- __Do__ include a detailed description of the problem.
|
||||
- __Do__ include a link to a [gist](http://gist.github.com/) with any
|
||||
interesting stack traces/logs (you may also attach these directly to the bug
|
||||
report).
|
||||
- __Do__ include a [reduced test case][reduction]. Reporting "unable to find
|
||||
element on the page" is _not_ a valid report - there's nothing for us to
|
||||
look into. Expect your bug report to be closed if you do not provide enough
|
||||
information for us to investigate.
|
||||
- __Do not__ use the issue tracker to submit basic help requests. All help
|
||||
inquiries should be directed to the [user forum][users] or #selenium IRC
|
||||
channel.
|
||||
- __Do not__ post empty "I see this too" or "Any updates?" comments. These
|
||||
provide no additional information and clutter the log.
|
||||
- __Do not__ report regressions on closed bugs as they are not actively
|
||||
monitored for updates (especially bugs that are >6 months old). Please open a
|
||||
new issue and reference the original bug in your report.
|
||||
|
||||
## License
|
||||
|
||||
Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The SFC licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
[LTS]: https://github.com/nodejs/LTS
|
||||
[PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
|
||||
[api]: http://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/
|
||||
[chrome]: http://chromedriver.storage.googleapis.com/index.html
|
||||
[gh]: https://github.com/SeleniumHQ/selenium/
|
||||
[issues]: https://github.com/SeleniumHQ/selenium/issues
|
||||
[edge]: http://go.microsoft.com/fwlink/?LinkId=619687
|
||||
[geckodriver]: https://github.com/mozilla/geckodriver/releases/
|
||||
[reduction]: http://www.webkit.org/quality/reduction.html
|
||||
|
||||
[release]: https://www.selenium.dev/downloads/
|
||||
|
||||
[users]: https://groups.google.com/forum/#!forum/selenium-users
|
||||
[safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
|
||||
[operadriver]: https://github.com/operasoftware/operachromiumdriver/releases
|
||||
194
node_modules/selenium-webdriver/bidi/browsingContext.js
generated
vendored
Normal file
194
node_modules/selenium-webdriver/bidi/browsingContext.js
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
class BrowsingContext {
|
||||
constructor(driver) {
|
||||
this._driver = driver
|
||||
}
|
||||
|
||||
async init({ browsingContextId, type, referenceContext }) {
|
||||
if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
|
||||
throw Error('WebDriver instance must support BiDi protocol')
|
||||
}
|
||||
|
||||
if (type != undefined && !['window', 'tab'].includes(type)) {
|
||||
throw Error(`Valid types are 'window' & 'tab'. Received: ${type}`)
|
||||
}
|
||||
|
||||
this.bidi = await this._driver.getBidi()
|
||||
this._id =
|
||||
browsingContextId == undefined
|
||||
? (await this.create(type, referenceContext))['result']['context']
|
||||
: browsingContextId
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a browsing context for the given type and referenceContext
|
||||
*/
|
||||
async create(type, referenceContext) {
|
||||
const params = {
|
||||
method: 'browsingContext.create',
|
||||
params: {
|
||||
type: type,
|
||||
referenceContext: referenceContext,
|
||||
},
|
||||
}
|
||||
return await this.bidi.send(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns id
|
||||
*/
|
||||
get id() {
|
||||
return this._id
|
||||
}
|
||||
|
||||
/**
|
||||
* @param url the url to navigate to
|
||||
* @param readinessState type of readiness state: "none" / "interactive" / "complete"
|
||||
* @returns NavigateResult object
|
||||
*/
|
||||
async navigate(url, readinessState = undefined) {
|
||||
if (
|
||||
readinessState != undefined &&
|
||||
!['none', 'interactive', 'complete'].includes(readinessState)
|
||||
) {
|
||||
throw Error(
|
||||
`Valid readiness states are 'none', 'interactive' & 'complete'. Received: ${readinessState}`
|
||||
)
|
||||
}
|
||||
|
||||
const params = {
|
||||
method: 'browsingContext.navigate',
|
||||
params: {
|
||||
context: this._id,
|
||||
url: url,
|
||||
wait: readinessState,
|
||||
},
|
||||
}
|
||||
const navigateResult = (await this.bidi.send(params))['result']
|
||||
|
||||
return new NavigateResult(
|
||||
navigateResult['url'],
|
||||
navigateResult['navigation']
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxDepth the max depth of the descendents of browsing context tree
|
||||
* @returns BrowsingContextInfo object
|
||||
*/
|
||||
async getTree(maxDepth = undefined) {
|
||||
const params = {
|
||||
method: 'browsingContext.getTree',
|
||||
params: {
|
||||
root: this._id,
|
||||
maxDepth: maxDepth,
|
||||
},
|
||||
}
|
||||
|
||||
let result = await this.bidi.send(params)
|
||||
if ('error' in result) {
|
||||
throw Error(result['error'])
|
||||
}
|
||||
|
||||
result = result['result']['contexts'][0]
|
||||
return new BrowsingContextInfo(
|
||||
result['context'],
|
||||
result['url'],
|
||||
result['children'],
|
||||
result['parent']
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the browing context
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async close() {
|
||||
const params = {
|
||||
method: 'browsingContext.close',
|
||||
params: {
|
||||
context: this._id,
|
||||
},
|
||||
}
|
||||
await this.bidi.send(params)
|
||||
}
|
||||
}
|
||||
|
||||
class NavigateResult {
|
||||
constructor(url, navigationId) {
|
||||
this._url = url
|
||||
this._navigationId = navigationId
|
||||
}
|
||||
|
||||
get url() {
|
||||
return this._url
|
||||
}
|
||||
|
||||
get navigationId() {
|
||||
return this._navigationId
|
||||
}
|
||||
}
|
||||
|
||||
class BrowsingContextInfo {
|
||||
constructor(id, url, children, parentBrowsingContext) {
|
||||
this._id = id
|
||||
this._url = url
|
||||
this._children = children
|
||||
this._parentBrowsingContext = parentBrowsingContext
|
||||
}
|
||||
|
||||
get id() {
|
||||
return this._id
|
||||
}
|
||||
|
||||
get url() {
|
||||
return this._url
|
||||
}
|
||||
|
||||
get children() {
|
||||
return this._children
|
||||
}
|
||||
|
||||
get parentBrowsingContext() {
|
||||
return this._parentBrowsingContext
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initiate browsing context instance and return
|
||||
* @param driver
|
||||
* @param browsingContextId The browsing context of current window/tab
|
||||
* @param type "window" or "tab"
|
||||
* @param referenceContext To get a browsing context for this reference if passed
|
||||
* @returns {Promise<BrowsingContext>}
|
||||
*/
|
||||
async function getBrowsingContextInstance(
|
||||
driver,
|
||||
{ browsingContextId, type, referenceContext }
|
||||
) {
|
||||
let instance = new BrowsingContext(driver)
|
||||
await instance.init({ browsingContextId, type, referenceContext })
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* API
|
||||
* @type {function(*, {*,*,*}): Promise<BrowsingContext>}
|
||||
*/
|
||||
module.exports = getBrowsingContextInstance
|
||||
46
node_modules/selenium-webdriver/bidi/filterBy.js
generated
vendored
Normal file
46
node_modules/selenium-webdriver/bidi/filterBy.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
class FilterBy {
|
||||
constructor(level) {
|
||||
this.level_ = level
|
||||
}
|
||||
|
||||
static logLevel(level) {
|
||||
if (
|
||||
level === undefined ||
|
||||
(level != undefined &&
|
||||
!['debug', 'error', 'info', 'warning'].includes(level))
|
||||
) {
|
||||
throw Error(
|
||||
`Please pass valid log level. Valid log levels are 'debug', 'error', 'info' and 'warning'. Received: ${level}`
|
||||
)
|
||||
}
|
||||
|
||||
return new FilterBy(level)
|
||||
}
|
||||
|
||||
getLevel() {
|
||||
return this.level_
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
FilterBy,
|
||||
}
|
||||
224
node_modules/selenium-webdriver/bidi/index.js
generated
vendored
Normal file
224
node_modules/selenium-webdriver/bidi/index.js
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
const { EventEmitter } = require('node:events')
|
||||
const WebSocket = require('ws')
|
||||
|
||||
const RESPONSE_TIMEOUT = 1000 * 30
|
||||
|
||||
class Index extends EventEmitter {
|
||||
id = 0
|
||||
isConnected = false
|
||||
events = []
|
||||
browsingContexts = []
|
||||
|
||||
/**
|
||||
* Create a new websocket connection
|
||||
* @param _webSocketUrl
|
||||
*/
|
||||
constructor (_webSocketUrl) {
|
||||
super()
|
||||
this.isConnected = false
|
||||
this._ws = new WebSocket(_webSocketUrl)
|
||||
this._ws.on('open', () => {
|
||||
this.isConnected = true
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve connection
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
async waitForConnection () {
|
||||
return new Promise((resolve) => {
|
||||
if (this.isConnected) {
|
||||
resolve()
|
||||
} else {
|
||||
this._ws.once('open', () => {
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {WebSocket}
|
||||
*/
|
||||
get socket () {
|
||||
return this._ws
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {boolean|*}
|
||||
*/
|
||||
get isConnected () {
|
||||
return this.isConnected
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a bidi request
|
||||
* @param params
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
async send (params) {
|
||||
if (!this.isConnected) {
|
||||
await this.waitForConnection()
|
||||
}
|
||||
|
||||
const id = ++this.id
|
||||
|
||||
this._ws.send(JSON.stringify({ id, ...params }))
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
reject(new Error(`Request with id ${id} timed out`))
|
||||
handler.off('message', listener)
|
||||
}, RESPONSE_TIMEOUT)
|
||||
|
||||
const listener = (data) => {
|
||||
try {
|
||||
const payload = JSON.parse(data.toString())
|
||||
if (payload.id === id) {
|
||||
clearTimeout(timeoutId)
|
||||
handler.off('message', listener)
|
||||
resolve(payload)
|
||||
}
|
||||
} catch (err) {
|
||||
log.error(`Failed parse message: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const handler = this._ws.on('message', listener)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to events
|
||||
* @param events
|
||||
* @param browsingContexts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async subscribe (events, browsingContexts) {
|
||||
function toArray (arg) {
|
||||
if (arg === undefined) {
|
||||
return []
|
||||
}
|
||||
|
||||
return Array.isArray(arg) ? [...arg] : [arg]
|
||||
}
|
||||
|
||||
const eventsArray = toArray(events)
|
||||
const contextsArray = toArray(browsingContexts)
|
||||
|
||||
const params = {
|
||||
method: 'session.subscribe', params: {},
|
||||
}
|
||||
|
||||
if (eventsArray.length && eventsArray.some(
|
||||
event => typeof event !== 'string')) {
|
||||
throw new TypeError('events should be string or string array')
|
||||
}
|
||||
|
||||
if (contextsArray.length && contextsArray.some(
|
||||
context => typeof context !== 'string')) {
|
||||
throw new TypeError('browsingContexts should be string or string array')
|
||||
}
|
||||
|
||||
if (eventsArray.length) {
|
||||
params.params.events = eventsArray
|
||||
}
|
||||
|
||||
if (contextsArray.length) {
|
||||
params.params.contexts = contextsArray
|
||||
}
|
||||
|
||||
await this.send(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe to events
|
||||
* @param events
|
||||
* @param browsingContexts
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async unsubscribe (events, browsingContexts) {
|
||||
if (typeof events === 'string') {
|
||||
this.events = this.events.filter(event => event !== events)
|
||||
} else if (Array.isArray(events)) {
|
||||
this.events = this.events.filter(event => !events.includes(event))
|
||||
}
|
||||
|
||||
if (typeof browsingContexts === 'string') {
|
||||
this.browsingContexts.pop()
|
||||
} else if (Array.isArray(browsingContexts)) {
|
||||
this.browsingContexts =
|
||||
this.browsingContexts.filter(id => !browsingContexts.includes(id))
|
||||
}
|
||||
|
||||
const params = {
|
||||
method: 'session.unsubscribe', params: {
|
||||
events: this.events,
|
||||
}
|
||||
}
|
||||
|
||||
if (this.browsingContexts.length > 0) {
|
||||
params.params.contexts = this.browsingContexts
|
||||
}
|
||||
|
||||
await this.send(params)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bidi Status
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
get status () {
|
||||
return this.send({
|
||||
method: 'session.status', params: {}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Close ws connection.
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
close () {
|
||||
const closeWebSocket = (callback) => {
|
||||
// don't close if it's already closed
|
||||
if (this._ws.readyState === 3) {
|
||||
callback()
|
||||
} else {
|
||||
// don't notify on user-initiated shutdown ('disconnect' event)
|
||||
this._ws.removeAllListeners('close')
|
||||
this._ws.once('close', () => {
|
||||
this._ws.removeAllListeners()
|
||||
callback()
|
||||
})
|
||||
this._ws.close()
|
||||
}
|
||||
}
|
||||
return new Promise((fulfill, reject) => {
|
||||
closeWebSocket(fulfill)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* API
|
||||
* @type {function(*): Promise<Index>}
|
||||
*/
|
||||
module.exports = Index
|
||||
87
node_modules/selenium-webdriver/bidi/logEntries.js
generated
vendored
Normal file
87
node_modules/selenium-webdriver/bidi/logEntries.js
generated
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
class BaseLogEntry {
|
||||
constructor (level, text, timeStamp, stackTrace) {
|
||||
this._level = level
|
||||
this._text = text
|
||||
this._timeStamp = timeStamp
|
||||
this._stackTrace = stackTrace
|
||||
}
|
||||
|
||||
get level () {
|
||||
return this._level
|
||||
}
|
||||
|
||||
get text () {
|
||||
return this._text
|
||||
}
|
||||
|
||||
get timeStamp () {
|
||||
return this._timeStamp
|
||||
}
|
||||
|
||||
get stackTrace () {
|
||||
return this._stackTrace
|
||||
}
|
||||
}
|
||||
|
||||
class GenericLogEntry extends BaseLogEntry {
|
||||
constructor (level, text, timeStamp, type, stackTrace) {
|
||||
super(level, text, timeStamp, stackTrace)
|
||||
this._type = type
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type
|
||||
}
|
||||
}
|
||||
|
||||
class ConsoleLogEntry extends GenericLogEntry {
|
||||
constructor (level, text, timeStamp, type, method, realm, args, stackTrace) {
|
||||
super(level, text, timeStamp, type, stackTrace)
|
||||
this._method = method
|
||||
this._realm = realm
|
||||
this._args = args
|
||||
}
|
||||
|
||||
get method () {
|
||||
return this._method
|
||||
}
|
||||
|
||||
get realm () {
|
||||
return this._realm
|
||||
}
|
||||
|
||||
get args () {
|
||||
return this._args
|
||||
}
|
||||
}
|
||||
|
||||
class JavascriptLogEntry extends GenericLogEntry {
|
||||
constructor (level, text, timeStamp, type, stackTrace) {
|
||||
super(level, text, timeStamp, type, stackTrace)
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
BaseLogEntry, GenericLogEntry, ConsoleLogEntry, JavascriptLogEntry,
|
||||
}
|
||||
269
node_modules/selenium-webdriver/bidi/logInspector.js
generated
vendored
Normal file
269
node_modules/selenium-webdriver/bidi/logInspector.js
generated
vendored
Normal file
@@ -0,0 +1,269 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
const { FilterBy } = require('./filterBy')
|
||||
const { ConsoleLogEntry, JavascriptLogEntry, GenericLogEntry } = require('./logEntries')
|
||||
|
||||
const LOG = {
|
||||
TYPE_CONSOLE : 'console',
|
||||
TYPE_JS_LOGS : 'javascript',
|
||||
}
|
||||
|
||||
class LogInspector {
|
||||
bidi
|
||||
ws
|
||||
|
||||
constructor (driver, browsingContextIds) {
|
||||
this._driver = driver
|
||||
this._browsingContextIds = browsingContextIds
|
||||
this.listener = {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to log event
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async init () {
|
||||
this.bidi = await this._driver.getBidi()
|
||||
await this.bidi.subscribe('log.entryAdded', this._browsingContextIds)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param kind
|
||||
*/
|
||||
logListener (kind) {
|
||||
if (!(kind in this.listener)) {
|
||||
this.listener[kind] = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to Console logs
|
||||
* @param callback
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onConsoleEntry(callback, filterBy = undefined) {
|
||||
if (filterBy != undefined && !(filterBy instanceof FilterBy)) {
|
||||
throw Error(`Pass valid FilterBy object. Received: ${filterBy}`)
|
||||
}
|
||||
|
||||
this.ws = await this.bidi.socket
|
||||
|
||||
this.ws.on('message', (event) => {
|
||||
const { params } = JSON.parse(Buffer.from(event.toString()))
|
||||
|
||||
if (params?.type === LOG.TYPE_CONSOLE) {
|
||||
let consoleEntry = new ConsoleLogEntry(
|
||||
params.level,
|
||||
params.text,
|
||||
params.timestamp,
|
||||
params.type,
|
||||
params.method,
|
||||
params.realm,
|
||||
params.args,
|
||||
params.stackTrace
|
||||
)
|
||||
|
||||
if (filterBy != undefined) {
|
||||
if (params?.level === filterBy.getLevel()) {
|
||||
callback(consoleEntry)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
callback(consoleEntry)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to JS logs
|
||||
* @param callback
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onJavascriptLog(callback, filterBy = undefined) {
|
||||
if (filterBy != undefined && !(filterBy instanceof FilterBy)) {
|
||||
throw Error(`Pass valid FilterBy object. Received: ${filterBy}`)
|
||||
}
|
||||
|
||||
this.ws = await this.bidi.socket
|
||||
|
||||
this.ws.on('message', (event) => {
|
||||
const { params } = JSON.parse(Buffer.from(event.toString()))
|
||||
|
||||
if (params?.type === LOG.TYPE_JS_LOGS) {
|
||||
let jsEntry = new JavascriptLogEntry(
|
||||
params.level,
|
||||
params.text,
|
||||
params.timestamp,
|
||||
params.type,
|
||||
params.stackTrace
|
||||
)
|
||||
|
||||
if (filterBy != undefined) {
|
||||
if (params?.level === filterBy.getLevel()) {
|
||||
callback(jsEntry)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
callback(jsEntry)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to JS Exceptions
|
||||
* @param callback
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onJavascriptException(callback) {
|
||||
this.ws = await this.bidi.socket
|
||||
let enabled =
|
||||
LOG.TYPE_JS_EXCEPTION in this.listener ||
|
||||
this.logListener(LOG.TYPE_JS_EXCEPTION)
|
||||
this.listener[LOG.TYPE_JS_EXCEPTION].push(callback)
|
||||
|
||||
if (enabled) {
|
||||
return
|
||||
}
|
||||
|
||||
this.ws.on('message', (event) => {
|
||||
const { params } = JSON.parse(Buffer.from(event.toString()))
|
||||
if (params?.type === 'javascript' && params?.level === 'error') {
|
||||
let jsErrorEntry = new JavascriptLogEntry(
|
||||
params.level,
|
||||
params.text,
|
||||
params.timestamp,
|
||||
params.type,
|
||||
params.stackTrace
|
||||
)
|
||||
|
||||
this.listener[LOG.TYPE_JS_EXCEPTION].forEach((listener) => {
|
||||
listener(jsErrorEntry)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen to any logs
|
||||
* @param callback
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async onLog(callback, filterBy = undefined) {
|
||||
if (filterBy != undefined && !(filterBy instanceof FilterBy)) {
|
||||
throw Error(`Pass valid FilterBy object. Received: ${filterBy}`)
|
||||
}
|
||||
|
||||
this.ws = await this.bidi.socket
|
||||
|
||||
this.ws.on('message', (event) => {
|
||||
const { params } = JSON.parse(Buffer.from(event.toString()))
|
||||
if (params?.type === 'javascript') {
|
||||
let jsEntry = new JavascriptLogEntry(
|
||||
params.level,
|
||||
params.text,
|
||||
params.timestamp,
|
||||
params.type,
|
||||
params.stackTrace
|
||||
)
|
||||
|
||||
if (filterBy != undefined) {
|
||||
if (params?.level === filterBy.getLevel()) {
|
||||
callback(jsEntry)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
callback(jsEntry)
|
||||
return
|
||||
}
|
||||
|
||||
if (params?.type === 'console') {
|
||||
let consoleEntry = new ConsoleLogEntry(
|
||||
params.level,
|
||||
params.text,
|
||||
params.timestamp,
|
||||
params.type,
|
||||
params.method,
|
||||
params.realm,
|
||||
params.args,
|
||||
params.stackTrace
|
||||
)
|
||||
|
||||
if (filterBy != undefined) {
|
||||
if (params?.level === filterBy.getLevel()) {
|
||||
callback(consoleEntry)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
callback(consoleEntry)
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
params != undefined &&
|
||||
!['console', 'javascript'].includes(params?.type)
|
||||
) {
|
||||
let genericEntry = new GenericLogEntry(
|
||||
params.level,
|
||||
params.text,
|
||||
params.timestamp,
|
||||
params.type,
|
||||
params.stackTrace
|
||||
)
|
||||
|
||||
if (filterBy != undefined) {
|
||||
if (params?.level === filterBy.getLevel()) {
|
||||
callback(genericEntry)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
callback(genericEntry)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe to log event
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async close () {
|
||||
await this.bidi.unsubscribe('log.entryAdded', this._browsingContextIds)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* initiate inspector instance and return
|
||||
* @param driver
|
||||
* @param browsingContextIds
|
||||
* @returns {Promise<LogInspector>}
|
||||
*/
|
||||
async function getLogInspectorInstance (driver, browsingContextIds) {
|
||||
let instance = new LogInspector(driver, browsingContextIds)
|
||||
await instance.init()
|
||||
return instance
|
||||
}
|
||||
|
||||
/**
|
||||
* API
|
||||
* @type {function(*, *): Promise<LogInspector>}
|
||||
*/
|
||||
module.exports = getLogInspectorInstance
|
||||
BIN
node_modules/selenium-webdriver/bin/linux/selenium-manager
generated
vendored
Normal file
BIN
node_modules/selenium-webdriver/bin/linux/selenium-manager
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/selenium-webdriver/bin/macos/selenium-manager
generated
vendored
Normal file
BIN
node_modules/selenium-webdriver/bin/macos/selenium-manager
generated
vendored
Normal file
Binary file not shown.
BIN
node_modules/selenium-webdriver/bin/windows/selenium-manager.exe
generated
vendored
Normal file
BIN
node_modules/selenium-webdriver/bin/windows/selenium-manager.exe
generated
vendored
Normal file
Binary file not shown.
288
node_modules/selenium-webdriver/chrome.js
generated
vendored
Normal file
288
node_modules/selenium-webdriver/chrome.js
generated
vendored
Normal file
@@ -0,0 +1,288 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a {@linkplain Driver WebDriver} client for the Chrome
|
||||
* web browser. Before using this module, you must download the latest
|
||||
* [ChromeDriver release] and ensure it can be found on your system [PATH].
|
||||
*
|
||||
* There are three primary classes exported by this module:
|
||||
*
|
||||
* 1. {@linkplain ServiceBuilder}: configures the
|
||||
* {@link selenium-webdriver/remote.DriverService remote.DriverService}
|
||||
* that manages the [ChromeDriver] child process.
|
||||
*
|
||||
* 2. {@linkplain Options}: defines configuration options for each new Chrome
|
||||
* session, such as which {@linkplain Options#setProxy proxy} to use,
|
||||
* what {@linkplain Options#addExtensions extensions} to install, or
|
||||
* what {@linkplain Options#addArguments command-line switches} to use when
|
||||
* starting the browser.
|
||||
*
|
||||
* 3. {@linkplain Driver}: the WebDriver client; each new instance will control
|
||||
* a unique browser session with a clean user profile (unless otherwise
|
||||
* configured through the {@link Options} class).
|
||||
*
|
||||
* __Headless Chrome__ <a id="headless"></a>
|
||||
*
|
||||
* To start Chrome in headless mode, simply call
|
||||
* {@linkplain Options#headless Options.headless()}.
|
||||
*
|
||||
* let chrome = require('selenium-webdriver/chrome');
|
||||
* let {Builder} = require('selenium-webdriver');
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('chrome')
|
||||
* .setChromeOptions(new chrome.Options().headless())
|
||||
* .build();
|
||||
*
|
||||
* __Customizing the ChromeDriver Server__ <a id="custom-server"></a>
|
||||
*
|
||||
* By default, every Chrome session will use a single driver service, which is
|
||||
* started the first time a {@link Driver} instance is created and terminated
|
||||
* when this process exits. The default service will inherit its environment
|
||||
* from the current process and direct all output to /dev/null. You may obtain
|
||||
* a handle to this default service using
|
||||
* {@link #getDefaultService getDefaultService()} and change its configuration
|
||||
* with {@link #setDefaultService setDefaultService()}.
|
||||
*
|
||||
* You may also create a {@link Driver} with its own driver service. This is
|
||||
* useful if you need to capture the server's log output for a specific session:
|
||||
*
|
||||
* let chrome = require('selenium-webdriver/chrome');
|
||||
*
|
||||
* let service = new chrome.ServiceBuilder()
|
||||
* .loggingTo('/my/log/file.txt')
|
||||
* .enableVerboseLogging()
|
||||
* .build();
|
||||
*
|
||||
* let options = new chrome.Options();
|
||||
* // configure browser options ...
|
||||
*
|
||||
* let driver = chrome.Driver.createSession(options, service);
|
||||
*
|
||||
* Users should only instantiate the {@link Driver} class directly when they
|
||||
* need a custom driver service configuration (as shown above). For normal
|
||||
* operation, users should start Chrome using the
|
||||
* {@link selenium-webdriver.Builder}.
|
||||
*
|
||||
* __Working with Android__ <a id="android"></a>
|
||||
*
|
||||
* The [ChromeDriver][android] supports running tests on the Chrome browser as
|
||||
* well as [WebView apps][webview] starting in Android 4.4 (KitKat). In order to
|
||||
* work with Android, you must first start the adb
|
||||
*
|
||||
* adb start-server
|
||||
*
|
||||
* By default, adb will start on port 5037. You may change this port, but this
|
||||
* will require configuring a [custom server](#custom-server) that will connect
|
||||
* to adb on the {@linkplain ServiceBuilder#setAdbPort correct port}:
|
||||
*
|
||||
* let service = new chrome.ServiceBuilder()
|
||||
* .setAdbPort(1234)
|
||||
* build();
|
||||
* // etc.
|
||||
*
|
||||
* The ChromeDriver may be configured to launch Chrome on Android using
|
||||
* {@link Options#androidChrome()}:
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('chrome')
|
||||
* .setChromeOptions(new chrome.Options().androidChrome())
|
||||
* .build();
|
||||
*
|
||||
* Alternatively, you can configure the ChromeDriver to launch an app with a
|
||||
* Chrome-WebView by setting the {@linkplain Options#androidActivity
|
||||
* androidActivity} option:
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('chrome')
|
||||
* .setChromeOptions(new chrome.Options()
|
||||
* .androidPackage('com.example')
|
||||
* .androidActivity('com.example.Activity'))
|
||||
* .build();
|
||||
*
|
||||
* [Refer to the ChromeDriver site] for more information on using the
|
||||
* [ChromeDriver with Android][android].
|
||||
*
|
||||
* [ChromeDriver]: https://chromedriver.chromium.org/
|
||||
* [ChromeDriver release]: http://chromedriver.storage.googleapis.com/index.html
|
||||
* [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
|
||||
* [android]: https://chromedriver.chromium.org/getting-started/getting-started---android
|
||||
* [webview]: https://developer.chrome.com/multidevice/webview/overview
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const io = require('./io')
|
||||
const { Browser } = require('./lib/capabilities')
|
||||
const chromium = require('./chromium')
|
||||
const { driverLocation } = require('./common/seleniumManager')
|
||||
|
||||
/**
|
||||
* Name of the ChromeDriver executable.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
const CHROMEDRIVER_EXE =
|
||||
process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver'
|
||||
|
||||
/** @type {remote.DriverService} */
|
||||
|
||||
/**
|
||||
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
||||
* a [ChromeDriver](https://chromedriver.chromium.org/)
|
||||
* server in a child process.
|
||||
*/
|
||||
class ServiceBuilder extends chromium.ServiceBuilder {
|
||||
/**
|
||||
* @param {string=} opt_exe Path to the server executable to use. If omitted,
|
||||
* the builder will attempt to locate the chromedriver on the current
|
||||
* PATH. If the chromedriver is not available in path, selenium-manager will
|
||||
* download the chromedriver
|
||||
* @throws {Error} If provided executable does not exist, or the chromedriver
|
||||
* cannot be found on the PATH.
|
||||
*/
|
||||
constructor(opt_exe) {
|
||||
let exe = opt_exe || locateSynchronously()
|
||||
|
||||
if (!exe) {
|
||||
console.log(
|
||||
` The ChromeDriver could not be found on the current PATH, trying Selenium Manager`
|
||||
)
|
||||
|
||||
try {
|
||||
exe = driverLocation(Browser.CHROME)
|
||||
} catch (err) {
|
||||
console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!exe) {
|
||||
throw Error(
|
||||
`The ChromeDriver could not be found on the current PATH.
|
||||
Please download the latest version of the ChromeDriver
|
||||
from http://chromedriver.storage.googleapis.com/index.html
|
||||
and ensure it can be found on your PATH.`
|
||||
)
|
||||
}
|
||||
|
||||
super(exe)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for managing ChromeDriver specific options.
|
||||
*/
|
||||
class Options extends chromium.Options {
|
||||
/**
|
||||
* Sets the path to the Chrome binary to use. On Mac OS X, this path should
|
||||
* reference the actual Chrome executable, not just the application binary
|
||||
* (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
|
||||
*
|
||||
* The binary path be absolute or relative to the chromedriver server
|
||||
* executable, but it must exist on the machine that will launch Chrome.
|
||||
*
|
||||
* @param {string} path The path to the Chrome binary to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setChromeBinaryPath(path) {
|
||||
return this.setBinaryPath(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the ChromeDriver to launch Chrome on Android via adb. This
|
||||
* function is shorthand for
|
||||
* {@link #androidPackage options.androidPackage('com.android.chrome')}.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
androidChrome() {
|
||||
return this.androidPackage('com.android.chrome')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to Chrome's log file. This path should exist on the machine
|
||||
* that will launch Chrome.
|
||||
* @param {string} path Path to the log file to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setChromeLogFile(path) {
|
||||
return this.setBrowserLogFile(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the directory to store Chrome minidumps in. This option is only
|
||||
* supported when ChromeDriver is running on Linux.
|
||||
* @param {string} path The directory path.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setChromeMinidumpPath(path) {
|
||||
return this.setBrowserMinidumpPath(path)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebDriver client for Chrome.
|
||||
*/
|
||||
class Driver extends chromium.Driver {
|
||||
/**
|
||||
* Creates a new session with the ChromeDriver.
|
||||
*
|
||||
* @param {(Capabilities|Options)=} opt_config The configuration options.
|
||||
* @param {(remote.DriverService|http.Executor)=} opt_serviceExecutor Either
|
||||
* a DriverService to use for the remote end, or a preconfigured executor
|
||||
* for an externally managed endpoint. If neither is provided, the
|
||||
* {@linkplain ##getDefaultService default service} will be used by
|
||||
* default.
|
||||
* @return {!Driver} A new driver instance.
|
||||
*/
|
||||
static createSession(opt_config, opt_serviceExecutor) {
|
||||
let caps = opt_config || new Options()
|
||||
return /** @type {!Driver} */ (
|
||||
super.createSession(caps, opt_serviceExecutor)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* returns new instance chrome driver service
|
||||
* @returns {remote.DriverService}
|
||||
*/
|
||||
static getDefaultService() {
|
||||
return new ServiceBuilder().build()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _Synchronously_ attempts to locate the chromedriver executable on the current
|
||||
* system.
|
||||
*
|
||||
* @return {?string} the located executable, or `null`.
|
||||
*/
|
||||
function locateSynchronously() {
|
||||
return io.findInPath(CHROMEDRIVER_EXE, true)
|
||||
}
|
||||
|
||||
Options.prototype.CAPABILITY_KEY = 'goog:chromeOptions'
|
||||
Options.prototype.BROWSER_NAME_VALUE = Browser.CHROME
|
||||
Driver.prototype.VENDOR_COMMAND_PREFIX = 'goog'
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = {
|
||||
Driver: Driver,
|
||||
Options,
|
||||
ServiceBuilder,
|
||||
locateSynchronously,
|
||||
}
|
||||
945
node_modules/selenium-webdriver/chromium.js
generated
vendored
Normal file
945
node_modules/selenium-webdriver/chromium.js
generated
vendored
Normal file
@@ -0,0 +1,945 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines an abstract {@linkplain Driver WebDriver} client for
|
||||
* Chromium-based web browsers. These classes should not be instantiated
|
||||
* directly.
|
||||
*
|
||||
* There are three primary classes exported by this module:
|
||||
*
|
||||
* 1. {@linkplain ServiceBuilder}: configures the
|
||||
* {@link selenium-webdriver/remote.DriverService remote.DriverService}
|
||||
* that manages a WebDriver server child process.
|
||||
*
|
||||
* 2. {@linkplain Options}: defines configuration options for each new Chromium
|
||||
* session, such as which {@linkplain Options#setProxy proxy} to use,
|
||||
* what {@linkplain Options#addExtensions extensions} to install, or
|
||||
* what {@linkplain Options#addArguments command-line switches} to use when
|
||||
* starting the browser.
|
||||
*
|
||||
* 3. {@linkplain Driver}: the WebDriver client; each new instance will control
|
||||
* a unique browser session with a clean user profile (unless otherwise
|
||||
* configured through the {@link Options} class).
|
||||
*
|
||||
* __Headless Chromium__ <a id="headless"></a>
|
||||
*
|
||||
* To start the browser in headless mode, simply call
|
||||
* {@linkplain Options#headless Options.headless()}.
|
||||
*
|
||||
* let chrome = require('selenium-webdriver/chrome');
|
||||
* let {Builder} = require('selenium-webdriver');
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('chrome')
|
||||
* .setChromeOptions(new chrome.Options().headless())
|
||||
* .build();
|
||||
*
|
||||
* __Customizing the Chromium WebDriver Server__ <a id="custom-server"></a>
|
||||
*
|
||||
* Subclasses of {@link Driver} are expected to provide a static
|
||||
* getDefaultService method. By default, this method will be called every time
|
||||
* a {@link Driver} instance is created to obtain the default driver service
|
||||
* for that specific browser (e.g. Chrome or Chromium Edge). Subclasses are
|
||||
* responsible for managing the lifetime of the default service.
|
||||
*
|
||||
* You may also create a {@link Driver} with its own driver service. This is
|
||||
* useful if you need to capture the server's log output for a specific session:
|
||||
*
|
||||
* let chrome = require('selenium-webdriver/chrome');
|
||||
*
|
||||
* let service = new chrome.ServiceBuilder()
|
||||
* .loggingTo('/my/log/file.txt')
|
||||
* .enableVerboseLogging()
|
||||
* .build();
|
||||
*
|
||||
* let options = new chrome.Options();
|
||||
* // configure browser options ...
|
||||
*
|
||||
* let driver = chrome.Driver.createSession(options, service);
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const http = require('./http')
|
||||
const io = require('./io')
|
||||
const { Capabilities, Capability } = require('./lib/capabilities')
|
||||
const command = require('./lib/command')
|
||||
const error = require('./lib/error')
|
||||
const Symbols = require('./lib/symbols')
|
||||
const webdriver = require('./lib/webdriver')
|
||||
const remote = require('./remote')
|
||||
|
||||
/**
|
||||
* Custom command names supported by Chromium WebDriver.
|
||||
* @enum {string}
|
||||
*/
|
||||
const Command = {
|
||||
LAUNCH_APP: 'launchApp',
|
||||
GET_NETWORK_CONDITIONS: 'getNetworkConditions',
|
||||
SET_NETWORK_CONDITIONS: 'setNetworkConditions',
|
||||
DELETE_NETWORK_CONDITIONS: 'deleteNetworkConditions',
|
||||
SEND_DEVTOOLS_COMMAND: 'sendDevToolsCommand',
|
||||
SEND_AND_GET_DEVTOOLS_COMMAND: 'sendAndGetDevToolsCommand',
|
||||
SET_PERMISSION: 'setPermission',
|
||||
GET_CAST_SINKS: 'getCastSinks',
|
||||
SET_CAST_SINK_TO_USE: 'setCastSinkToUse',
|
||||
START_CAST_DESKTOP_MIRRORING: 'startDesktopMirroring',
|
||||
START_CAST_TAB_MIRRORING: 'setCastTabMirroring',
|
||||
GET_CAST_ISSUE_MESSAGE: 'getCastIssueMessage',
|
||||
STOP_CASTING: 'stopCasting',
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command executor with support for Chromium's custom commands.
|
||||
* @param {!Promise<string>} url The server's URL.
|
||||
* @param vendorPrefix
|
||||
* @return {!command.Executor} The new command executor.
|
||||
*/
|
||||
function createExecutor(url, vendorPrefix) {
|
||||
const agent = new http.Agent({ keepAlive: true })
|
||||
const client = url.then((url) => new http.HttpClient(url, agent))
|
||||
const executor = new http.Executor(client)
|
||||
configureExecutor(executor, vendorPrefix)
|
||||
return executor
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given executor with Chromium-specific commands.
|
||||
* @param {!http.Executor} executor the executor to configure.
|
||||
*/
|
||||
function configureExecutor(executor, vendorPrefix) {
|
||||
executor.defineCommand(
|
||||
Command.LAUNCH_APP,
|
||||
'POST',
|
||||
'/session/:sessionId/chromium/launch_app'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.GET_NETWORK_CONDITIONS,
|
||||
'GET',
|
||||
'/session/:sessionId/chromium/network_conditions'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.SET_NETWORK_CONDITIONS,
|
||||
'POST',
|
||||
'/session/:sessionId/chromium/network_conditions'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.DELETE_NETWORK_CONDITIONS,
|
||||
'DELETE',
|
||||
'/session/:sessionId/chromium/network_conditions'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.SEND_DEVTOOLS_COMMAND,
|
||||
'POST',
|
||||
'/session/:sessionId/chromium/send_command'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.SEND_AND_GET_DEVTOOLS_COMMAND,
|
||||
'POST',
|
||||
'/session/:sessionId/chromium/send_command_and_get_result'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.SET_PERMISSION,
|
||||
'POST',
|
||||
'/session/:sessionId/permissions'
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.GET_CAST_SINKS,
|
||||
'GET',
|
||||
`/session/:sessionId/${vendorPrefix}/cast/get_sinks`
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.SET_CAST_SINK_TO_USE,
|
||||
'POST',
|
||||
`/session/:sessionId/${vendorPrefix}/cast/set_sink_to_use`
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.START_CAST_DESKTOP_MIRRORING,
|
||||
'POST',
|
||||
`/session/:sessionId/${vendorPrefix}/cast/start_desktop_mirroring`
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.START_CAST_TAB_MIRRORING,
|
||||
'POST',
|
||||
`/session/:sessionId/${vendorPrefix}/cast/start_tab_mirroring`
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.GET_CAST_ISSUE_MESSAGE,
|
||||
'GET',
|
||||
`/session/:sessionId/${vendorPrefix}/cast/get_issue_message`
|
||||
)
|
||||
executor.defineCommand(
|
||||
Command.STOP_CASTING,
|
||||
'POST',
|
||||
`/session/:sessionId/${vendorPrefix}/cast/stop_casting`
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
||||
* a WebDriver server in a child process.
|
||||
*/
|
||||
class ServiceBuilder extends remote.DriverService.Builder {
|
||||
/**
|
||||
* @param {string=} exe Path to the server executable to use. Subclasses
|
||||
* should ensure a valid path to the appropriate exe is provided.
|
||||
*/
|
||||
constructor(exe) {
|
||||
super(exe)
|
||||
this.setLoopback(true) // Required
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets which port adb is listening to. _The driver will connect to adb
|
||||
* if an {@linkplain Options#androidPackage Android session} is requested, but
|
||||
* adb **must** be started beforehand._
|
||||
*
|
||||
* @param {number} port Which port adb is running on.
|
||||
* @return {!ServiceBuilder} A self reference.
|
||||
*/
|
||||
setAdbPort(port) {
|
||||
return this.addArguments('--adb-port=' + port)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of the log file the driver should log to. If a log file is
|
||||
* not specified, the driver will log to stderr.
|
||||
* @param {string} path Path of the log file to use.
|
||||
* @return {!ServiceBuilder} A self reference.
|
||||
*/
|
||||
loggingTo(path) {
|
||||
return this.addArguments('--log-path=' + path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Chrome logging.
|
||||
* @returns {!ServiceBuilder} A self reference.
|
||||
*/
|
||||
enableChromeLogging() {
|
||||
return this.addArguments('--enable-chrome-logs')
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables verbose logging.
|
||||
* @return {!ServiceBuilder} A self reference.
|
||||
*/
|
||||
enableVerboseLogging() {
|
||||
return this.addArguments('--verbose')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of threads the driver should use to manage HTTP requests.
|
||||
* By default, the driver will use 4 threads.
|
||||
* @param {number} n The number of threads to use.
|
||||
* @return {!ServiceBuilder} A self reference.
|
||||
*/
|
||||
setNumHttpThreads(n) {
|
||||
return this.addArguments('--http-threads=' + n)
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setPath(path) {
|
||||
super.setPath(path)
|
||||
return this.addArguments('--url-base=' + path)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for managing WebDriver options specific to a Chromium-based browser.
|
||||
*/
|
||||
class Options extends Capabilities {
|
||||
/**
|
||||
* @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
|
||||
* capabilities to initialize this instance from.
|
||||
*/
|
||||
constructor(other = undefined) {
|
||||
super(other)
|
||||
|
||||
/** @private {!Object} */
|
||||
this.options_ = this.get(this.CAPABILITY_KEY) || {}
|
||||
|
||||
this.setBrowserName(this.BROWSER_NAME_VALUE)
|
||||
this.set(this.CAPABILITY_KEY, this.options_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional command line arguments to use when launching the browser.
|
||||
* Each argument may be specified with or without the "--" prefix
|
||||
* (e.g. "--foo" and "foo"). Arguments with an associated value should be
|
||||
* delimited by an "=": "foo=bar".
|
||||
*
|
||||
* @param {...(string|!Array<string>)} args The arguments to add.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
addArguments(...args) {
|
||||
let newArgs = (this.options_.args || []).concat(...args)
|
||||
if (newArgs.length) {
|
||||
this.options_.args = newArgs
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the address of a Chromium remote debugging server to connect to.
|
||||
* Address should be of the form "{hostname|IP address}:port"
|
||||
* (e.g. "localhost:9222").
|
||||
*
|
||||
* @param {string} address The address to connect to.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
debuggerAddress(address) {
|
||||
this.options_.debuggerAddress = address
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link Options#addArguments} instead.
|
||||
* @example
|
||||
* options.addArguments('--headless=chrome'); (or)
|
||||
* options.addArguments('--headless');
|
||||
* @example
|
||||
*
|
||||
* Recommended to use '--headless=chrome' as argument for browsers v94-108.
|
||||
* Recommended to use '--headless=new' as argument for browsers v109+.
|
||||
*
|
||||
* Configures the driver to start the browser in headless mode.
|
||||
*
|
||||
* > __NOTE:__ Resizing the browser window in headless mode is only supported
|
||||
* > in Chromium 60+. Users are encouraged to set an initial window size with
|
||||
* > the {@link #windowSize windowSize({width, height})} option.
|
||||
*
|
||||
* > __NOTE__: For security, Chromium disables downloads by default when
|
||||
* > in headless mode (to prevent sites from silently downloading files to
|
||||
* > your machine). After creating a session, you may call
|
||||
* > {@link ./chrome.Driver#setDownloadPath setDownloadPath} to re-enable
|
||||
* > downloads, saving files in the specified directory.
|
||||
*
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
headless() {
|
||||
return this.addArguments('headless')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial window size.
|
||||
*
|
||||
* @param {{width: number, height: number}} size The desired window size.
|
||||
* @return {!Options} A self reference.
|
||||
* @throws {TypeError} if width or height is unspecified, not a number, or
|
||||
* less than or equal to 0.
|
||||
*/
|
||||
windowSize({ width, height }) {
|
||||
function checkArg(arg) {
|
||||
if (typeof arg !== 'number' || arg <= 0) {
|
||||
throw TypeError('Arguments must be {width, height} with numbers > 0')
|
||||
}
|
||||
}
|
||||
checkArg(width)
|
||||
checkArg(height)
|
||||
return this.addArguments(`window-size=${width},${height}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* List of Chrome command line switches to exclude that ChromeDriver by default
|
||||
* passes when starting Chrome. Do not prefix switches with "--".
|
||||
*
|
||||
* @param {...(string|!Array<string>)} args The switches to exclude.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
excludeSwitches(...args) {
|
||||
let switches = (this.options_.excludeSwitches || []).concat(...args)
|
||||
if (switches.length) {
|
||||
this.options_.excludeSwitches = switches
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional extensions to install when launching the browser. Each extension
|
||||
* should be specified as the path to the packed CRX file, or a Buffer for an
|
||||
* extension.
|
||||
* @param {...(string|!Buffer|!Array<(string|!Buffer)>)} args The
|
||||
* extensions to add.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
addExtensions(...args) {
|
||||
let extensions = this.options_.extensions || new Extensions()
|
||||
extensions.add(...args)
|
||||
if (extensions.length) {
|
||||
this.options_.extensions = extensions
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the browser binary to use. On Mac OS X, this path should
|
||||
* reference the actual Chromium executable, not just the application binary
|
||||
* (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
|
||||
*
|
||||
* The binary path can be absolute or relative to the WebDriver server
|
||||
* executable, but it must exist on the machine that will launch the browser.
|
||||
*
|
||||
* @param {string} path The path to the browser binary to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setBinaryPath(path) {
|
||||
this.options_.binary = path
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to leave the started browser process running if the controlling
|
||||
* driver service is killed before {@link webdriver.WebDriver#quit()} is
|
||||
* called.
|
||||
* @param {boolean} detach Whether to leave the browser running if the
|
||||
* driver service is killed before the session.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
detachDriver(detach) {
|
||||
this.options_.detach = detach
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user preferences for Chrome's user profile. See the "Preferences"
|
||||
* file in Chrome's user data directory for examples.
|
||||
* @param {!Object} prefs Dictionary of user preferences to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setUserPreferences(prefs) {
|
||||
this.options_.prefs = prefs
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the performance logging preferences. Options include:
|
||||
*
|
||||
* - `enableNetwork`: Whether or not to collect events from Network domain.
|
||||
* - `enablePage`: Whether or not to collect events from Page domain.
|
||||
* - `enableTimeline`: Whether or not to collect events from Timeline domain.
|
||||
* Note: when tracing is enabled, Timeline domain is implicitly disabled,
|
||||
* unless `enableTimeline` is explicitly set to true.
|
||||
* - `traceCategories`: A comma-separated string of Chromium tracing
|
||||
* categories for which trace events should be collected. An unspecified
|
||||
* or empty string disables tracing.
|
||||
* - `bufferUsageReportingInterval`: The requested number of milliseconds
|
||||
* between DevTools trace buffer usage events. For example, if 1000, then
|
||||
* once per second, DevTools will report how full the trace buffer is. If
|
||||
* a report indicates the buffer usage is 100%, a warning will be issued.
|
||||
*
|
||||
* @param {{enableNetwork: boolean,
|
||||
* enablePage: boolean,
|
||||
* enableTimeline: boolean,
|
||||
* traceCategories: string,
|
||||
* bufferUsageReportingInterval: number}} prefs The performance
|
||||
* logging preferences.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setPerfLoggingPrefs(prefs) {
|
||||
this.options_.perfLoggingPrefs = prefs
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets preferences for the "Local State" file in Chrome's user data
|
||||
* directory.
|
||||
* @param {!Object} state Dictionary of local state preferences.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setLocalState(state) {
|
||||
this.options_.localState = state
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the activity hosting a Chrome-based Android WebView. This
|
||||
* option must be set to connect to an [Android WebView](
|
||||
* https://chromedriver.chromium.org/getting-started/getting-started---android)
|
||||
*
|
||||
* @param {string} name The activity name.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
androidActivity(name) {
|
||||
this.options_.androidActivity = name
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the device serial number to connect to via ADB. If not specified, the
|
||||
* WebDriver server will select an unused device at random. An error will be
|
||||
* returned if all devices already have active sessions.
|
||||
*
|
||||
* @param {string} serial The device serial number to connect to.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
androidDeviceSerial(serial) {
|
||||
this.options_.androidDeviceSerial = serial
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the package name of the Chrome or WebView app.
|
||||
*
|
||||
* @param {?string} pkg The package to connect to, or `null` to disable Android
|
||||
* and switch back to using desktop browser.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
androidPackage(pkg) {
|
||||
this.options_.androidPackage = pkg
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the process name of the Activity hosting the WebView (as given by
|
||||
* `ps`). If not specified, the process name is assumed to be the same as
|
||||
* {@link #androidPackage}.
|
||||
*
|
||||
* @param {string} processName The main activity name.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
androidProcess(processName) {
|
||||
this.options_.androidProcess = processName
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether to connect to an already-running instead of the specified
|
||||
* {@linkplain #androidProcess app} instead of launching the app with a clean
|
||||
* data directory.
|
||||
*
|
||||
* @param {boolean} useRunning Whether to connect to a running instance.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
androidUseRunningApp(useRunning) {
|
||||
this.options_.androidUseRunningApp = useRunning
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the browser's log file. This path should exist on the machine
|
||||
* that will launch the browser.
|
||||
* @param {string} path Path to the log file to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setBrowserLogFile(path) {
|
||||
this.options_.logPath = path
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the directory to store browser minidumps in. This option is only
|
||||
* supported when the driver is running on Linux.
|
||||
* @param {string} path The directory path.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setBrowserMinidumpPath(path) {
|
||||
this.options_.minidumpPath = path
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the browser to emulate a mobile device. For more information, refer
|
||||
* to the ChromeDriver project page on [mobile emulation][em]. Configuration
|
||||
* options include:
|
||||
*
|
||||
* - `deviceName`: The name of a pre-configured [emulated device][devem]
|
||||
* - `width`: screen width, in pixels
|
||||
* - `height`: screen height, in pixels
|
||||
* - `pixelRatio`: screen pixel ratio
|
||||
*
|
||||
* __Example 1: Using a Pre-configured Device__
|
||||
*
|
||||
* let options = new chrome.Options().setMobileEmulation(
|
||||
* {deviceName: 'Google Nexus 5'});
|
||||
*
|
||||
* let driver = chrome.Driver.createSession(options);
|
||||
*
|
||||
* __Example 2: Using Custom Screen Configuration__
|
||||
*
|
||||
* let options = new chrome.Options().setMobileEmulation({deviceMetrics: {
|
||||
* width: 360,
|
||||
* height: 640,
|
||||
* pixelRatio: 3.0
|
||||
* }});
|
||||
*
|
||||
* let driver = chrome.Driver.createSession(options);
|
||||
*
|
||||
*
|
||||
* [em]: https://chromedriver.chromium.org/mobile-emulation
|
||||
* [devem]: https://developer.chrome.com/devtools/docs/device-mode
|
||||
*
|
||||
* @param {?({deviceName: string}|
|
||||
* {width: number, height: number, pixelRatio: number})} config The
|
||||
* mobile emulation configuration, or `null` to disable emulation.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setMobileEmulation(config) {
|
||||
this.options_.mobileEmulation = config
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of the window types that will appear when getting window
|
||||
* handles. For access to <webview> elements, include "webview" in the list.
|
||||
* @param {...(string|!Array<string>)} args The window types that will appear
|
||||
* when getting window handles.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
windowTypes(...args) {
|
||||
let windowTypes = (this.options_.windowTypes || []).concat(...args)
|
||||
if (windowTypes.length) {
|
||||
this.options_.windowTypes = windowTypes
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable bidi connection
|
||||
* @returns {!Capabilities}
|
||||
*/
|
||||
enableBidi() {
|
||||
return this.set('webSocketUrl', true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of extensions to install when launching the browser.
|
||||
*/
|
||||
class Extensions {
|
||||
constructor() {
|
||||
this.extensions = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {number} The length of the extensions list.
|
||||
*/
|
||||
get length() {
|
||||
return this.extensions.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional extensions to install when launching the browser. Each
|
||||
* extension should be specified as the path to the packed CRX file, or a
|
||||
* Buffer for an extension.
|
||||
*
|
||||
* @param {...(string|!Buffer|!Array<(string|!Buffer)>)} args The
|
||||
* extensions to add.
|
||||
*/
|
||||
add(...args) {
|
||||
this.extensions = this.extensions.concat(...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object} A serialized representation of this Extensions object.
|
||||
*/
|
||||
[Symbols.serialize]() {
|
||||
return this.extensions.map(function (extension) {
|
||||
if (Buffer.isBuffer(extension)) {
|
||||
return extension.toString('base64')
|
||||
}
|
||||
return io
|
||||
.read(/** @type {string} */ (extension))
|
||||
.then((buffer) => buffer.toString('base64'))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebDriver client for Chromium-based browsers.
|
||||
*/
|
||||
class Driver extends webdriver.WebDriver {
|
||||
/**
|
||||
* Creates a new session with the WebDriver server.
|
||||
*
|
||||
* @param {(Capabilities|Options)=} opt_config The configuration options.
|
||||
* @param {(remote.DriverService|http.Executor)=} opt_serviceExecutor Either
|
||||
* a DriverService to use for the remote end, or a preconfigured executor
|
||||
* for an externally managed endpoint. If neither is provided, the
|
||||
* {@linkplain ##getDefaultService default service} will be used by
|
||||
* default.
|
||||
* @return {!Driver} A new driver instance.
|
||||
*/
|
||||
static createSession(caps, opt_serviceExecutor) {
|
||||
let executor
|
||||
let onQuit
|
||||
if (opt_serviceExecutor instanceof http.Executor) {
|
||||
executor = opt_serviceExecutor
|
||||
configureExecutor(executor, this.VENDOR_COMMAND_PREFIX)
|
||||
} else {
|
||||
let service = opt_serviceExecutor || this.getDefaultService()
|
||||
onQuit = () => service.kill()
|
||||
executor = createExecutor(service.start(), this.VENDOR_COMMAND_PREFIX)
|
||||
}
|
||||
|
||||
// W3C spec requires noProxy value to be an array of strings, but Chromium
|
||||
// expects a single host as a string.
|
||||
let proxy = caps.get(Capability.PROXY)
|
||||
if (proxy && Array.isArray(proxy.noProxy)) {
|
||||
proxy.noProxy = proxy.noProxy[0]
|
||||
if (!proxy.noProxy) {
|
||||
proxy.noProxy = undefined
|
||||
}
|
||||
}
|
||||
|
||||
return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a no-op as file detectors are not supported by this
|
||||
* implementation.
|
||||
* @override
|
||||
*/
|
||||
setFileDetector() {}
|
||||
|
||||
/**
|
||||
* Schedules a command to launch Chrome App with given ID.
|
||||
* @param {string} id ID of the App to launch.
|
||||
* @return {!Promise<void>} A promise that will be resolved
|
||||
* when app is launched.
|
||||
*/
|
||||
launchApp(id) {
|
||||
return this.execute(
|
||||
new command.Command(Command.LAUNCH_APP).setParameter('id', id)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a command to get Chromium network emulation settings.
|
||||
* @return {!Promise} A promise that will be resolved when network
|
||||
* emulation settings are retrieved.
|
||||
*/
|
||||
getNetworkConditions() {
|
||||
return this.execute(new command.Command(Command.GET_NETWORK_CONDITIONS))
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a command to delete Chromium network emulation settings.
|
||||
* @return {!Promise} A promise that will be resolved when network
|
||||
* emulation settings have been deleted.
|
||||
*/
|
||||
deleteNetworkConditions() {
|
||||
return this.execute(new command.Command(Command.DELETE_NETWORK_CONDITIONS))
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedules a command to set Chromium network emulation settings.
|
||||
*
|
||||
* __Sample Usage:__
|
||||
*
|
||||
* driver.setNetworkConditions({
|
||||
* offline: false,
|
||||
* latency: 5, // Additional latency (ms).
|
||||
* download_throughput: 500 * 1024, // Maximal aggregated download throughput.
|
||||
* upload_throughput: 500 * 1024 // Maximal aggregated upload throughput.
|
||||
* });
|
||||
*
|
||||
* @param {Object} spec Defines the network conditions to set
|
||||
* @return {!Promise<void>} A promise that will be resolved when network
|
||||
* emulation settings are set.
|
||||
*/
|
||||
setNetworkConditions(spec) {
|
||||
if (!spec || typeof spec !== 'object') {
|
||||
throw TypeError(
|
||||
'setNetworkConditions called with non-network-conditions parameter'
|
||||
)
|
||||
}
|
||||
return this.execute(
|
||||
new command.Command(Command.SET_NETWORK_CONDITIONS).setParameter(
|
||||
'network_conditions',
|
||||
spec
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an arbitrary devtools command to the browser.
|
||||
*
|
||||
* @param {string} cmd The name of the command to send.
|
||||
* @param {Object=} params The command parameters.
|
||||
* @return {!Promise<void>} A promise that will be resolved when the command
|
||||
* has finished.
|
||||
* @see <https://chromedevtools.github.io/devtools-protocol/>
|
||||
*/
|
||||
sendDevToolsCommand(cmd, params = {}) {
|
||||
return this.execute(
|
||||
new command.Command(Command.SEND_DEVTOOLS_COMMAND)
|
||||
.setParameter('cmd', cmd)
|
||||
.setParameter('params', params)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an arbitrary devtools command to the browser and get the result.
|
||||
*
|
||||
* @param {string} cmd The name of the command to send.
|
||||
* @param {Object=} params The command parameters.
|
||||
* @return {!Promise<string>} A promise that will be resolved when the command
|
||||
* has finished.
|
||||
* @see <https://chromedevtools.github.io/devtools-protocol/>
|
||||
*/
|
||||
sendAndGetDevToolsCommand(cmd, params = {}) {
|
||||
return this.execute(
|
||||
new command.Command(Command.SEND_AND_GET_DEVTOOLS_COMMAND)
|
||||
.setParameter('cmd', cmd)
|
||||
.setParameter('params', params)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a permission state to the given value.
|
||||
*
|
||||
* @param {string} name A name of the permission to update.
|
||||
* @param {('granted'|'denied'|'prompt')} state State to set permission to.
|
||||
* @returns {!Promise<Object>} A promise that will be resolved when the
|
||||
* command has finished.
|
||||
* @see <https://w3c.github.io/permissions/#permission-registry> for valid
|
||||
* names
|
||||
*/
|
||||
setPermission(name, state) {
|
||||
return this.execute(
|
||||
new command.Command(Command.SET_PERMISSION)
|
||||
.setParameter('descriptor', { name })
|
||||
.setParameter('state', state)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a DevTools command to change the browser's download directory.
|
||||
*
|
||||
* @param {string} path The desired download directory.
|
||||
* @return {!Promise<void>} A promise that will be resolved when the command
|
||||
* has finished.
|
||||
* @see #sendDevToolsCommand
|
||||
*/
|
||||
async setDownloadPath(path) {
|
||||
if (!path || typeof path !== 'string') {
|
||||
throw new error.InvalidArgumentError('invalid download path')
|
||||
}
|
||||
const stat = await io.stat(path)
|
||||
if (!stat.isDirectory()) {
|
||||
throw new error.InvalidArgumentError('not a directory: ' + path)
|
||||
}
|
||||
return this.sendDevToolsCommand('Page.setDownloadBehavior', {
|
||||
behavior: 'allow',
|
||||
downloadPath: path,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of cast sinks (Cast devices) available to the Chrome media router.
|
||||
*
|
||||
* @return {!promise.Thenable<void>} A promise that will be resolved with an array of Strings
|
||||
* containing the friendly device names of available cast sink targets.
|
||||
*/
|
||||
getCastSinks() {
|
||||
return this.schedule(
|
||||
new command.Command(Command.GET_CAST_SINKS),
|
||||
'Driver.getCastSinks()'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a cast sink (Cast device) as the recipient of media router intents (connect or play).
|
||||
*
|
||||
* @param {String} deviceName name of the target device.
|
||||
* @return {!promise.Thenable<void>} A promise that will be resolved
|
||||
* when the target device has been selected to respond further webdriver commands.
|
||||
*/
|
||||
setCastSinkToUse(deviceName) {
|
||||
return this.schedule(
|
||||
new command.Command(Command.SET_CAST_SINK_TO_USE).setParameter(
|
||||
'sinkName',
|
||||
deviceName
|
||||
),
|
||||
'Driver.setCastSinkToUse(' + deviceName + ')'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates desktop mirroring for the current browser tab on the specified device.
|
||||
*
|
||||
* @param {String} deviceName name of the target device.
|
||||
* @return {!promise.Thenable<void>} A promise that will be resolved
|
||||
* when the mirror command has been issued to the device.
|
||||
*/
|
||||
startDesktopMirroring(deviceName) {
|
||||
return this.schedule(
|
||||
new command.Command(Command.START_CAST_DESKTOP_MIRRORING).setParameter(
|
||||
'sinkName',
|
||||
deviceName
|
||||
),
|
||||
'Driver.startDesktopMirroring(' + deviceName + ')'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates tab mirroring for the current browser tab on the specified device.
|
||||
*
|
||||
* @param {String} deviceName name of the target device.
|
||||
* @return {!promise.Thenable<void>} A promise that will be resolved
|
||||
* when the mirror command has been issued to the device.
|
||||
*/
|
||||
startCastTabMirroring(deviceName) {
|
||||
return this.schedule(
|
||||
new command.Command(Command.START_CAST_TAB_MIRRORING).setParameter(
|
||||
'sinkName',
|
||||
deviceName
|
||||
),
|
||||
'Driver.startCastTabMirroring(' + deviceName + ')'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error message when there is any issue in a Cast session.
|
||||
* @return {!promise.Thenable<void>} A promise that will be resolved
|
||||
* when the mirror command has been issued to the device.
|
||||
*/
|
||||
getCastIssueMessage() {
|
||||
return this.schedule(
|
||||
new command.Command(Command.GET_CAST_ISSUE_MESSAGE),
|
||||
'Driver.getCastIssueMessage()'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops casting from media router to the specified device, if connected.
|
||||
*
|
||||
* @param {String} deviceName name of the target device.
|
||||
* @return {!promise.Thenable<void>} A promise that will be resolved
|
||||
* when the stop command has been issued to the device.
|
||||
*/
|
||||
stopCasting(deviceName) {
|
||||
return this.schedule(
|
||||
new command.Command(Command.STOP_CASTING).setParameter(
|
||||
'sinkName',
|
||||
deviceName
|
||||
),
|
||||
'Driver.stopCasting(' + deviceName + ')'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Driver,
|
||||
Options,
|
||||
ServiceBuilder,
|
||||
}
|
||||
91
node_modules/selenium-webdriver/common/seleniumManager.js
generated
vendored
Normal file
91
node_modules/selenium-webdriver/common/seleniumManager.js
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* This implementation is still in beta, and may change.
|
||||
*
|
||||
* Wrapper for getting information from the Selenium Manager binaries
|
||||
*/
|
||||
|
||||
const { platform } = require('process')
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const execSync = require('child_process').execSync
|
||||
|
||||
/**
|
||||
* currently supported browsers for selenium-manager
|
||||
* @type {string[]}
|
||||
*/
|
||||
const Browser = ['chrome', 'firefox', 'edge']
|
||||
|
||||
/**
|
||||
* Determines the path of the correct Selenium Manager binary
|
||||
* @returns {string}
|
||||
*/
|
||||
function getBinary() {
|
||||
const directory = {
|
||||
darwin: 'macos',
|
||||
win32: 'windows',
|
||||
cygwin: 'windows',
|
||||
linux: 'linux',
|
||||
}[platform]
|
||||
|
||||
const file =
|
||||
directory === 'windows' ? 'selenium-manager.exe' : 'selenium-manager'
|
||||
|
||||
const filePath = path.join(__dirname, '..', '/bin', directory, file)
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`Unable to obtain Selenium Manager`)
|
||||
}
|
||||
|
||||
return filePath
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the path of the correct driver
|
||||
* @param {Browser|string} browser name to fetch the driver
|
||||
* @returns {string} path of the driver location
|
||||
*/
|
||||
|
||||
function driverLocation(browser) {
|
||||
if (!Browser.includes(browser.toLocaleString())) {
|
||||
throw new Error(
|
||||
`Unable to locate driver associated with browser name: ${browser}`
|
||||
)
|
||||
}
|
||||
|
||||
let args = [getBinary(), '--browser', browser]
|
||||
let result
|
||||
|
||||
try {
|
||||
result = execSync(args.join(' ')).toString()
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Error executing command with ${args}\n${e.stdout.toString()}${e.stderr.toString()}`
|
||||
)
|
||||
}
|
||||
|
||||
if (!result.startsWith('INFO\t')) {
|
||||
throw new Error(`Unsuccessful command executed: ${args}\n${result}`)
|
||||
}
|
||||
|
||||
return result.replace('INFO\t', '').trim()
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = { driverLocation }
|
||||
40
node_modules/selenium-webdriver/devtools/CDPConnection.js
generated
vendored
Normal file
40
node_modules/selenium-webdriver/devtools/CDPConnection.js
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
class CDPConnection {
|
||||
constructor(wsConnection) {
|
||||
this._wsConnection = wsConnection
|
||||
this.cmd_id = 0
|
||||
this.targetID = null
|
||||
this.sessionId = null
|
||||
}
|
||||
|
||||
execute(method, params, callback) {
|
||||
let message = {
|
||||
method,
|
||||
id: this.cmd_id++,
|
||||
}
|
||||
if (this.sessionId) {
|
||||
message['sessionId'] = this.sessionId
|
||||
}
|
||||
|
||||
const mergedMessage = Object.assign({ params: params }, message)
|
||||
this._wsConnection.send(JSON.stringify(mergedMessage), callback)
|
||||
}
|
||||
}
|
||||
|
||||
exports.CdpConnection = CDPConnection
|
||||
88
node_modules/selenium-webdriver/devtools/networkinterceptor.js
generated
vendored
Normal file
88
node_modules/selenium-webdriver/devtools/networkinterceptor.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
class HttpResponse {
|
||||
/**
|
||||
* Creates a HTTP Response that will be used to
|
||||
* mock out network interceptions.
|
||||
* @param {*} urlToIntercept
|
||||
*/
|
||||
constructor(urlToIntercept = '') {
|
||||
this.returnBody = ''
|
||||
this.returnHeaders = []
|
||||
this.returnMethod = 'GET'
|
||||
this.returnStatus = 200
|
||||
this.urlToIntercept = urlToIntercept
|
||||
}
|
||||
|
||||
/**
|
||||
* Add headers that will be returned when we intercept
|
||||
* a HTTP Request
|
||||
* @param {*} header
|
||||
* @param {*} value
|
||||
*/
|
||||
addHeaders(header, value) {
|
||||
this.returnHeaders.push({ name: header, value: value })
|
||||
}
|
||||
|
||||
get headers() {
|
||||
return this.returnHeaders
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the STATUS value of the returned HTTP Request
|
||||
* @param {*} value
|
||||
*/
|
||||
set status(value) {
|
||||
// Add in check that his should be a number
|
||||
this.returnStatus = value
|
||||
}
|
||||
|
||||
get status() {
|
||||
return this.returnStatus
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of the body of the HTTP Request that
|
||||
* will be returned.
|
||||
* @param {*} value
|
||||
*/
|
||||
set body(value) {
|
||||
this.returnBody = value
|
||||
}
|
||||
|
||||
get body() {
|
||||
let buff = Buffer.from(this.returnBody, 'utf-8')
|
||||
return buff.toString('base64')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the method of the HTTP Request
|
||||
* @param {*} value the method of the request.
|
||||
*/
|
||||
set method(value) {
|
||||
this.returnMethod = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Method to be used in the intercept
|
||||
*/
|
||||
get method() {
|
||||
return this.returnMethod
|
||||
}
|
||||
}
|
||||
exports.HttpResponse = HttpResponse
|
||||
211
node_modules/selenium-webdriver/edge.js
generated
vendored
Normal file
211
node_modules/selenium-webdriver/edge.js
generated
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a {@linkplain Driver WebDriver} client for
|
||||
* Microsoft's Edge web browser. Edge (Chromium) is supported and support
|
||||
* for Edge Legacy (EdgeHTML) as part of https://github.com/SeleniumHQ/selenium/issues/9166.
|
||||
* Before using this module, you must download and install the correct
|
||||
* [WebDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/) server.
|
||||
*
|
||||
* Ensure that the msedgedriver (Chromium)
|
||||
* is on your [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29).
|
||||
*
|
||||
* You may use {@link Options} to specify whether Edge Chromium options should be used:
|
||||
|
||||
* const edge = require('selenium-webdriver/edge');
|
||||
* const options = new edge.Options();
|
||||
|
||||
* There are three primary classes exported by this module:
|
||||
*
|
||||
* 1. {@linkplain ServiceBuilder}: configures the
|
||||
* {@link ./remote.DriverService remote.DriverService}
|
||||
* that manages the [WebDriver] child process.
|
||||
*
|
||||
* 2. {@linkplain Options}: defines configuration options for each new
|
||||
* WebDriver session, such as which
|
||||
* {@linkplain Options#setProxy proxy} to use when starting the browser.
|
||||
*
|
||||
* 3. {@linkplain Driver}: the WebDriver client; each new instance will control
|
||||
* a unique browser session.
|
||||
*
|
||||
* __Customizing the WebDriver Server__ <a id="custom-server"></a>
|
||||
*
|
||||
* By default, every MicrosoftEdge session will use a single driver service,
|
||||
* which is started the first time a {@link Driver} instance is created and
|
||||
* terminated when this process exits. The default service will inherit its
|
||||
* environment from the current process.
|
||||
* You may obtain a handle to this default service using
|
||||
* {@link #getDefaultService getDefaultService()} and change its configuration
|
||||
* with {@link #setDefaultService setDefaultService()}.
|
||||
*
|
||||
* You may also create a {@link Driver} with its own driver service. This is
|
||||
* useful if you need to capture the server's log output for a specific session:
|
||||
*
|
||||
* const edge = require('selenium-webdriver/edge');
|
||||
*
|
||||
* const service = new edge.ServiceBuilder()
|
||||
* .setPort(55555)
|
||||
* .build();
|
||||
*
|
||||
* let options = new edge.Options();
|
||||
* // configure browser options ...
|
||||
*
|
||||
* let driver = edge.Driver.createSession(options, service);
|
||||
*
|
||||
* Users should only instantiate the {@link Driver} class directly when they
|
||||
* need a custom driver service configuration (as shown above). For normal
|
||||
* operation, users should start msedgedriver using the
|
||||
* {@link ./builder.Builder selenium-webdriver.Builder}.
|
||||
*
|
||||
* [WebDriver (Chromium)]: https://docs.microsoft.com/en-us/microsoft-edge/webdriver-chromium
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { Browser } = require('./lib/capabilities')
|
||||
const io = require('./io')
|
||||
const chromium = require('./chromium')
|
||||
const { driverLocation } = require('./common/seleniumManager')
|
||||
|
||||
/**
|
||||
* Name of the EdgeDriver executable.
|
||||
* @type {string}
|
||||
* @const
|
||||
*/
|
||||
const EDGEDRIVER_CHROMIUM_EXE =
|
||||
process.platform === 'win32' ? 'msedgedriver.exe' : 'msedgedriver'
|
||||
|
||||
/** @type {remote.DriverService} */
|
||||
|
||||
/**
|
||||
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
||||
* a [MSEdgeDriver](https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/)
|
||||
* server in a child process.
|
||||
*/
|
||||
class ServiceBuilder extends chromium.ServiceBuilder {
|
||||
/**
|
||||
* @param {string=} opt_exe Path to the server executable to use. If omitted,
|
||||
* the builder will attempt to locate the msedgedriver on the current
|
||||
* PATH.
|
||||
* @throws {Error} If provided executable does not exist, or the msedgedriver
|
||||
* cannot be found on the PATH.
|
||||
*/
|
||||
constructor(opt_exe) {
|
||||
let exe = opt_exe || locateSynchronously()
|
||||
|
||||
if (!exe) {
|
||||
console.log(
|
||||
`The WebDriver for Edge could not be found on the current PATH, trying Selenium Manager`
|
||||
)
|
||||
|
||||
try {
|
||||
exe = driverLocation('edge')
|
||||
} catch (err) {
|
||||
console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!exe) {
|
||||
throw Error(
|
||||
`The WebDriver for Edge could not be found on the current PATH. Please download the ` +
|
||||
`latest version of ${EDGEDRIVER_CHROMIUM_EXE} from ` +
|
||||
`https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/ ` +
|
||||
`and ensure it can be found on your PATH.`
|
||||
)
|
||||
}
|
||||
|
||||
super(exe)
|
||||
this.setLoopback(true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for managing edge chromium specific options.
|
||||
*/
|
||||
class Options extends chromium.Options {
|
||||
/**
|
||||
* Sets the path to the edge binary to use
|
||||
*
|
||||
* The binary path be absolute or relative to the msedgedriver server
|
||||
* executable, but it must exist on the machine that will launch edge chromium.
|
||||
*
|
||||
* @param {string} path The path to the msedgedriver binary to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setEdgeChromiumBinaryPath(path) {
|
||||
return this.setBinaryPath(path)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebDriver client for Microsoft's Edge.
|
||||
*/
|
||||
class Driver extends chromium.Driver {
|
||||
/**
|
||||
* Creates a new browser session for Microsoft's Edge browser.
|
||||
*
|
||||
* @param {(Capabilities|Options)=} opt_config The configuration options.
|
||||
* @param {remote.DriverService=} opt_serviceExecutor The service to use; will create
|
||||
* a new Legacy or Chromium service based on {@linkplain Options} by default.
|
||||
* @return {!Driver} A new driver instance.
|
||||
*/
|
||||
static createSession(opt_config, opt_serviceExecutor) {
|
||||
let caps = opt_config || new Options()
|
||||
return /** @type {!Driver} */ (
|
||||
super.createSession(caps, opt_serviceExecutor)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* returns new instance of edge driver service
|
||||
* @returns {remote.DriverService}
|
||||
*/
|
||||
static getDefaultService() {
|
||||
return new ServiceBuilder().build()
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a no-op as file detectors are not supported by this
|
||||
* implementation.
|
||||
* @override
|
||||
*/
|
||||
setFileDetector() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* _Synchronously_ attempts to locate the chromedriver executable on the current
|
||||
* system.
|
||||
*
|
||||
* @return {?string} the located executable, or `null`.
|
||||
*/
|
||||
function locateSynchronously() {
|
||||
return io.findInPath(EDGEDRIVER_CHROMIUM_EXE, true)
|
||||
}
|
||||
|
||||
Options.prototype.BROWSER_NAME_VALUE = Browser.EDGE
|
||||
Options.prototype.CAPABILITY_KEY = 'ms:edgeOptions'
|
||||
Driver.prototype.VENDOR_CAPABILITY_PREFIX = 'ms'
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Driver,
|
||||
Options,
|
||||
ServiceBuilder,
|
||||
locateSynchronously,
|
||||
}
|
||||
45
node_modules/selenium-webdriver/example/chrome_android.js
generated
vendored
Normal file
45
node_modules/selenium-webdriver/example/chrome_android.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview A basic example of working with Chrome on Android. Before
|
||||
* running this example, you must start adb and connect a device (or start an
|
||||
* AVD).
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { Builder, By, Key, until } = require('..')
|
||||
const { Options } = require('../chrome')
|
||||
|
||||
;(async function () {
|
||||
let driver
|
||||
try {
|
||||
driver = await new Builder()
|
||||
.forBrowser('chrome')
|
||||
.setChromeOptions(new Options().androidChrome())
|
||||
.build()
|
||||
await driver.get('http://www.google.com/ncr')
|
||||
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
|
||||
} finally {
|
||||
;(await driver) && driver.quit()
|
||||
}
|
||||
})().then(
|
||||
(_) => console.log('SUCCESS'),
|
||||
(err) => console.error('ERROR: ' + err)
|
||||
)
|
||||
46
node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
generated
vendored
Normal file
46
node_modules/selenium-webdriver/example/chrome_mobile_emulation.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview This is an example of emulating a mobile device using the
|
||||
* ChromeDriver.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { Builder, By, Key, until } = require('..')
|
||||
const { Options } = require('../chrome')
|
||||
|
||||
;(async function () {
|
||||
let driver
|
||||
try {
|
||||
driver = await new Builder()
|
||||
.forBrowser('chrome')
|
||||
.setChromeOptions(
|
||||
new Options().setMobileEmulation({ deviceName: 'Nexus 5X' })
|
||||
)
|
||||
.build()
|
||||
await driver.get('http://www.google.com/ncr')
|
||||
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
|
||||
} finally {
|
||||
;(await driver) && driver.quit()
|
||||
}
|
||||
})().then(
|
||||
(_) => console.log('SUCCESS'),
|
||||
(err) => console.error('ERROR: ' + err)
|
||||
)
|
||||
84
node_modules/selenium-webdriver/example/firefox_channels.js
generated
vendored
Normal file
84
node_modules/selenium-webdriver/example/firefox_channels.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview This is an example of working with the different Firefox
|
||||
* release channels. Before running this example, you will need to have
|
||||
* installed Firefox's release, nightly, and developer editions:
|
||||
*
|
||||
* - https://www.mozilla.org/en-US/firefox/channel/desktop/#aurora
|
||||
* - https://www.mozilla.org/en-US/firefox/channel/desktop/#nightly
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { Builder, By, Key, promise, until } = require('..')
|
||||
const { Channel, Options } = require('../firefox')
|
||||
|
||||
let i = 0
|
||||
function resposition(driver) {
|
||||
return driver
|
||||
.manage()
|
||||
.window()
|
||||
.setRect({
|
||||
width: 600,
|
||||
height: 400,
|
||||
x: 300 * i++,
|
||||
y: 0,
|
||||
})
|
||||
}
|
||||
|
||||
async function doSearch(driver) {
|
||||
try {
|
||||
// Start on the base about page.
|
||||
await driver.get('about:')
|
||||
// Reposition so users can see the three windows.
|
||||
await resposition(driver)
|
||||
// Pause so users can see the magic.
|
||||
await promise.delayed(750)
|
||||
// Now do the rest.
|
||||
await driver.get('http://www.google.com/ncr')
|
||||
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
|
||||
console.log('Success!')
|
||||
} catch (ex) {
|
||||
console.log('An error occured! ' + ex)
|
||||
} finally {
|
||||
await driver.quit()
|
||||
}
|
||||
}
|
||||
|
||||
function createDriver(channel) {
|
||||
let options = new Options().setBinary(channel)
|
||||
return new Builder().forBrowser('firefox').setFirefoxOptions(options).build()
|
||||
}
|
||||
|
||||
Promise.all([
|
||||
doSearch(createDriver(Channel.RELEASE)),
|
||||
doSearch(createDriver(Channel.AURORA)), // Developer Edition.
|
||||
doSearch(createDriver(Channel.NIGHTLY)),
|
||||
]).then(
|
||||
(_) => {
|
||||
console.log('All done!')
|
||||
},
|
||||
(err) => {
|
||||
console.error('An error occured! ' + err)
|
||||
setTimeout(() => {
|
||||
throw err
|
||||
}, 0)
|
||||
}
|
||||
)
|
||||
50
node_modules/selenium-webdriver/example/google_search.js
generated
vendored
Normal file
50
node_modules/selenium-webdriver/example/google_search.js
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview An example WebDriver script.
|
||||
*
|
||||
* Before running this script, ensure that Mozilla's geckodriver is present on
|
||||
* your system PATH: <https://github.com/mozilla/geckodriver/releases>
|
||||
*
|
||||
* Usage:
|
||||
* // Default behavior
|
||||
* node selenium-webdriver/example/google_search.js
|
||||
*
|
||||
* // Target Chrome locally; the chromedriver must be on your PATH
|
||||
* SELENIUM_BROWSER=chrome node selenium-webdriver/example/google_search.js
|
||||
*
|
||||
* // Use a local copy of the standalone Selenium server
|
||||
* SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
|
||||
* node selenium-webdriver/example/google_search.js
|
||||
*
|
||||
* // Target a remote Selenium server
|
||||
* SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
|
||||
* node selenium-webdriver/example/google_search.js
|
||||
*/
|
||||
|
||||
const { Builder, By, Key, until } = require('..')
|
||||
|
||||
const driver = new Builder().forBrowser('firefox').build()
|
||||
|
||||
driver
|
||||
.get('http://www.google.com/ncr')
|
||||
.then((_) =>
|
||||
driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
)
|
||||
.then((_) => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
|
||||
.then((_) => driver.quit())
|
||||
70
node_modules/selenium-webdriver/example/google_search_test.js
generated
vendored
Normal file
70
node_modules/selenium-webdriver/example/google_search_test.js
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview An example test that may be run using Mocha.
|
||||
*
|
||||
* This example uses the `selenium-webdriver/testing.suite` function, which will
|
||||
* automatically run tests against every available WebDriver browser on the
|
||||
* current system. Alternatively, you may use the `SELENIUM_BROWSER`
|
||||
* environment variable to narrow the scope at runtime.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* # Automatically determine which browsers to run against.
|
||||
* mocha -t 10000 selenium-webdriver/example/google_search_test.js
|
||||
*
|
||||
* # Configure tests to only run against Google Chrome.
|
||||
* SELENIUM_BROWSER=chrome \
|
||||
* mocha -t 10000 selenium-webdriver/example/google_search_test.js
|
||||
*/
|
||||
|
||||
const { Browser, By, Key, until } = require('..')
|
||||
const { ignore, suite } = require('../testing')
|
||||
|
||||
suite(function (env) {
|
||||
describe('Google Search', function () {
|
||||
let driver
|
||||
|
||||
before(async function () {
|
||||
// env.builder() returns a Builder instance preconfigured for the
|
||||
// envrionment's target browser (you may still define browser specific
|
||||
// options if necessary (i.e. firefox.Options or chrome.Options)).
|
||||
driver = await env.builder().build()
|
||||
})
|
||||
|
||||
it('demo', async function () {
|
||||
await driver.get('https://www.google.com/ncr')
|
||||
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
|
||||
})
|
||||
|
||||
// The ignore function returns wrappers around describe & it that will
|
||||
// suppress tests if the provided predicate returns true. You may provide
|
||||
// any synchronous predicate. The env.browsers(...) function generates a
|
||||
// predicate that will suppress tests if the env targets one of the
|
||||
// specified browsers.
|
||||
//
|
||||
// This example is always configured to skip Chrome.
|
||||
ignore(env.browsers(Browser.CHROME)).it('demo 2', async function () {
|
||||
await driver.get('https://www.google.com/ncr')
|
||||
await driver.wait(until.urlIs('https://www.google.com/'), 1500)
|
||||
})
|
||||
|
||||
after(() => driver && driver.quit())
|
||||
})
|
||||
})
|
||||
63
node_modules/selenium-webdriver/example/headless.js
generated
vendored
Normal file
63
node_modules/selenium-webdriver/example/headless.js
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview An example of running Chrome or Firefox in headless mode.
|
||||
*
|
||||
* To run with Chrome, ensure you have Chrome 59+ installed and that
|
||||
* chromedriver 2.30+ is present on your system PATH:
|
||||
* <https://chromedriver.chromium.org/downloads>
|
||||
*
|
||||
* SELENIUM_BROWSER=chrome node selenium-webdriver/example/headless.js
|
||||
*
|
||||
* To run with Firefox, ensure you have Firefox 57+ installed and that
|
||||
* geckodriver 0.19.0+ is present on your system PATH:
|
||||
* <https://github.com/mozilla/geckodriver/releases>
|
||||
*
|
||||
* SELENIUM_BROWSER=firefox node selenium-webdriver/example/headless.js
|
||||
*/
|
||||
|
||||
const chrome = require('../chrome')
|
||||
const firefox = require('../firefox')
|
||||
const { Builder, By, Key, until } = require('..')
|
||||
|
||||
const width = 640
|
||||
const height = 480
|
||||
|
||||
let driver = new Builder()
|
||||
.forBrowser('chrome')
|
||||
.setChromeOptions(
|
||||
new chrome.Options().headless().windowSize({ width, height })
|
||||
)
|
||||
.setFirefoxOptions(
|
||||
new firefox.Options().headless().windowSize({ width, height })
|
||||
)
|
||||
.build()
|
||||
|
||||
driver
|
||||
.get('http://www.google.com/ncr')
|
||||
.then((_) =>
|
||||
driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
)
|
||||
.then((_) => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
|
||||
.then(
|
||||
(_) => driver.quit(),
|
||||
(e) =>
|
||||
driver.quit().then(() => {
|
||||
throw e
|
||||
})
|
||||
)
|
||||
64
node_modules/selenium-webdriver/example/logging.js
generated
vendored
Normal file
64
node_modules/selenium-webdriver/example/logging.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Demonstrates how to use WebDriver's logging sysem.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const chrome = require('../chrome')
|
||||
const firefox = require('../firefox')
|
||||
const edge = require('../edge')
|
||||
const { Builder, By, Key, logging, until } = require('..')
|
||||
|
||||
logging.installConsoleHandler()
|
||||
logging.getLogger('webdriver.http').setLevel(logging.Level.ALL)
|
||||
;(async function () {
|
||||
let driver
|
||||
try {
|
||||
driver = await new Builder()
|
||||
// Default to using Firefox. This can be overridden at runtime by
|
||||
// setting the SELENIUM_BROWSER environment variable:
|
||||
//
|
||||
// SELENIUM_BROWSER=chrome node selenium-webdriver/example/logging.js
|
||||
.forBrowser('firefox')
|
||||
// Configure the service for each browser to enable verbose logging and
|
||||
// to inherit the stdio settings from the current process. The builder
|
||||
// will only start the service if needed for the selected browser.
|
||||
.setChromeService(
|
||||
new chrome.ServiceBuilder().enableVerboseLogging().setStdio('inherit')
|
||||
)
|
||||
.setEdgeService(
|
||||
process.platform === 'win32'
|
||||
? new edge.ServiceBuilder().enableVerboseLogging().setStdio('inherit')
|
||||
: null
|
||||
)
|
||||
.setFirefoxService(
|
||||
new firefox.ServiceBuilder().enableVerboseLogging().setStdio('inherit')
|
||||
)
|
||||
.build()
|
||||
|
||||
await driver.get('http://www.google.com/ncr')
|
||||
await driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN)
|
||||
await driver.wait(until.titleIs('webdriver - Google Search'), 1000)
|
||||
} finally {
|
||||
if (driver) {
|
||||
await driver.quit()
|
||||
}
|
||||
}
|
||||
})()
|
||||
852
node_modules/selenium-webdriver/firefox.js
generated
vendored
Normal file
852
node_modules/selenium-webdriver/firefox.js
generated
vendored
Normal file
@@ -0,0 +1,852 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines the {@linkplain Driver WebDriver} client for Firefox.
|
||||
* Before using this module, you must download the latest
|
||||
* [geckodriver release] and ensure it can be found on your system [PATH].
|
||||
*
|
||||
* Each FirefoxDriver instance will be created with an anonymous profile,
|
||||
* ensuring browser historys do not share session data (cookies, history, cache,
|
||||
* offline storage, etc.)
|
||||
*
|
||||
* __Customizing the Firefox Profile__
|
||||
*
|
||||
* The profile used for each WebDriver session may be configured using the
|
||||
* {@linkplain Options} class. For example, you may install an extension, like
|
||||
* Firebug:
|
||||
*
|
||||
* const {Builder} = require('selenium-webdriver');
|
||||
* const firefox = require('selenium-webdriver/firefox');
|
||||
*
|
||||
* let options = new firefox.Options()
|
||||
* .addExtensions('/path/to/firebug.xpi')
|
||||
* .setPreference('extensions.firebug.showChromeErrors', true);
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('firefox')
|
||||
* .setFirefoxOptions(options)
|
||||
* .build();
|
||||
*
|
||||
* The {@linkplain Options} class may also be used to configure WebDriver based
|
||||
* on a pre-existing browser profile:
|
||||
*
|
||||
* let profile = '/usr/local/home/bob/.mozilla/firefox/3fgog75h.testing';
|
||||
* let options = new firefox.Options().setProfile(profile);
|
||||
*
|
||||
* The FirefoxDriver will _never_ modify a pre-existing profile; instead it will
|
||||
* create a copy for it to modify. By extension, there are certain browser
|
||||
* preferences that are required for WebDriver to function properly and they
|
||||
* will always be overwritten.
|
||||
*
|
||||
* __Using a Custom Firefox Binary__
|
||||
*
|
||||
* On Windows and MacOS, the FirefoxDriver will search for Firefox in its
|
||||
* default installation location:
|
||||
*
|
||||
* - Windows: C:\Program Files and C:\Program Files (x86).
|
||||
* - MacOS: /Applications/Firefox.app
|
||||
*
|
||||
* For Linux, Firefox will always be located on the PATH: `$(where firefox)`.
|
||||
*
|
||||
* Several methods are provided for starting Firefox with a custom executable.
|
||||
* First, on Windows and MacOS, you may configure WebDriver to check the default
|
||||
* install location for a non-release channel. If the requested channel cannot
|
||||
* be found in its default location, WebDriver will fallback to searching your
|
||||
* PATH. _Note:_ on Linux, Firefox is _always_ located on your path, regardless
|
||||
* of the requested channel.
|
||||
*
|
||||
* const {Builder} = require('selenium-webdriver');
|
||||
* const firefox = require('selenium-webdriver/firefox');
|
||||
*
|
||||
* let options = new firefox.Options().setBinary(firefox.Channel.NIGHTLY);
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('firefox')
|
||||
* .setFirefoxOptions(options)
|
||||
* .build();
|
||||
*
|
||||
* On all platforms, you may configure WebDriver to use a Firefox specific
|
||||
* executable:
|
||||
*
|
||||
* let options = new firefox.Options()
|
||||
* .setBinary('/my/firefox/install/dir/firefox-bin');
|
||||
*
|
||||
* __Remote Testing__
|
||||
*
|
||||
* You may customize the Firefox binary and profile when running against a
|
||||
* remote Selenium server. Your custom profile will be packaged as a zip and
|
||||
* transferred to the remote host for use. The profile will be transferred
|
||||
* _once for each new session_. The performance impact should be minimal if
|
||||
* you've only configured a few extra browser preferences. If you have a large
|
||||
* profile with several extensions, you should consider installing it on the
|
||||
* remote host and defining its path via the {@link Options} class. Custom
|
||||
* binaries are never copied to remote machines and must be referenced by
|
||||
* installation path.
|
||||
*
|
||||
* const {Builder} = require('selenium-webdriver');
|
||||
* const firefox = require('selenium-webdriver/firefox');
|
||||
*
|
||||
* let options = new firefox.Options()
|
||||
* .setProfile('/profile/path/on/remote/host')
|
||||
* .setBinary('/install/dir/on/remote/host/firefox-bin');
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .forBrowser('firefox')
|
||||
* .usingServer('http://127.0.0.1:4444/wd/hub')
|
||||
* .setFirefoxOptions(options)
|
||||
* .build();
|
||||
*
|
||||
* [geckodriver release]: https://github.com/mozilla/geckodriver/releases/
|
||||
* [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const Symbols = require('./lib/symbols')
|
||||
const command = require('./lib/command')
|
||||
const http = require('./http')
|
||||
const io = require('./io')
|
||||
const remote = require('./remote')
|
||||
const webdriver = require('./lib/webdriver')
|
||||
const zip = require('./io/zip')
|
||||
const { Browser, Capabilities } = require('./lib/capabilities')
|
||||
const { Zip } = require('./io/zip')
|
||||
const { driverLocation } = require('./common/seleniumManager')
|
||||
|
||||
/**
|
||||
* Thrown when there an add-on is malformed.
|
||||
* @final
|
||||
*/
|
||||
class AddonFormatError extends Error {
|
||||
/** @param {string} msg The error message. */
|
||||
constructor(msg) {
|
||||
super(msg)
|
||||
/** @override */
|
||||
this.name = this.constructor.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs an extension to the given directory.
|
||||
* @param {string} extension Path to the xpi extension file to install.
|
||||
* @param {string} dir Path to the directory to install the extension in.
|
||||
* @return {!Promise<string>} A promise for the add-on ID once
|
||||
* installed.
|
||||
*/
|
||||
async function installExtension(extension, dir) {
|
||||
const ext = extension.slice(-4)
|
||||
if (ext !== '.xpi' && ext !== '.zip') {
|
||||
throw Error('File name does not end in ".zip" or ".xpi": ' + ext)
|
||||
}
|
||||
|
||||
let archive = await zip.load(extension)
|
||||
if (!archive.has('manifest.json')) {
|
||||
throw new AddonFormatError(`Couldn't find manifest.json in ${extension}`)
|
||||
}
|
||||
|
||||
let buf = await archive.getFile('manifest.json')
|
||||
let parsedJSON = JSON.parse(buf.toString('utf8'))
|
||||
|
||||
let { browser_specific_settings } =
|
||||
/** @type {{browser_specific_settings:{gecko:{id:string}}}} */
|
||||
parsedJSON
|
||||
|
||||
if (browser_specific_settings && browser_specific_settings.gecko) {
|
||||
/* browser_specific_settings is an alternative to applications
|
||||
* It is meant to facilitate cross-browser plugins since Firefox48
|
||||
* see https://bugzilla.mozilla.org/show_bug.cgi?id=1262005
|
||||
*/
|
||||
parsedJSON.applications = browser_specific_settings
|
||||
}
|
||||
|
||||
let { applications } =
|
||||
/** @type {{applications:{gecko:{id:string}}}} */
|
||||
parsedJSON
|
||||
if (!(applications && applications.gecko && applications.gecko.id)) {
|
||||
throw new AddonFormatError(`Could not find add-on ID for ${extension}`)
|
||||
}
|
||||
|
||||
await io.copy(extension, `${path.join(dir, applications.gecko.id)}.xpi`)
|
||||
return applications.gecko.id
|
||||
}
|
||||
|
||||
class Profile {
|
||||
constructor() {
|
||||
/** @private {?string} */
|
||||
this.template_ = null
|
||||
|
||||
/** @private {!Array<string>} */
|
||||
this.extensions_ = []
|
||||
}
|
||||
|
||||
addExtensions(/** !Array<string> */ paths) {
|
||||
this.extensions_ = this.extensions_.concat(...paths)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(!Promise<string>|undefined)} a promise for a base64 encoded
|
||||
* profile, or undefined if there's no data to include.
|
||||
*/
|
||||
[Symbols.serialize]() {
|
||||
if (this.template_ || this.extensions_.length) {
|
||||
return buildProfile(this.template_, this.extensions_)
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?string} template path to an existing profile to use as a template.
|
||||
* @param {!Array<string>} extensions paths to extensions to install in the new
|
||||
* profile.
|
||||
* @return {!Promise<string>} a promise for the base64 encoded profile.
|
||||
*/
|
||||
async function buildProfile(template, extensions) {
|
||||
let dir = template
|
||||
|
||||
if (extensions.length) {
|
||||
dir = await io.tmpDir()
|
||||
if (template) {
|
||||
await io.copyDir(
|
||||
/** @type {string} */ (template),
|
||||
dir,
|
||||
/(parent\.lock|lock|\.parentlock)/
|
||||
)
|
||||
}
|
||||
|
||||
const extensionsDir = path.join(dir, 'extensions')
|
||||
await io.mkdir(extensionsDir)
|
||||
|
||||
for (let i = 0; i < extensions.length; i++) {
|
||||
await installExtension(extensions[i], extensionsDir)
|
||||
}
|
||||
}
|
||||
|
||||
let zip = new Zip()
|
||||
return zip
|
||||
.addDir(dir)
|
||||
.then(() => zip.toBuffer())
|
||||
.then((buf) => buf.toString('base64'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for the FirefoxDriver.
|
||||
*/
|
||||
class Options extends Capabilities {
|
||||
/**
|
||||
* @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
|
||||
* capabilities to initialize this instance from.
|
||||
*/
|
||||
constructor(other) {
|
||||
super(other)
|
||||
this.setBrowserName(Browser.FIREFOX)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object}
|
||||
* @private
|
||||
*/
|
||||
firefoxOptions_() {
|
||||
let options = this.get('moz:firefoxOptions')
|
||||
if (!options) {
|
||||
options = {}
|
||||
this.set('moz:firefoxOptions', options)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Profile}
|
||||
* @private
|
||||
*/
|
||||
profile_() {
|
||||
let options = this.firefoxOptions_()
|
||||
if (!options.profile) {
|
||||
options.profile = new Profile()
|
||||
}
|
||||
return options.profile
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify additional command line arguments that should be used when starting
|
||||
* the Firefox browser.
|
||||
*
|
||||
* @param {...(string|!Array<string>)} args The arguments to include.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
addArguments(...args) {
|
||||
if (args.length) {
|
||||
let options = this.firefoxOptions_()
|
||||
options.args = options.args ? options.args.concat(...args) : args
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link Options#addArguments} instead.
|
||||
* @example
|
||||
* options.addArguments('-headless');
|
||||
* @example
|
||||
* Configures the geckodriver to start Firefox in headless mode.
|
||||
*
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
headless() {
|
||||
return this.addArguments('-headless')
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial window size when running in
|
||||
* {@linkplain #headless headless} mode.
|
||||
*
|
||||
* @param {{width: number, height: number}} size The desired window size.
|
||||
* @return {!Options} A self reference.
|
||||
* @throws {TypeError} if width or height is unspecified, not a number, or
|
||||
* less than or equal to 0.
|
||||
*/
|
||||
windowSize({ width, height }) {
|
||||
function checkArg(arg) {
|
||||
if (typeof arg !== 'number' || arg <= 0) {
|
||||
throw TypeError('Arguments must be {width, height} with numbers > 0')
|
||||
}
|
||||
}
|
||||
checkArg(width)
|
||||
checkArg(height)
|
||||
return this.addArguments(`--width=${width}`, `--height=${height}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extensions that should be installed when starting Firefox.
|
||||
*
|
||||
* @param {...string} paths The paths to the extension XPI files to install.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
addExtensions(...paths) {
|
||||
this.profile_().addExtensions(paths)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key the preference key.
|
||||
* @param {(string|number|boolean)} value the preference value.
|
||||
* @return {!Options} A self reference.
|
||||
* @throws {TypeError} if either the key or value has an invalid type.
|
||||
*/
|
||||
setPreference(key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw TypeError(`key must be a string, but got ${typeof key}`)
|
||||
}
|
||||
if (
|
||||
typeof value !== 'string' &&
|
||||
typeof value !== 'number' &&
|
||||
typeof value !== 'boolean'
|
||||
) {
|
||||
throw TypeError(
|
||||
`value must be a string, number, or boolean, but got ${typeof value}`
|
||||
)
|
||||
}
|
||||
let options = this.firefoxOptions_()
|
||||
options.prefs = options.prefs || {}
|
||||
options.prefs[key] = value
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to an existing profile to use as a template for new browser
|
||||
* sessions. This profile will be copied for each new session - changes will
|
||||
* not be applied to the profile itself.
|
||||
*
|
||||
* @param {string} profile The profile to use.
|
||||
* @return {!Options} A self reference.
|
||||
* @throws {TypeError} if profile is not a string.
|
||||
*/
|
||||
setProfile(profile) {
|
||||
if (typeof profile !== 'string') {
|
||||
throw TypeError(`profile must be a string, but got ${typeof profile}`)
|
||||
}
|
||||
this.profile_().template_ = profile
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the binary to use. The binary may be specified as the path to a
|
||||
* Firefox executable or a desired release {@link Channel}.
|
||||
*
|
||||
* @param {(string|!Channel)} binary The binary to use.
|
||||
* @return {!Options} A self reference.
|
||||
* @throws {TypeError} If `binary` is an invalid type.
|
||||
*/
|
||||
setBinary(binary) {
|
||||
if (binary instanceof Channel || typeof binary === 'string') {
|
||||
this.firefoxOptions_().binary = binary
|
||||
return this
|
||||
}
|
||||
throw TypeError('binary must be a string path or Channel object')
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Mobile start up features
|
||||
*
|
||||
* @param {string} androidPackage The package to use
|
||||
* @return {!Options} A self reference
|
||||
*/
|
||||
enableMobile(
|
||||
androidPackage = 'org.mozilla.firefox',
|
||||
androidActivity = null,
|
||||
deviceSerial = null
|
||||
) {
|
||||
this.firefoxOptions_().androidPackage = androidPackage
|
||||
|
||||
if (androidActivity) {
|
||||
this.firefoxOptions_().androidActivity = androidActivity
|
||||
}
|
||||
if (deviceSerial) {
|
||||
this.firefoxOptions_().deviceSerial = deviceSerial
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables moz:debuggerAddress for firefox cdp
|
||||
*/
|
||||
enableDebugger() {
|
||||
return this.set('moz:debuggerAddress', true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable bidi connection
|
||||
* @returns {!Capabilities}
|
||||
*/
|
||||
enableBidi() {
|
||||
return this.set('webSocketUrl', true)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enum of available command contexts.
|
||||
*
|
||||
* Command contexts are specific to Marionette, and may be used with the
|
||||
* {@link #context=} method. Contexts allow you to direct all subsequent
|
||||
* commands to either "content" (default) or "chrome". The latter gives
|
||||
* you elevated security permissions.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
const Context = {
|
||||
CONTENT: 'content',
|
||||
CHROME: 'chrome',
|
||||
}
|
||||
|
||||
const GECKO_DRIVER_EXE =
|
||||
process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
|
||||
|
||||
/**
|
||||
* _Synchronously_ attempts to locate the geckodriver executable on the current
|
||||
* system.
|
||||
*
|
||||
* @return {?string} the located executable, or `null`.
|
||||
*/
|
||||
function locateSynchronously() {
|
||||
return io.findInPath(GECKO_DRIVER_EXE, true)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string} .
|
||||
* @throws {Error}
|
||||
*/
|
||||
function findGeckoDriver() {
|
||||
let exe = locateSynchronously()
|
||||
|
||||
if (!exe) {
|
||||
console.log(
|
||||
`The ${GECKO_DRIVER_EXE} executable could not be found on the current PATH, trying Selenium Manager`
|
||||
)
|
||||
|
||||
try {
|
||||
exe = driverLocation(Browser.FIREFOX)
|
||||
} catch (err) {
|
||||
console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!exe) {
|
||||
throw Error(
|
||||
`The ${GECKO_DRIVER_EXE} executable could not be found on the current PATH.
|
||||
Please download the latest version from https://github.com/mozilla/geckodriver/releases/
|
||||
and ensure it can be found on your PATH.`
|
||||
)
|
||||
}
|
||||
|
||||
return exe
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} file Path to the file to find, relative to the program files
|
||||
* root.
|
||||
* @return {!Promise<?string>} A promise for the located executable.
|
||||
* The promise will resolve to {@code null} if Firefox was not found.
|
||||
*/
|
||||
function findInProgramFiles(file) {
|
||||
let files = [
|
||||
process.env['PROGRAMFILES'] || 'C:\\Program Files',
|
||||
process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)',
|
||||
].map((prefix) => path.join(prefix, file))
|
||||
return io.exists(files[0]).then(function (exists) {
|
||||
return exists
|
||||
? files[0]
|
||||
: io.exists(files[1]).then(function (exists) {
|
||||
return exists ? files[1] : null
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/** @enum {string} */
|
||||
const ExtensionCommand = {
|
||||
GET_CONTEXT: 'getContext',
|
||||
SET_CONTEXT: 'setContext',
|
||||
INSTALL_ADDON: 'install addon',
|
||||
UNINSTALL_ADDON: 'uninstall addon',
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a command executor with support for Marionette's custom commands.
|
||||
* @param {!Promise<string>} serverUrl The server's URL.
|
||||
* @return {!command.Executor} The new command executor.
|
||||
*/
|
||||
function createExecutor(serverUrl) {
|
||||
let client = serverUrl.then((url) => new http.HttpClient(url))
|
||||
let executor = new http.Executor(client)
|
||||
configureExecutor(executor)
|
||||
return executor
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the given executor with Firefox-specific commands.
|
||||
* @param {!http.Executor} executor the executor to configure.
|
||||
*/
|
||||
function configureExecutor(executor) {
|
||||
executor.defineCommand(
|
||||
ExtensionCommand.GET_CONTEXT,
|
||||
'GET',
|
||||
'/session/:sessionId/moz/context'
|
||||
)
|
||||
|
||||
executor.defineCommand(
|
||||
ExtensionCommand.SET_CONTEXT,
|
||||
'POST',
|
||||
'/session/:sessionId/moz/context'
|
||||
)
|
||||
|
||||
executor.defineCommand(
|
||||
ExtensionCommand.INSTALL_ADDON,
|
||||
'POST',
|
||||
'/session/:sessionId/moz/addon/install'
|
||||
)
|
||||
|
||||
executor.defineCommand(
|
||||
ExtensionCommand.UNINSTALL_ADDON,
|
||||
'POST',
|
||||
'/session/:sessionId/moz/addon/uninstall'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
||||
* a [geckodriver](https://github.com/mozilla/geckodriver) server in a child
|
||||
* process.
|
||||
*/
|
||||
class ServiceBuilder extends remote.DriverService.Builder {
|
||||
/**
|
||||
* @param {string=} opt_exe Path to the server executable to use. If omitted,
|
||||
* the builder will attempt to locate the geckodriver on the system PATH.
|
||||
*/
|
||||
constructor(opt_exe) {
|
||||
super(opt_exe || findGeckoDriver())
|
||||
this.setLoopback(true) // Required.
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables verbose logging.
|
||||
*
|
||||
* @param {boolean=} opt_trace Whether to enable trace-level logging. By
|
||||
* default, only debug logging is enabled.
|
||||
* @return {!ServiceBuilder} A self reference.
|
||||
*/
|
||||
enableVerboseLogging(opt_trace) {
|
||||
return this.addArguments(opt_trace ? '-vv' : '-v')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A WebDriver client for Firefox.
|
||||
*/
|
||||
class Driver extends webdriver.WebDriver {
|
||||
/**
|
||||
* Creates a new Firefox session.
|
||||
*
|
||||
* @param {(Options|Capabilities|Object)=} opt_config The
|
||||
* configuration options for this driver, specified as either an
|
||||
* {@link Options} or {@link Capabilities}, or as a raw hash object.
|
||||
* @param {(http.Executor|remote.DriverService)=} opt_executor Either a
|
||||
* pre-configured command executor to use for communicating with an
|
||||
* externally managed remote end (which is assumed to already be running),
|
||||
* or the `DriverService` to use to start the geckodriver in a child
|
||||
* process.
|
||||
*
|
||||
* If an executor is provided, care should e taken not to use reuse it with
|
||||
* other clients as its internal command mappings will be updated to support
|
||||
* Firefox-specific commands.
|
||||
*
|
||||
* _This parameter may only be used with Mozilla's GeckoDriver._
|
||||
*
|
||||
* @throws {Error} If a custom command executor is provided and the driver is
|
||||
* configured to use the legacy FirefoxDriver from the Selenium project.
|
||||
* @return {!Driver} A new driver instance.
|
||||
*/
|
||||
static createSession(opt_config, opt_executor) {
|
||||
let caps =
|
||||
opt_config instanceof Capabilities ? opt_config : new Options(opt_config)
|
||||
|
||||
let executor
|
||||
let onQuit
|
||||
|
||||
if (opt_executor instanceof http.Executor) {
|
||||
executor = opt_executor
|
||||
configureExecutor(executor)
|
||||
} else if (opt_executor instanceof remote.DriverService) {
|
||||
executor = createExecutor(opt_executor.start())
|
||||
onQuit = () => opt_executor.kill()
|
||||
} else {
|
||||
let service = new ServiceBuilder().build()
|
||||
executor = createExecutor(service.start())
|
||||
onQuit = () => service.kill()
|
||||
}
|
||||
|
||||
return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a no-op as file detectors are not supported by this
|
||||
* implementation.
|
||||
* @override
|
||||
*/
|
||||
setFileDetector() {}
|
||||
|
||||
/**
|
||||
* Get the context that is currently in effect.
|
||||
*
|
||||
* @return {!Promise<Context>} Current context.
|
||||
*/
|
||||
getContext() {
|
||||
return this.execute(new command.Command(ExtensionCommand.GET_CONTEXT))
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes target context for commands between chrome- and content.
|
||||
*
|
||||
* Changing the current context has a stateful impact on all subsequent
|
||||
* commands. The {@link Context.CONTENT} context has normal web
|
||||
* platform document permissions, as if you would evaluate arbitrary
|
||||
* JavaScript. The {@link Context.CHROME} context gets elevated
|
||||
* permissions that lets you manipulate the browser chrome itself,
|
||||
* with full access to the XUL toolkit.
|
||||
*
|
||||
* Use your powers wisely.
|
||||
*
|
||||
* @param {!Promise<void>} ctx The context to switch to.
|
||||
*/
|
||||
setContext(ctx) {
|
||||
return this.execute(
|
||||
new command.Command(ExtensionCommand.SET_CONTEXT).setParameter(
|
||||
'context',
|
||||
ctx
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs a new addon with the current session. This function will return an
|
||||
* ID that may later be used to {@linkplain #uninstallAddon uninstall} the
|
||||
* addon.
|
||||
*
|
||||
*
|
||||
* @param {string} path Path on the local filesystem to the web extension to
|
||||
* install.
|
||||
* @param {boolean} temporary Flag indicating whether the extension should be
|
||||
* installed temporarily - gets removed on restart
|
||||
* @return {!Promise<string>} A promise that will resolve to an ID for the
|
||||
* newly installed addon.
|
||||
* @see #uninstallAddon
|
||||
*/
|
||||
async installAddon(path, temporary = false) {
|
||||
let stats = fs.statSync(path)
|
||||
let buf
|
||||
if (stats.isDirectory()) {
|
||||
let zip = new Zip()
|
||||
await zip.addDir(path)
|
||||
buf = await zip.toBuffer('DEFLATE')
|
||||
} else {
|
||||
buf = await io.read(path)
|
||||
}
|
||||
return this.execute(
|
||||
new command.Command(ExtensionCommand.INSTALL_ADDON)
|
||||
.setParameter('addon', buf.toString('base64'))
|
||||
.setParameter('temporary', temporary)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstalls an addon from the current browser session's profile.
|
||||
*
|
||||
* @param {(string|!Promise<string>)} id ID of the addon to uninstall.
|
||||
* @return {!Promise} A promise that will resolve when the operation has
|
||||
* completed.
|
||||
* @see #installAddon
|
||||
*/
|
||||
async uninstallAddon(id) {
|
||||
id = await Promise.resolve(id)
|
||||
return this.execute(
|
||||
new command.Command(ExtensionCommand.UNINSTALL_ADDON).setParameter(
|
||||
'id',
|
||||
id
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides methods for locating the executable for a Firefox release channel
|
||||
* on Windows and MacOS. For other systems (i.e. Linux), Firefox will always
|
||||
* be located on the system PATH.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Channel {
|
||||
/**
|
||||
* @param {string} darwin The path to check when running on MacOS.
|
||||
* @param {string} win32 The path to check when running on Windows.
|
||||
*/
|
||||
constructor(darwin, win32) {
|
||||
/** @private @const */ this.darwin_ = darwin
|
||||
/** @private @const */ this.win32_ = win32
|
||||
/** @private {Promise<string>} */
|
||||
this.found_ = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to locate the Firefox executable for this release channel. This
|
||||
* will first check the default installation location for the channel before
|
||||
* checking the user's PATH. The returned promise will be rejected if Firefox
|
||||
* can not be found.
|
||||
*
|
||||
* @return {!Promise<string>} A promise for the location of the located
|
||||
* Firefox executable.
|
||||
*/
|
||||
locate() {
|
||||
if (this.found_) {
|
||||
return this.found_
|
||||
}
|
||||
|
||||
let found
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
found = io
|
||||
.exists(this.darwin_)
|
||||
.then((exists) => (exists ? this.darwin_ : io.findInPath('firefox')))
|
||||
break
|
||||
|
||||
case 'win32':
|
||||
found = findInProgramFiles(this.win32_).then(
|
||||
(found) => found || io.findInPath('firefox.exe')
|
||||
)
|
||||
break
|
||||
|
||||
default:
|
||||
found = Promise.resolve(io.findInPath('firefox'))
|
||||
break
|
||||
}
|
||||
|
||||
this.found_ = found.then((found) => {
|
||||
if (found) {
|
||||
// TODO: verify version info.
|
||||
return found
|
||||
}
|
||||
throw Error('Could not locate Firefox on the current system')
|
||||
})
|
||||
return this.found_
|
||||
}
|
||||
|
||||
/** @return {!Promise<string>} */
|
||||
[Symbols.serialize]() {
|
||||
return this.locate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Firefox's developer channel.
|
||||
* @const
|
||||
* @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#aurora>
|
||||
*/
|
||||
Channel.AURORA = new Channel(
|
||||
'/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin',
|
||||
'Firefox Developer Edition\\firefox.exe'
|
||||
)
|
||||
|
||||
/**
|
||||
* Firefox's beta channel. Note this is provided mainly for convenience as
|
||||
* the beta channel has the same installation location as the main release
|
||||
* channel.
|
||||
* @const
|
||||
* @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#beta>
|
||||
*/
|
||||
Channel.BETA = new Channel(
|
||||
'/Applications/Firefox.app/Contents/MacOS/firefox-bin',
|
||||
'Mozilla Firefox\\firefox.exe'
|
||||
)
|
||||
|
||||
/**
|
||||
* Firefox's release channel.
|
||||
* @const
|
||||
* @see <https://www.mozilla.org/en-US/firefox/desktop/>
|
||||
*/
|
||||
Channel.RELEASE = new Channel(
|
||||
'/Applications/Firefox.app/Contents/MacOS/firefox-bin',
|
||||
'Mozilla Firefox\\firefox.exe'
|
||||
)
|
||||
|
||||
/**
|
||||
* Firefox's nightly release channel.
|
||||
* @const
|
||||
* @see <https://www.mozilla.org/en-US/firefox/channel/desktop/#nightly>
|
||||
*/
|
||||
Channel.NIGHTLY = new Channel(
|
||||
'/Applications/Firefox Nightly.app/Contents/MacOS/firefox-bin',
|
||||
'Nightly\\firefox.exe'
|
||||
)
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Channel,
|
||||
Context,
|
||||
Driver,
|
||||
Options,
|
||||
ServiceBuilder,
|
||||
locateSynchronously,
|
||||
}
|
||||
346
node_modules/selenium-webdriver/http/index.js
generated
vendored
Normal file
346
node_modules/selenium-webdriver/http/index.js
generated
vendored
Normal file
@@ -0,0 +1,346 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines an {@linkplain cmd.Executor command executor} that
|
||||
* communicates with a remote end using HTTP + JSON.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
const url = require('url')
|
||||
|
||||
const httpLib = require('../lib/http')
|
||||
|
||||
/**
|
||||
* @typedef {{protocol: (?string|undefined),
|
||||
* auth: (?string|undefined),
|
||||
* hostname: (?string|undefined),
|
||||
* host: (?string|undefined),
|
||||
* port: (?string|undefined),
|
||||
* path: (?string|undefined),
|
||||
* pathname: (?string|undefined)}}
|
||||
*/
|
||||
let RequestOptions // eslint-disable-line
|
||||
|
||||
/**
|
||||
* @param {string} aUrl The request URL to parse.
|
||||
* @return {RequestOptions} The request options.
|
||||
* @throws {Error} if the URL does not include a hostname.
|
||||
*/
|
||||
function getRequestOptions(aUrl) {
|
||||
//eslint-disable-next-line node/no-deprecated-api
|
||||
let options = url.parse(aUrl)
|
||||
if (!options.hostname) {
|
||||
throw new Error('Invalid URL: ' + aUrl)
|
||||
}
|
||||
// Delete the search and has portions as they are not used.
|
||||
options.search = null
|
||||
options.hash = null
|
||||
options.path = options.pathname
|
||||
options.hostname = options.hostname === 'localhost' ? '127.0.0.1' : options.hostname // To support Node 17 and above. Refer https://github.com/nodejs/node/issues/40702 for details.
|
||||
return options
|
||||
}
|
||||
|
||||
/** @const {string} */
|
||||
const USER_AGENT = (function () {
|
||||
const version = require('../package.json').version
|
||||
const platform =
|
||||
{ darwin: 'mac', win32: 'windows' }[process.platform] || 'linux'
|
||||
return `selenium/${version} (js ${platform})`
|
||||
})()
|
||||
|
||||
/**
|
||||
* A basic HTTP client used to send messages to a remote end.
|
||||
*
|
||||
* @implements {httpLib.Client}
|
||||
*/
|
||||
class HttpClient {
|
||||
/**
|
||||
* @param {string} serverUrl URL for the WebDriver server to send commands to.
|
||||
* @param {http.Agent=} opt_agent The agent to use for each request.
|
||||
* Defaults to `http.globalAgent`.
|
||||
* @param {?string=} opt_proxy The proxy to use for the connection to the
|
||||
* server. Default is to use no proxy.
|
||||
* @param {?Object.<string,Object>} client_options
|
||||
*/
|
||||
constructor(serverUrl, opt_agent, opt_proxy, client_options = {}) {
|
||||
/** @private {http.Agent} */
|
||||
this.agent_ = opt_agent || null
|
||||
|
||||
/**
|
||||
* Base options for each request.
|
||||
* @private {RequestOptions}
|
||||
*/
|
||||
this.options_ = getRequestOptions(serverUrl)
|
||||
|
||||
/**
|
||||
* client options, header overrides
|
||||
*/
|
||||
this.client_options = client_options
|
||||
|
||||
/**
|
||||
* sets keep-alive for the agent
|
||||
* see https://stackoverflow.com/a/58332910
|
||||
*/
|
||||
this.keepAlive = this.client_options['keep-alive']
|
||||
|
||||
/** @private {?RequestOptions} */
|
||||
this.proxyOptions_ = opt_proxy ? getRequestOptions(opt_proxy) : null
|
||||
}
|
||||
|
||||
get keepAlive() {
|
||||
return this.agent_.keepAlive
|
||||
}
|
||||
|
||||
set keepAlive(value) {
|
||||
if (value === 'true' || value === true) {
|
||||
this.agent_.keepAlive = true
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
send(httpRequest) {
|
||||
let data
|
||||
|
||||
let headers = {}
|
||||
|
||||
if (httpRequest.headers) {
|
||||
httpRequest.headers.forEach(function (value, name) {
|
||||
headers[name] = value
|
||||
})
|
||||
}
|
||||
|
||||
headers['User-Agent'] = this.client_options['user-agent'] || USER_AGENT
|
||||
headers['Content-Length'] = 0
|
||||
if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') {
|
||||
data = JSON.stringify(httpRequest.data)
|
||||
headers['Content-Length'] = Buffer.byteLength(data, 'utf8')
|
||||
headers['Content-Type'] = 'application/json;charset=UTF-8'
|
||||
}
|
||||
|
||||
let path = this.options_.path
|
||||
if (path.endsWith('/') && httpRequest.path.startsWith('/')) {
|
||||
path += httpRequest.path.substring(1)
|
||||
} else {
|
||||
path += httpRequest.path
|
||||
}
|
||||
//eslint-disable-next-line node/no-deprecated-api
|
||||
let parsedPath = url.parse(path)
|
||||
|
||||
let options = {
|
||||
agent: this.agent_ || null,
|
||||
method: httpRequest.method,
|
||||
|
||||
auth: this.options_.auth,
|
||||
hostname: this.options_.hostname,
|
||||
port: this.options_.port,
|
||||
protocol: this.options_.protocol,
|
||||
|
||||
path: parsedPath.path,
|
||||
pathname: parsedPath.pathname,
|
||||
search: parsedPath.search,
|
||||
hash: parsedPath.hash,
|
||||
|
||||
headers,
|
||||
}
|
||||
|
||||
return new Promise((fulfill, reject) => {
|
||||
sendRequest(options, fulfill, reject, data, this.proxyOptions_)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a single HTTP request.
|
||||
* @param {!Object} options The request options.
|
||||
* @param {function(!httpLib.Response)} onOk The function to call if the
|
||||
* request succeeds.
|
||||
* @param {function(!Error)} onError The function to call if the request fails.
|
||||
* @param {?string=} opt_data The data to send with the request.
|
||||
* @param {?RequestOptions=} opt_proxy The proxy server to use for the request.
|
||||
* @param {number=} opt_retries The current number of retries.
|
||||
*/
|
||||
function sendRequest(options, onOk, onError, opt_data, opt_proxy, opt_retries) {
|
||||
var hostname = options.hostname
|
||||
var port = options.port
|
||||
|
||||
if (opt_proxy) {
|
||||
let proxy = /** @type {RequestOptions} */ (opt_proxy)
|
||||
|
||||
// RFC 2616, section 5.1.2:
|
||||
// The absoluteURI form is REQUIRED when the request is being made to a
|
||||
// proxy.
|
||||
let absoluteUri = url.format(options)
|
||||
|
||||
// RFC 2616, section 14.23:
|
||||
// An HTTP/1.1 proxy MUST ensure that any request message it forwards does
|
||||
// contain an appropriate Host header field that identifies the service
|
||||
// being requested by the proxy.
|
||||
let targetHost = options.hostname
|
||||
if (options.port) {
|
||||
targetHost += ':' + options.port
|
||||
}
|
||||
|
||||
// Update the request options with our proxy info.
|
||||
options.headers['Host'] = targetHost
|
||||
options.path = absoluteUri
|
||||
options.host = proxy.host
|
||||
options.hostname = proxy.hostname
|
||||
options.port = proxy.port
|
||||
|
||||
// Update the protocol to avoid EPROTO errors when the webdriver proxy
|
||||
// uses a different protocol from the remote selenium server.
|
||||
options.protocol = opt_proxy.protocol
|
||||
|
||||
if (proxy.auth) {
|
||||
options.headers['Proxy-Authorization'] =
|
||||
'Basic ' + Buffer.from(proxy.auth).toString('base64')
|
||||
}
|
||||
}
|
||||
|
||||
let requestFn = options.protocol === 'https:' ? https.request : http.request
|
||||
var request = requestFn(options, function onResponse(response) {
|
||||
if (response.statusCode == 302 || response.statusCode == 303) {
|
||||
let location
|
||||
try {
|
||||
// eslint-disable-next-line node/no-deprecated-api
|
||||
location = url.parse(response.headers['location'])
|
||||
} catch (ex) {
|
||||
onError(
|
||||
Error(
|
||||
'Failed to parse "Location" header for server redirect: ' +
|
||||
ex.message +
|
||||
'\nResponse was: \n' +
|
||||
new httpLib.Response(response.statusCode, response.headers, '')
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (!location.hostname) {
|
||||
location.hostname = hostname
|
||||
location.port = port
|
||||
location.auth = options.auth
|
||||
}
|
||||
|
||||
request.destroy()
|
||||
sendRequest(
|
||||
{
|
||||
method: 'GET',
|
||||
protocol: location.protocol || options.protocol,
|
||||
hostname: location.hostname,
|
||||
port: location.port,
|
||||
path: location.path,
|
||||
auth: location.auth,
|
||||
pathname: location.pathname,
|
||||
search: location.search,
|
||||
hash: location.hash,
|
||||
headers: {
|
||||
Accept: 'application/json; charset=utf-8',
|
||||
'User-Agent': options.headers['User-Agent'] || USER_AGENT,
|
||||
},
|
||||
},
|
||||
onOk,
|
||||
onError,
|
||||
undefined,
|
||||
opt_proxy
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const body = []
|
||||
response.on('data', body.push.bind(body))
|
||||
response.on('end', function () {
|
||||
const resp = new httpLib.Response(
|
||||
/** @type {number} */ (response.statusCode),
|
||||
/** @type {!Object<string>} */ (response.headers),
|
||||
Buffer.concat(body).toString('utf8').replace(/\0/g, '')
|
||||
)
|
||||
onOk(resp)
|
||||
})
|
||||
})
|
||||
|
||||
request.on('error', function (e) {
|
||||
if (typeof opt_retries === 'undefined') {
|
||||
opt_retries = 0
|
||||
}
|
||||
|
||||
if (shouldRetryRequest(opt_retries, e)) {
|
||||
opt_retries += 1
|
||||
setTimeout(function () {
|
||||
sendRequest(options, onOk, onError, opt_data, opt_proxy, opt_retries)
|
||||
}, 15)
|
||||
} else {
|
||||
let message = e.message
|
||||
if (e.code) {
|
||||
message = e.code + ' ' + message
|
||||
}
|
||||
onError(new Error(message))
|
||||
}
|
||||
})
|
||||
|
||||
if (opt_data) {
|
||||
request.write(opt_data)
|
||||
}
|
||||
|
||||
request.end()
|
||||
}
|
||||
|
||||
const MAX_RETRIES = 3
|
||||
|
||||
/**
|
||||
* A retry is sometimes needed on Windows where we may quickly run out of
|
||||
* ephemeral ports. A more robust solution is bumping the MaxUserPort setting
|
||||
* as described here: http://msdn.microsoft.com/en-us/library/aa560610%28v=bts.20%29.aspx
|
||||
*
|
||||
* @param {!number} retries
|
||||
* @param {!Error} err
|
||||
* @return {boolean}
|
||||
*/
|
||||
function shouldRetryRequest(retries, err) {
|
||||
return retries < MAX_RETRIES && isRetryableNetworkError(err)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Error} err
|
||||
* @return {boolean}
|
||||
*/
|
||||
function isRetryableNetworkError(err) {
|
||||
if (err && err.code) {
|
||||
return (
|
||||
err.code === 'ECONNABORTED' ||
|
||||
err.code === 'ECONNRESET' ||
|
||||
err.code === 'ECONNREFUSED' ||
|
||||
err.code === 'EADDRINUSE' ||
|
||||
err.code === 'EPIPE' ||
|
||||
err.code === 'ETIMEDOUT'
|
||||
)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports.Agent = http.Agent
|
||||
module.exports.Executor = httpLib.Executor
|
||||
module.exports.HttpClient = HttpClient
|
||||
module.exports.Request = httpLib.Request
|
||||
module.exports.Response = httpLib.Response
|
||||
166
node_modules/selenium-webdriver/http/util.js
generated
vendored
Normal file
166
node_modules/selenium-webdriver/http/util.js
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Various HTTP utilities.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const Executor = require('./index').Executor
|
||||
const HttpClient = require('./index').HttpClient
|
||||
const HttpRequest = require('./index').Request
|
||||
const Command = require('../lib/command').Command
|
||||
const CommandName = require('../lib/command').Name
|
||||
const error = require('../lib/error')
|
||||
|
||||
/**
|
||||
* Queries a WebDriver server for its current status.
|
||||
* @param {string} url Base URL of the server to query.
|
||||
* @return {!Promise<!Object>} A promise that resolves with
|
||||
* a hash of the server status.
|
||||
*/
|
||||
function getStatus(url) {
|
||||
const client = new HttpClient(url)
|
||||
const executor = new Executor(client)
|
||||
const command = new Command(CommandName.GET_SERVER_STATUS)
|
||||
return executor.execute(command)
|
||||
}
|
||||
|
||||
class CancellationError {}
|
||||
|
||||
/**
|
||||
* Waits for a WebDriver server to be healthy and accepting requests.
|
||||
* @param {string} url Base URL of the server to query.
|
||||
* @param {number} timeout How long to wait for the server.
|
||||
* @param {Promise=} opt_cancelToken A promise used as a cancellation signal:
|
||||
* if resolved before the server is ready, the wait will be terminated
|
||||
* early with a {@link CancellationError}.
|
||||
* @return {!Promise} A promise that will resolve when the server is ready, or
|
||||
* if the wait is cancelled.
|
||||
*/
|
||||
function waitForServer(url, timeout, opt_cancelToken) {
|
||||
return new Promise((onResolve, onReject) => {
|
||||
let start = Date.now()
|
||||
|
||||
let done = false
|
||||
let resolve = (status) => {
|
||||
done = true
|
||||
onResolve(status)
|
||||
}
|
||||
let reject = (err) => {
|
||||
done = true
|
||||
onReject(err)
|
||||
}
|
||||
|
||||
if (opt_cancelToken) {
|
||||
opt_cancelToken.then((_) => reject(new CancellationError()))
|
||||
}
|
||||
|
||||
checkServerStatus()
|
||||
function checkServerStatus() {
|
||||
return getStatus(url).then((status) => resolve(status), onError)
|
||||
}
|
||||
|
||||
function onError(e) {
|
||||
// Some servers don't support the status command. If they are able to
|
||||
// response with an error, then can consider the server ready.
|
||||
if (e instanceof error.UnsupportedOperationError) {
|
||||
resolve({})
|
||||
return
|
||||
}
|
||||
|
||||
if (Date.now() - start > timeout) {
|
||||
reject(Error('Timed out waiting for the WebDriver server at ' + url))
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
if (!done) {
|
||||
checkServerStatus()
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls a URL with GET requests until it returns a 2xx response or the
|
||||
* timeout expires.
|
||||
* @param {string} url The URL to poll.
|
||||
* @param {number} timeout How long to wait, in milliseconds.
|
||||
* @param {Promise=} opt_cancelToken A promise used as a cancellation signal:
|
||||
* if resolved before the a 2xx response is received, the wait will be
|
||||
* terminated early with a {@link CancellationError}.
|
||||
* @return {!Promise} A promise that will resolve when a 2xx is received from
|
||||
* the given URL, or if the wait is cancelled.
|
||||
*/
|
||||
function waitForUrl(url, timeout, opt_cancelToken) {
|
||||
return new Promise((onResolve, onReject) => {
|
||||
let client = new HttpClient(url)
|
||||
let request = new HttpRequest('GET', '')
|
||||
let start = Date.now()
|
||||
|
||||
let done = false
|
||||
let resolve = () => {
|
||||
done = true
|
||||
onResolve()
|
||||
}
|
||||
let reject = (err) => {
|
||||
done = true
|
||||
onReject(err)
|
||||
}
|
||||
|
||||
if (opt_cancelToken) {
|
||||
opt_cancelToken.then((_) => reject(new CancellationError()))
|
||||
}
|
||||
|
||||
testUrl()
|
||||
|
||||
function testUrl() {
|
||||
client.send(request).then(onResponse, onError)
|
||||
}
|
||||
|
||||
function onError() {
|
||||
if (Date.now() - start > timeout) {
|
||||
reject(Error('Timed out waiting for the URL to return 2xx: ' + url))
|
||||
} else {
|
||||
setTimeout(function () {
|
||||
if (!done) {
|
||||
testUrl()
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
|
||||
function onResponse(response) {
|
||||
if (done) {
|
||||
return
|
||||
}
|
||||
if (response.status > 199 && response.status < 300) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
onError()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports.getStatus = getStatus
|
||||
module.exports.CancellationError = CancellationError
|
||||
module.exports.waitForServer = waitForServer
|
||||
module.exports.waitForUrl = waitForUrl
|
||||
516
node_modules/selenium-webdriver/ie.js
generated
vendored
Normal file
516
node_modules/selenium-webdriver/ie.js
generated
vendored
Normal file
@@ -0,0 +1,516 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a {@linkplain Driver WebDriver} client for Microsoft's
|
||||
* Internet Explorer. Before using the IEDriver, you must download the latest
|
||||
* [IEDriverServer](https://www.selenium.dev/downloads/)
|
||||
* and place it on your
|
||||
* [PATH](http://en.wikipedia.org/wiki/PATH_%28variable%29). You must also apply
|
||||
* the system configuration outlined on the Selenium project
|
||||
* [wiki](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const http = require('./http')
|
||||
const io = require('./io')
|
||||
const portprober = require('./net/portprober')
|
||||
const remote = require('./remote')
|
||||
const webdriver = require('./lib/webdriver')
|
||||
const { Browser, Capabilities } = require('./lib/capabilities')
|
||||
const error = require('./lib/error')
|
||||
const { driverLocation } = require('./common/seleniumManager')
|
||||
|
||||
const IEDRIVER_EXE = 'IEDriverServer.exe'
|
||||
const OPTIONS_CAPABILITY_KEY = 'se:ieOptions'
|
||||
const SCROLL_BEHAVIOUR = {
|
||||
BOTTOM: 1,
|
||||
TOP: 0,
|
||||
}
|
||||
|
||||
/**
|
||||
* IEDriverServer logging levels.
|
||||
* @enum {string}
|
||||
*/
|
||||
const Level = {
|
||||
FATAL: 'FATAL',
|
||||
ERROR: 'ERROR',
|
||||
WARN: 'WARN',
|
||||
INFO: 'INFO',
|
||||
DEBUG: 'DEBUG',
|
||||
TRACE: 'TRACE',
|
||||
}
|
||||
|
||||
/**
|
||||
* Option keys:
|
||||
* https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities#ie-specific
|
||||
* @enum {string}
|
||||
*/
|
||||
const Key = {
|
||||
IGNORE_PROTECTED_MODE_SETTINGS: 'ignoreProtectedModeSettings',
|
||||
IGNORE_ZOOM_SETTING: 'ignoreZoomSetting',
|
||||
INITIAL_BROWSER_URL: 'initialBrowserUrl',
|
||||
ENABLE_PERSISTENT_HOVER: 'enablePersistentHover',
|
||||
ENABLE_ELEMENT_CACHE_CLEANUP: 'enableElementCacheCleanup',
|
||||
ELEMENT_SCROLL_BEHAVIOR: 'elementScrollBehavior',
|
||||
REQUIRE_WINDOW_FOCUS: 'requireWindowFocus',
|
||||
BROWSER_ATTACH_TIMEOUT: 'browserAttachTimeout',
|
||||
FORCE_CREATE_PROCESS: 'ie.forceCreateProcessApi',
|
||||
BROWSER_COMMAND_LINE_SWITCHES: 'ie.browserCommandLineSwitches',
|
||||
USE_PER_PROCESS_PROXY: 'ie.usePerProcessProxy',
|
||||
ENSURE_CLEAN_SESSION: 'ie.ensureCleanSession',
|
||||
LOG_FILE: 'logFile',
|
||||
LOG_LEVEL: 'logLevel',
|
||||
HOST: 'host',
|
||||
EXTRACT_PATH: 'extractPath',
|
||||
SILENT: 'silent',
|
||||
FILE_UPLOAD_DIALOG_TIMEOUT: 'ie.fileUploadDialogTimeout',
|
||||
ATTACH_TO_EDGE_CHROMIUM: 'ie.edgechromium',
|
||||
EDGE_EXECUTABLE_PATH: 'ie.edgepath',
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for managing IEDriver specific options.
|
||||
*/
|
||||
class Options extends Capabilities {
|
||||
/**
|
||||
* @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
|
||||
* capabilities to initialize this instance from.
|
||||
*/
|
||||
constructor(other = undefined) {
|
||||
super(other)
|
||||
|
||||
/** @private {!Object} */
|
||||
this.options_ = this.get(OPTIONS_CAPABILITY_KEY) || {}
|
||||
|
||||
this.set(OPTIONS_CAPABILITY_KEY, this.options_)
|
||||
this.setBrowserName(Browser.INTERNET_EXPLORER)
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to disable the protected mode settings check when the session is
|
||||
* created. Disabling this setting may lead to significant instability as the
|
||||
* browser may become unresponsive/hang. Only "best effort" support is provided
|
||||
* when using this capability.
|
||||
*
|
||||
* For more information, refer to the IEDriver's
|
||||
* [required system configuration](http://goo.gl/eH0Yi3).
|
||||
*
|
||||
* @param {boolean} ignoreSettings Whether to ignore protected mode settings.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
introduceFlakinessByIgnoringProtectedModeSettings(ignoreSettings) {
|
||||
this.options_[Key.IGNORE_PROTECTED_MODE_SETTINGS] = !!ignoreSettings
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether to skip the check that the browser's zoom level is set to
|
||||
* 100%.
|
||||
*
|
||||
* @param {boolean} ignore Whether to ignore the browser's zoom level settings.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
ignoreZoomSetting(ignore) {
|
||||
this.options_[Key.IGNORE_ZOOM_SETTING] = !!ignore
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial URL loaded when IE starts. This is intended to be used with
|
||||
* {@link #introduceFlakinessByIgnoringProtectedModeSettings} to allow the user to initialize IE in
|
||||
* the proper Protected Mode zone. Setting this option may cause browser
|
||||
* instability or flaky and unresponsive code. Only "best effort" support is
|
||||
* provided when using this option.
|
||||
*
|
||||
* @param {string} url The initial browser URL.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
initialBrowserUrl(url) {
|
||||
this.options_[Key.INITIAL_BROWSER_URL] = url
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether to enable persistent mouse hovering (true by default).
|
||||
* Persistent hovering is achieved by continuously firing mouse over events at
|
||||
* the last location the mouse cursor has been moved to.
|
||||
*
|
||||
* @param {boolean} enable Whether to enable persistent hovering.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
enablePersistentHover(enable) {
|
||||
this.options_[Key.ENABLE_PERSISTENT_HOVER] = !!enable
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether the driver should attempt to remove obsolete
|
||||
* {@linkplain webdriver.WebElement WebElements} from its internal cache on
|
||||
* page navigation (true by default). Disabling this option will cause the
|
||||
* driver to run with a larger memory footprint.
|
||||
*
|
||||
* @param {boolean} enable Whether to enable element reference cleanup.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
enableElementCacheCleanup(enable) {
|
||||
this.options_[Key.ENABLE_ELEMENT_CACHE_CLEANUP] = !!enable
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether to require the IE window to have input focus before
|
||||
* performing any user interactions (i.e. mouse or keyboard events). This
|
||||
* option is disabled by default, but delivers much more accurate interaction
|
||||
* events when enabled.
|
||||
*
|
||||
* @param {boolean} require Whether to require window focus.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
requireWindowFocus(require) {
|
||||
this.options_[Key.REQUIRE_WINDOW_FOCUS] = !!require
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the timeout, in milliseconds, that the driver will attempt to
|
||||
* located and attach to a newly opened instance of Internet Explorer. The
|
||||
* default is zero, which indicates waiting indefinitely.
|
||||
*
|
||||
* @param {number} timeout How long to wait for IE.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
browserAttachTimeout(timeout) {
|
||||
this.options_[Key.BROWSER_ATTACH_TIMEOUT] = Math.max(timeout, 0)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether to launch Internet Explorer using the CreateProcess API.
|
||||
* If this option is not specified, IE is launched using IELaunchURL, if
|
||||
* available. For IE 8 and above, this option requires the TabProcGrowth
|
||||
* registry value to be set to 0.
|
||||
*
|
||||
* @param {boolean} force Whether to use the CreateProcess API.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
forceCreateProcessApi(force) {
|
||||
this.options_[Key.FORCE_CREATE_PROCESS] = !!force
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies command-line switches to use when launching Internet Explorer.
|
||||
* This is only valid when used with {@link #forceCreateProcessApi}.
|
||||
*
|
||||
* @param {...(string|!Array.<string>)} args The arguments to add.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
|
||||
addBrowserCommandSwitches(...args) {
|
||||
let current = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || []
|
||||
if (typeof current == 'string') current = current.split(' ')
|
||||
this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = current
|
||||
.concat(args)
|
||||
.join(' ')
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies command-line switches to use when launching Internet Explorer.
|
||||
* This is only valid when used with {@link #forceCreateProcessApi}.
|
||||
*
|
||||
* @param {...(string|!Array.<string>)} args The arguments to add.
|
||||
* @deprecated Use {@link #addBrowserCommandSwitches} instead.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
|
||||
addArguments(...args) {
|
||||
let current = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || []
|
||||
if (typeof current == 'string') current = current.split(' ')
|
||||
this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = current
|
||||
.concat(args)
|
||||
.join(' ')
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether proxies should be configured on a per-process basis. If
|
||||
* not set, setting a {@linkplain #setProxy proxy} will configure the system
|
||||
* proxy. The default behavior is to use the system proxy.
|
||||
*
|
||||
* @param {boolean} enable Whether to enable per-process proxy settings.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
usePerProcessProxy(enable) {
|
||||
this.options_[Key.USE_PER_PROCESS_PROXY] = !!enable
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures whether to clear the cache, cookies, history, and saved form data
|
||||
* before starting the browser. _Using this capability will clear session data
|
||||
* for all running instances of Internet Explorer, including those started
|
||||
* manually._
|
||||
*
|
||||
* @param {boolean} cleanSession Whether to clear all session data on startup.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
ensureCleanSession(cleanSession) {
|
||||
this.options_[Key.ENSURE_CLEAN_SESSION] = !!cleanSession
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path to the log file the driver should log to.
|
||||
* @param {string} file The log file path.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setLogFile(file) {
|
||||
this.options_[Key.LOG_FILE] = file
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IEDriverServer's logging {@linkplain Level level}.
|
||||
* @param {Level} level The logging level.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setLogLevel(level) {
|
||||
this.options_[Key.LOG_LEVEL] = level
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IP address of the driver's host adapter.
|
||||
* @param {string} host The IP address to use.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setHost(host) {
|
||||
this.options_[Key.HOST] = host
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of the temporary data directory to use.
|
||||
* @param {string} path The log file path.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setExtractPath(path) {
|
||||
this.options_[Key.EXTRACT_PATH] = path
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the driver should start in silent mode.
|
||||
* @param {boolean} silent Whether to run in silent mode.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
silent(silent) {
|
||||
this.options_[Key.SILENT] = silent
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* The options File Upload Dialog Timeout in milliseconds
|
||||
*
|
||||
* @param {number} timeout How long to wait for IE.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
fileUploadDialogTimeout(timeout) {
|
||||
this.options_[Key.FILE_UPLOAD_DIALOG_TIMEOUT] = Math.max(timeout, 0)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the path of the EdgeChromium driver.
|
||||
* @param {string} path The EdgeChromium driver path.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setEdgePath(path) {
|
||||
this.options_[Key.EDGE_EXECUTABLE_PATH] = path
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IEDriver to drive Chromium-based Edge in Internet Explorer mode.
|
||||
*
|
||||
* @param {boolean} attachEdgeChromium Whether to run in Chromium-based-Edge in IE mode
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setEdgeChromium(attachEdgeChromium) {
|
||||
this.options_[Key.ATTACH_TO_EDGE_CHROMIUM] = !!attachEdgeChromium
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets how elements should be scrolled into view for interaction.
|
||||
* @param {number} behavior The desired scroll behavior: either 0 to align with
|
||||
* the top of the viewport or 1 to align with the bottom.
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setScrollBehavior(behavior) {
|
||||
if (
|
||||
behavior &&
|
||||
behavior !== SCROLL_BEHAVIOUR.TOP &&
|
||||
behavior !== SCROLL_BEHAVIOUR.BOTTOM
|
||||
) {
|
||||
throw new error.InvalidArgumentError(`Element Scroll Behavior out of range.
|
||||
It should be either ${SCROLL_BEHAVIOUR.TOP} or ${SCROLL_BEHAVIOUR.BOTTOM}`)
|
||||
}
|
||||
this.options_[Key.ELEMENT_SCROLL_BEHAVIOR] = behavior
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _Synchronously_ attempts to locate the IE driver executable on the current
|
||||
* system.
|
||||
*
|
||||
* @return {?string} the located executable, or `null`.
|
||||
*/
|
||||
function locateSynchronously() {
|
||||
return process.platform === 'win32' ? io.findInPath(IEDRIVER_EXE, true) : null
|
||||
}
|
||||
|
||||
function createServiceFromCapabilities(capabilities) {
|
||||
if (process.platform !== 'win32') {
|
||||
throw Error(
|
||||
'The IEDriver may only be used on Windows, but you appear to be on ' +
|
||||
process.platform +
|
||||
'. Did you mean to run against a remote ' +
|
||||
'WebDriver server?'
|
||||
)
|
||||
}
|
||||
|
||||
let exe = locateSynchronously()
|
||||
if (!exe) {
|
||||
console.log(
|
||||
`The ${IEDRIVER_EXE} executable could not be found on the current PATH, trying Selenium Manager`
|
||||
)
|
||||
|
||||
try {
|
||||
exe = driverLocation(Browser.INTERNET_EXPLORER)
|
||||
} catch (err) {
|
||||
console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (!exe || !fs.existsSync(exe)) {
|
||||
throw Error(
|
||||
`${IEDRIVER_EXE} could not be found on the current PATH. Please ` +
|
||||
`download the latest version of ${IEDRIVER_EXE} from ` +
|
||||
'https://www.selenium.dev/downloads/ and ' +
|
||||
'ensure it can be found on your system PATH.'
|
||||
)
|
||||
}
|
||||
|
||||
var args = []
|
||||
if (capabilities.has(Key.HOST)) {
|
||||
args.push('--host=' + capabilities.get(Key.HOST))
|
||||
}
|
||||
if (capabilities.has(Key.LOG_FILE)) {
|
||||
args.push('--log-file=' + capabilities.get(Key.LOG_FILE))
|
||||
}
|
||||
if (capabilities.has(Key.LOG_LEVEL)) {
|
||||
args.push('--log-level=' + capabilities.get(Key.LOG_LEVEL))
|
||||
}
|
||||
if (capabilities.has(Key.EXTRACT_PATH)) {
|
||||
args.push('--extract-path=' + capabilities.get(Key.EXTRACT_PATH))
|
||||
}
|
||||
if (capabilities.get(Key.SILENT)) {
|
||||
args.push('--silent')
|
||||
}
|
||||
|
||||
var port = portprober.findFreePort()
|
||||
return new remote.DriverService(exe, {
|
||||
loopback: true,
|
||||
port: port,
|
||||
args: port.then(function (port) {
|
||||
return args.concat('--port=' + port)
|
||||
}),
|
||||
stdio: 'ignore',
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
||||
* an [IEDriverServer](https://github.com/SeleniumHQ/selenium/wiki/InternetExplorerDriver)
|
||||
* server in a child process.
|
||||
*/
|
||||
class ServiceBuilder extends remote.DriverService.Builder {
|
||||
/**
|
||||
* @param {string=} opt_exe Path to the server executable to use. If omitted,
|
||||
* the builder will attempt to locate the IEDriverServer on the system PATH.
|
||||
*/
|
||||
constructor(opt_exe) {
|
||||
super(opt_exe || IEDRIVER_EXE)
|
||||
this.setLoopback(true) // Required.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A WebDriver client for Microsoft's Internet Explorer.
|
||||
*/
|
||||
class Driver extends webdriver.WebDriver {
|
||||
/**
|
||||
* Creates a new session for Microsoft's Internet Explorer.
|
||||
*
|
||||
* @param {(Capabilities|Options)=} options The configuration options.
|
||||
* @param {(remote.DriverService)=} opt_service The `DriverService` to use
|
||||
* to start the IEDriverServer in a child process, optionally.
|
||||
* @return {!Driver} A new driver instance.
|
||||
*/
|
||||
static createSession(options, opt_service) {
|
||||
options = options || new Options()
|
||||
|
||||
let service
|
||||
|
||||
if (opt_service instanceof remote.DriverService) {
|
||||
service = opt_service
|
||||
} else {
|
||||
service = createServiceFromCapabilities(options)
|
||||
}
|
||||
|
||||
let client = service.start().then((url) => new http.HttpClient(url))
|
||||
let executor = new http.Executor(client)
|
||||
|
||||
return /** @type {!Driver} */ (
|
||||
super.createSession(executor, options, () => service.kill())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is a no-op as file detectors are not supported by this
|
||||
* implementation.
|
||||
* @override
|
||||
*/
|
||||
setFileDetector() {}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
exports.Driver = Driver
|
||||
exports.Options = Options
|
||||
exports.Level = Level
|
||||
exports.ServiceBuilder = ServiceBuilder
|
||||
exports.Key = Key
|
||||
exports.VENDOR_COMMAND_PREFIX = OPTIONS_CAPABILITY_KEY
|
||||
exports.Behavior = SCROLL_BEHAVIOUR
|
||||
exports.locateSynchronously = locateSynchronously
|
||||
801
node_modules/selenium-webdriver/index.js
generated
vendored
Normal file
801
node_modules/selenium-webdriver/index.js
generated
vendored
Normal file
@@ -0,0 +1,801 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview The main user facing module. Exports WebDriver's primary
|
||||
* public API and provides convenience assessors to certain sub-modules.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const _http = require('./http')
|
||||
const by = require('./lib/by')
|
||||
const capabilities = require('./lib/capabilities')
|
||||
const chrome = require('./chrome')
|
||||
const edge = require('./edge')
|
||||
const error = require('./lib/error')
|
||||
const firefox = require('./firefox')
|
||||
const ie = require('./ie')
|
||||
const input = require('./lib/input')
|
||||
const logging = require('./lib/logging')
|
||||
const promise = require('./lib/promise')
|
||||
const remote = require('./remote')
|
||||
const safari = require('./safari')
|
||||
const session = require('./lib/session')
|
||||
const until = require('./lib/until')
|
||||
const webdriver = require('./lib/webdriver')
|
||||
const select = require('./lib/select')
|
||||
const LogInspector = require('./bidi/logInspector')
|
||||
const BrowsingContext = require('./bidi/browsingContext')
|
||||
|
||||
const Browser = capabilities.Browser
|
||||
const Capabilities = capabilities.Capabilities
|
||||
const Capability = capabilities.Capability
|
||||
const WebDriver = webdriver.WebDriver
|
||||
|
||||
let seleniumServer
|
||||
|
||||
/**
|
||||
* Starts an instance of the Selenium server if not yet running.
|
||||
* @param {string} jar Path to the server jar to use.
|
||||
* @return {!Promise<string>} A promise for the server's
|
||||
* address once started.
|
||||
*/
|
||||
function startSeleniumServer(jar) {
|
||||
if (!seleniumServer) {
|
||||
seleniumServer = new remote.SeleniumServer(jar)
|
||||
}
|
||||
return seleniumServer.start()
|
||||
}
|
||||
|
||||
/**
|
||||
* {@linkplain webdriver.WebDriver#setFileDetector WebDriver's setFileDetector}
|
||||
* method uses a non-standard command to transfer files from the local client
|
||||
* to the remote end hosting the browser. Many of the WebDriver sub-types, like
|
||||
* the {@link chrome.Driver} and {@link firefox.Driver}, do not support this
|
||||
* command. Thus, these classes override the `setFileDetector` to no-op.
|
||||
*
|
||||
* This function uses a mixin to re-enable `setFileDetector` by calling the
|
||||
* original method on the WebDriver prototype directly. This is used only when
|
||||
* the builder creates a Chrome or Firefox instance that communicates with a
|
||||
* remote end (and thus, support for remote file detectors is unknown).
|
||||
*
|
||||
* @param {function(new: webdriver.WebDriver, ...?)} ctor
|
||||
* @return {function(new: webdriver.WebDriver, ...?)}
|
||||
*/
|
||||
function ensureFileDetectorsAreEnabled(ctor) {
|
||||
return class extends ctor {
|
||||
/** @param {input.FileDetector} detector */
|
||||
setFileDetector(detector) {
|
||||
webdriver.WebDriver.prototype.setFileDetector.call(this, detector)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A thenable wrapper around a {@linkplain webdriver.IWebDriver IWebDriver}
|
||||
* instance that allows commands to be issued directly instead of having to
|
||||
* repeatedly call `then`:
|
||||
*
|
||||
* let driver = new Builder().build();
|
||||
* driver.then(d => d.get(url)); // You can do this...
|
||||
* driver.get(url); // ...or this
|
||||
*
|
||||
* If the driver instance fails to resolve (e.g. the session cannot be created),
|
||||
* every issued command will fail.
|
||||
*
|
||||
* @extends {webdriver.IWebDriver}
|
||||
* @extends {IThenable<!webdriver.IWebDriver>}
|
||||
* @interface
|
||||
*/
|
||||
class ThenableWebDriver {
|
||||
/** @param {...?} args */
|
||||
static createSession(...args) {} // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
* @const {!Map<function(new: WebDriver, !IThenable<!Session>, ...?),
|
||||
* function(new: ThenableWebDriver, !IThenable<!Session>, ...?)>}
|
||||
*/
|
||||
const THENABLE_DRIVERS = new Map()
|
||||
|
||||
/**
|
||||
* @param {function(new: WebDriver, !IThenable<!Session>, ...?)} ctor
|
||||
* @param {...?} args
|
||||
* @return {!ThenableWebDriver}
|
||||
*/
|
||||
function createDriver(ctor, ...args) {
|
||||
let thenableWebDriverProxy = THENABLE_DRIVERS.get(ctor)
|
||||
if (!thenableWebDriverProxy) {
|
||||
/**
|
||||
* @extends {WebDriver} // Needed since `ctor` is dynamically typed.
|
||||
* @implements {ThenableWebDriver}
|
||||
*/
|
||||
thenableWebDriverProxy = class extends ctor {
|
||||
/**
|
||||
* @param {!IThenable<!Session>} session
|
||||
* @param {...?} rest
|
||||
*/
|
||||
constructor(session, ...rest) {
|
||||
super(session, ...rest)
|
||||
|
||||
const pd = this.getSession().then((session) => {
|
||||
return new ctor(session, ...rest)
|
||||
})
|
||||
|
||||
/** @override */
|
||||
this.then = pd.then.bind(pd)
|
||||
|
||||
/** @override */
|
||||
this.catch = pd.catch.bind(pd)
|
||||
}
|
||||
}
|
||||
THENABLE_DRIVERS.set(ctor, thenableWebDriverProxy)
|
||||
}
|
||||
return thenableWebDriverProxy.createSession(...args)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new {@link webdriver.WebDriver WebDriver} instances. The environment
|
||||
* variables listed below may be used to override a builder's configuration,
|
||||
* allowing quick runtime changes.
|
||||
*
|
||||
* - {@code SELENIUM_BROWSER}: defines the target browser in the form
|
||||
* {@code browser[:version][:platform]}.
|
||||
*
|
||||
* - {@code SELENIUM_REMOTE_URL}: defines the remote URL for all builder
|
||||
* instances. This environment variable should be set to a fully qualified
|
||||
* URL for a WebDriver server (e.g. http://localhost:4444/wd/hub). This
|
||||
* option always takes precedence over {@code SELENIUM_SERVER_JAR}.
|
||||
*
|
||||
* - {@code SELENIUM_SERVER_JAR}: defines the path to the
|
||||
* <a href="https://www.selenium.dev/downloads/">
|
||||
* standalone Selenium server</a> jar to use. The server will be started the
|
||||
* first time a WebDriver instance and be killed when the process exits.
|
||||
*
|
||||
* Suppose you had mytest.js that created WebDriver with
|
||||
*
|
||||
* var driver = new webdriver.Builder()
|
||||
* .forBrowser('chrome')
|
||||
* .build();
|
||||
*
|
||||
* This test could be made to use Firefox on the local machine by running with
|
||||
* `SELENIUM_BROWSER=firefox node mytest.js`. Rather than change the code to
|
||||
* target Google Chrome on a remote machine, you can simply set the
|
||||
* `SELENIUM_BROWSER` and `SELENIUM_REMOTE_URL` environment variables:
|
||||
*
|
||||
* SELENIUM_BROWSER=chrome:36:LINUX \
|
||||
* SELENIUM_REMOTE_URL=http://www.example.com:4444/wd/hub \
|
||||
* node mytest.js
|
||||
*
|
||||
* You could also use a local copy of the standalone Selenium server:
|
||||
*
|
||||
* SELENIUM_BROWSER=chrome:36:LINUX \
|
||||
* SELENIUM_SERVER_JAR=/path/to/selenium-server-standalone.jar \
|
||||
* node mytest.js
|
||||
*/
|
||||
class Builder {
|
||||
constructor() {
|
||||
/** @private @const */
|
||||
this.log_ = logging.getLogger('webdriver.Builder')
|
||||
|
||||
/** @private {string} */
|
||||
this.url_ = ''
|
||||
|
||||
/** @private {?string} */
|
||||
this.proxy_ = null
|
||||
|
||||
/** @private {!Capabilities} */
|
||||
this.capabilities_ = new Capabilities()
|
||||
|
||||
/** @private {chrome.Options} */
|
||||
this.chromeOptions_ = null
|
||||
|
||||
/** @private {chrome.ServiceBuilder} */
|
||||
this.chromeService_ = null
|
||||
|
||||
/** @private {firefox.Options} */
|
||||
this.firefoxOptions_ = null
|
||||
|
||||
/** @private {firefox.ServiceBuilder} */
|
||||
this.firefoxService_ = null
|
||||
|
||||
/** @private {ie.Options} */
|
||||
this.ieOptions_ = null
|
||||
|
||||
/** @private {ie.ServiceBuilder} */
|
||||
this.ieService_ = null
|
||||
|
||||
/** @private {safari.Options} */
|
||||
this.safariOptions_ = null
|
||||
|
||||
/** @private {edge.Options} */
|
||||
this.edgeOptions_ = null
|
||||
|
||||
/** @private {remote.DriverService.Builder} */
|
||||
this.edgeService_ = null
|
||||
|
||||
/** @private {boolean} */
|
||||
this.ignoreEnv_ = false
|
||||
|
||||
/** @private {http.Agent} */
|
||||
this.agent_ = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures this builder to ignore any environment variable overrides and to
|
||||
* only use the configuration specified through this instance's API.
|
||||
*
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
disableEnvironmentOverrides() {
|
||||
this.ignoreEnv_ = true
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL of a remote WebDriver server to use. Once a remote URL has
|
||||
* been specified, the builder direct all new clients to that server. If this
|
||||
* method is never called, the Builder will attempt to create all clients
|
||||
* locally.
|
||||
*
|
||||
* As an alternative to this method, you may also set the
|
||||
* `SELENIUM_REMOTE_URL` environment variable.
|
||||
*
|
||||
* @param {string} url The URL of a remote server to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
usingServer(url) {
|
||||
this.url_ = url
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string} The URL of the WebDriver server this instance is
|
||||
* configured to use.
|
||||
*/
|
||||
getServerUrl() {
|
||||
return this.url_
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URL of the proxy to use for the WebDriver's HTTP connections.
|
||||
* If this method is never called, the Builder will create a connection
|
||||
* without a proxy.
|
||||
*
|
||||
* @param {string} proxy The URL of a proxy to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
usingWebDriverProxy(proxy) {
|
||||
this.proxy_ = proxy
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {?string} The URL of the proxy server to use for the WebDriver's
|
||||
* HTTP connections, or `null` if not set.
|
||||
*/
|
||||
getWebDriverProxy() {
|
||||
return this.proxy_
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the http agent to use for each request.
|
||||
* If this method is not called, the Builder will use http.globalAgent by default.
|
||||
*
|
||||
* @param {http.Agent} agent The agent to use for each request.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
usingHttpAgent(agent) {
|
||||
this.agent_ = agent
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {http.Agent} The http agent used for each request
|
||||
*/
|
||||
getHttpAgent() {
|
||||
return this.agent_
|
||||
}
|
||||
|
||||
/**
|
||||
* Recommended way is to use set*Options where * is the browser(eg setChromeOptions)
|
||||
*
|
||||
* Sets the desired capabilities when requesting a new session. This will
|
||||
* overwrite any previously set capabilities.
|
||||
* @param {!(Object|Capabilities)} capabilities The desired capabilities for
|
||||
* a new session.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
withCapabilities(capabilities) {
|
||||
this.capabilities_ = new Capabilities(capabilities)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base set of capabilities this instance is currently configured
|
||||
* to use.
|
||||
* @return {!Capabilities} The current capabilities for this builder.
|
||||
*/
|
||||
getCapabilities() {
|
||||
return this.capabilities_
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired capability when requesting a new session.
|
||||
* If there is already a capability named key, its value will be overwritten with value.
|
||||
* This is a convenience wrapper around builder.getCapabilities().set(key, value) to support Builder method chaining.
|
||||
* @param {string} key The capability key.
|
||||
* @param {*} value The capability value.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setCapability(key, value) {
|
||||
this.capabilities_.set(key, value)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the target browser for clients created by this instance.
|
||||
* Any calls to {@link #withCapabilities} after this function will
|
||||
* overwrite these settings.
|
||||
*
|
||||
* You may also define the target browser using the {@code SELENIUM_BROWSER}
|
||||
* environment variable. If set, this environment variable should be of the
|
||||
* form `browser[:[version][:platform]]`.
|
||||
*
|
||||
* @param {(string|!Browser)} name The name of the target browser;
|
||||
* common defaults are available on the {@link webdriver.Browser} enum.
|
||||
* @param {string=} opt_version A desired version; may be omitted if any
|
||||
* version should be used.
|
||||
* @param {(string|!capabilities.Platform)=} opt_platform
|
||||
* The desired platform; may be omitted if any platform may be used.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
forBrowser(name, opt_version, opt_platform) {
|
||||
this.capabilities_.setBrowserName(name)
|
||||
if (opt_version) {
|
||||
this.capabilities_.setBrowserVersion(opt_version)
|
||||
}
|
||||
if (opt_platform) {
|
||||
this.capabilities_.setPlatform(opt_platform)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the proxy configuration for the target browser.
|
||||
* Any calls to {@link #withCapabilities} after this function will
|
||||
* overwrite these settings.
|
||||
*
|
||||
* @param {!./lib/proxy.Config} config The configuration to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setProxy(config) {
|
||||
this.capabilities_.setProxy(config)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logging preferences for the created session. Preferences may be
|
||||
* changed by repeated calls, or by calling {@link #withCapabilities}.
|
||||
* @param {!(./lib/logging.Preferences|Object<string, string>)} prefs The
|
||||
* desired logging preferences.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setLoggingPrefs(prefs) {
|
||||
this.capabilities_.setLoggingPrefs(prefs)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default action to take with an unexpected alert before returning
|
||||
* an error.
|
||||
*
|
||||
* @param {?capabilities.UserPromptHandler} behavior The desired behavior.
|
||||
* @return {!Builder} A self reference.
|
||||
* @see capabilities.Capabilities#setAlertBehavior
|
||||
*/
|
||||
setAlertBehavior(behavior) {
|
||||
this.capabilities_.setAlertBehavior(behavior)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Chrome specific {@linkplain chrome.Options options} for drivers
|
||||
* created by this builder. Any logging or proxy settings defined on the given
|
||||
* options will take precedence over those set through
|
||||
* {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
|
||||
*
|
||||
* @param {!chrome.Options} options The ChromeDriver options to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setChromeOptions(options) {
|
||||
this.chromeOptions_ = options
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {chrome.Options} the Chrome specific options currently configured
|
||||
* for this builder.
|
||||
*/
|
||||
getChromeOptions() {
|
||||
return this.chromeOptions_
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the service builder to use for managing the chromedriver child process
|
||||
* when creating new Chrome sessions.
|
||||
*
|
||||
* @param {chrome.ServiceBuilder} service the service to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setChromeService(service) {
|
||||
if (service && !(service instanceof chrome.ServiceBuilder)) {
|
||||
throw TypeError('not a chrome.ServiceBuilder object')
|
||||
}
|
||||
this.chromeService_ = service
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Firefox specific {@linkplain firefox.Options options} for drivers
|
||||
* created by this builder. Any logging or proxy settings defined on the given
|
||||
* options will take precedence over those set through
|
||||
* {@link #setLoggingPrefs} and {@link #setProxy}, respectively.
|
||||
*
|
||||
* @param {!firefox.Options} options The FirefoxDriver options to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setFirefoxOptions(options) {
|
||||
this.firefoxOptions_ = options
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {firefox.Options} the Firefox specific options currently configured
|
||||
* for this instance.
|
||||
*/
|
||||
getFirefoxOptions() {
|
||||
return this.firefoxOptions_
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link firefox.ServiceBuilder} to use to manage the geckodriver
|
||||
* child process when creating Firefox sessions locally.
|
||||
*
|
||||
* @param {firefox.ServiceBuilder} service the service to use.
|
||||
* @return {!Builder} a self reference.
|
||||
*/
|
||||
setFirefoxService(service) {
|
||||
if (service && !(service instanceof firefox.ServiceBuilder)) {
|
||||
throw TypeError('not a firefox.ServiceBuilder object')
|
||||
}
|
||||
this.firefoxService_ = service
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Internet Explorer specific {@linkplain ie.Options options} for drivers
|
||||
* created by this builder. Any proxy settings defined on the given options
|
||||
* will take precedence over those set through {@link #setProxy}.
|
||||
*
|
||||
* @param {!ie.Options} options The IEDriver options to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setIeOptions(options) {
|
||||
this.ieOptions_ = options
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link ie.ServiceBuilder} to use to manage the geckodriver
|
||||
* child process when creating IE sessions locally.
|
||||
*
|
||||
* @param {ie.ServiceBuilder} service the service to use.
|
||||
* @return {!Builder} a self reference.
|
||||
*/
|
||||
setIeService(service) {
|
||||
this.ieService_ = service
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Set {@linkplain edge.Options options} specific to Microsoft's Edge browser
|
||||
* for drivers created by this builder. Any proxy settings defined on the
|
||||
* given options will take precedence over those set through
|
||||
* {@link #setProxy}.
|
||||
*
|
||||
* @param {!edge.Options} options The MicrosoftEdgeDriver options to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setEdgeOptions(options) {
|
||||
this.edgeOptions_ = options
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link edge.ServiceBuilder} to use to manage the
|
||||
* MicrosoftEdgeDriver child process when creating sessions locally.
|
||||
*
|
||||
* @param {edge.ServiceBuilder} service the service to use.
|
||||
* @return {!Builder} a self reference.
|
||||
*/
|
||||
setEdgeService(service) {
|
||||
if (service && !(service instanceof edge.ServiceBuilder)) {
|
||||
throw TypeError('not a edge.ServiceBuilder object')
|
||||
}
|
||||
this.edgeService_ = service
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Safari specific {@linkplain safari.Options options} for drivers
|
||||
* created by this builder. Any logging settings defined on the given options
|
||||
* will take precedence over those set through {@link #setLoggingPrefs}.
|
||||
*
|
||||
* @param {!safari.Options} options The Safari options to use.
|
||||
* @return {!Builder} A self reference.
|
||||
*/
|
||||
setSafariOptions(options) {
|
||||
this.safariOptions_ = options
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {safari.Options} the Safari specific options currently configured
|
||||
* for this instance.
|
||||
*/
|
||||
getSafariOptions() {
|
||||
return this.safariOptions_
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebDriver client based on this builder's current
|
||||
* configuration.
|
||||
*
|
||||
* This method will return a {@linkplain ThenableWebDriver} instance, allowing
|
||||
* users to issue commands directly without calling `then()`. The returned
|
||||
* thenable wraps a promise that will resolve to a concrete
|
||||
* {@linkplain webdriver.WebDriver WebDriver} instance. The promise will be
|
||||
* rejected if the remote end fails to create a new session.
|
||||
*
|
||||
* @return {!ThenableWebDriver} A new WebDriver instance.
|
||||
* @throws {Error} If the current configuration is invalid.
|
||||
*/
|
||||
build() {
|
||||
// Create a copy for any changes we may need to make based on the current
|
||||
// environment.
|
||||
const capabilities = new Capabilities(this.capabilities_)
|
||||
|
||||
let browser
|
||||
if (!this.ignoreEnv_ && process.env.SELENIUM_BROWSER) {
|
||||
this.log_.fine(`SELENIUM_BROWSER=${process.env.SELENIUM_BROWSER}`)
|
||||
browser = process.env.SELENIUM_BROWSER.split(/:/, 3)
|
||||
capabilities.setBrowserName(browser[0])
|
||||
|
||||
browser[1] && capabilities.setBrowserVersion(browser[1])
|
||||
browser[2] && capabilities.setPlatform(browser[2])
|
||||
}
|
||||
|
||||
browser = capabilities.get(Capability.BROWSER_NAME)
|
||||
|
||||
if (typeof browser !== 'string') {
|
||||
throw TypeError(
|
||||
`Target browser must be a string, but is <${typeof browser}>;` +
|
||||
' did you forget to call forBrowser()?'
|
||||
)
|
||||
}
|
||||
|
||||
if (browser === 'ie') {
|
||||
browser = Browser.INTERNET_EXPLORER
|
||||
}
|
||||
|
||||
// Apply browser specific overrides.
|
||||
if (browser === Browser.CHROME && this.chromeOptions_) {
|
||||
capabilities.merge(this.chromeOptions_)
|
||||
} else if (browser === Browser.FIREFOX && this.firefoxOptions_) {
|
||||
capabilities.merge(this.firefoxOptions_)
|
||||
} else if (browser === Browser.INTERNET_EXPLORER && this.ieOptions_) {
|
||||
capabilities.merge(this.ieOptions_)
|
||||
} else if (browser === Browser.SAFARI && this.safariOptions_) {
|
||||
capabilities.merge(this.safariOptions_)
|
||||
} else if (browser === Browser.EDGE && this.edgeOptions_) {
|
||||
capabilities.merge(this.edgeOptions_)
|
||||
}
|
||||
|
||||
checkOptions(
|
||||
capabilities,
|
||||
'chromeOptions',
|
||||
chrome.Options,
|
||||
'setChromeOptions'
|
||||
)
|
||||
checkOptions(
|
||||
capabilities,
|
||||
'moz:firefoxOptions',
|
||||
firefox.Options,
|
||||
'setFirefoxOptions'
|
||||
)
|
||||
checkOptions(
|
||||
capabilities,
|
||||
'safari.options',
|
||||
safari.Options,
|
||||
'setSafariOptions'
|
||||
)
|
||||
|
||||
// Check for a remote browser.
|
||||
let url = this.url_
|
||||
if (!this.ignoreEnv_) {
|
||||
if (process.env.SELENIUM_REMOTE_URL) {
|
||||
this.log_.fine(`SELENIUM_REMOTE_URL=${process.env.SELENIUM_REMOTE_URL}`)
|
||||
url = process.env.SELENIUM_REMOTE_URL
|
||||
} else if (process.env.SELENIUM_SERVER_JAR) {
|
||||
this.log_.fine(`SELENIUM_SERVER_JAR=${process.env.SELENIUM_SERVER_JAR}`)
|
||||
url = startSeleniumServer(process.env.SELENIUM_SERVER_JAR)
|
||||
}
|
||||
}
|
||||
|
||||
if (url) {
|
||||
this.log_.fine('Creating session on remote server')
|
||||
let client = Promise.resolve(url).then(
|
||||
(url) => new _http.HttpClient(url, this.agent_, this.proxy_)
|
||||
)
|
||||
let executor = new _http.Executor(client)
|
||||
|
||||
if (browser === Browser.CHROME) {
|
||||
const driver = ensureFileDetectorsAreEnabled(chrome.Driver)
|
||||
return createDriver(driver, capabilities, executor)
|
||||
}
|
||||
|
||||
if (browser === Browser.FIREFOX) {
|
||||
const driver = ensureFileDetectorsAreEnabled(firefox.Driver)
|
||||
return createDriver(driver, capabilities, executor)
|
||||
}
|
||||
return createDriver(WebDriver, executor, capabilities)
|
||||
}
|
||||
|
||||
// Check for a native browser.
|
||||
switch (browser) {
|
||||
case Browser.CHROME: {
|
||||
let service = null
|
||||
if (this.chromeService_) {
|
||||
service = this.chromeService_.build()
|
||||
}
|
||||
return createDriver(chrome.Driver, capabilities, service)
|
||||
}
|
||||
|
||||
case Browser.FIREFOX: {
|
||||
let service = null
|
||||
if (this.firefoxService_) {
|
||||
service = this.firefoxService_.build()
|
||||
}
|
||||
return createDriver(firefox.Driver, capabilities, service)
|
||||
}
|
||||
|
||||
case Browser.INTERNET_EXPLORER: {
|
||||
let service = null
|
||||
if (this.ieService_) {
|
||||
service = this.ieService_.build()
|
||||
}
|
||||
return createDriver(ie.Driver, capabilities, service)
|
||||
}
|
||||
|
||||
case Browser.EDGE: {
|
||||
let service = null
|
||||
if (this.edgeService_) {
|
||||
service = this.edgeService_.build()
|
||||
}
|
||||
return createDriver(edge.Driver, capabilities, service)
|
||||
}
|
||||
|
||||
case Browser.SAFARI:
|
||||
return createDriver(safari.Driver, capabilities)
|
||||
|
||||
default:
|
||||
throw new Error(
|
||||
'Do not know how to build driver: ' +
|
||||
browser +
|
||||
'; did you forget to call usingServer(url)?'
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In the 3.x releases, the various browser option classes
|
||||
* (e.g. firefox.Options) had to be manually set as an option using the
|
||||
* Capabilties class:
|
||||
*
|
||||
* let ffo = new firefox.Options();
|
||||
* // Configure firefox options...
|
||||
*
|
||||
* let caps = new Capabilities();
|
||||
* caps.set('moz:firefoxOptions', ffo);
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .withCapabilities(caps)
|
||||
* .build();
|
||||
*
|
||||
* The options are now subclasses of Capabilities and can be used directly. A
|
||||
* direct translation of the above is:
|
||||
*
|
||||
* let ffo = new firefox.Options();
|
||||
* // Configure firefox options...
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .withCapabilities(ffo)
|
||||
* .build();
|
||||
*
|
||||
* You can also set the options for various browsers at once and let the builder
|
||||
* choose the correct set at runtime (see Builder docs above):
|
||||
*
|
||||
* let ffo = new firefox.Options();
|
||||
* // Configure ...
|
||||
*
|
||||
* let co = new chrome.Options();
|
||||
* // Configure ...
|
||||
*
|
||||
* let driver = new Builder()
|
||||
* .setChromeOptions(co)
|
||||
* .setFirefoxOptions(ffo)
|
||||
* .build();
|
||||
*
|
||||
* @param {!Capabilities} caps
|
||||
* @param {string} key
|
||||
* @param {function(new: Capabilities)} optionType
|
||||
* @param {string} setMethod
|
||||
* @throws {error.InvalidArgumentError}
|
||||
*/
|
||||
function checkOptions(caps, key, optionType, setMethod) {
|
||||
let val = caps.get(key)
|
||||
if (val instanceof optionType) {
|
||||
throw new error.InvalidArgumentError(
|
||||
'Options class extends Capabilities and should not be set as key ' +
|
||||
`"${key}"; set browser-specific options with ` +
|
||||
`Builder.${setMethod}(). For more information, see the ` +
|
||||
'documentation attached to the function that threw this error'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
exports.Browser = capabilities.Browser
|
||||
exports.Builder = Builder
|
||||
exports.Button = input.Button
|
||||
exports.By = by.By
|
||||
exports.RelativeBy = by.RelativeBy
|
||||
exports.withTagName = by.withTagName
|
||||
exports.locateWith = by.locateWith
|
||||
exports.Capabilities = capabilities.Capabilities
|
||||
exports.Capability = capabilities.Capability
|
||||
exports.Condition = webdriver.Condition
|
||||
exports.FileDetector = input.FileDetector
|
||||
exports.Key = input.Key
|
||||
exports.Origin = input.Origin
|
||||
exports.Session = session.Session
|
||||
exports.ThenableWebDriver = ThenableWebDriver
|
||||
exports.WebDriver = webdriver.WebDriver
|
||||
exports.WebElement = webdriver.WebElement
|
||||
exports.WebElementCondition = webdriver.WebElementCondition
|
||||
exports.WebElementPromise = webdriver.WebElementPromise
|
||||
exports.error = error
|
||||
exports.logging = logging
|
||||
exports.promise = promise
|
||||
exports.until = until
|
||||
exports.Select = select.Select
|
||||
exports.LogInspector = LogInspector
|
||||
exports.BrowsingContext = BrowsingContext
|
||||
166
node_modules/selenium-webdriver/io/exec.js
generated
vendored
Normal file
166
node_modules/selenium-webdriver/io/exec.js
generated
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const childProcess = require('child_process')
|
||||
|
||||
/**
|
||||
* Options for configuring an executed command.
|
||||
*
|
||||
* @record
|
||||
*/
|
||||
class Options {
|
||||
constructor() {
|
||||
/**
|
||||
* Command line arguments for the child process, if any.
|
||||
* @type (!Array<string>|undefined)
|
||||
*/
|
||||
this.args
|
||||
|
||||
/**
|
||||
* Environment variables for the spawned process. If unspecified, the
|
||||
* child will inherit this process' environment.
|
||||
*
|
||||
* @type {(!Object<string, string>|undefined)}
|
||||
*/
|
||||
this.env
|
||||
|
||||
/**
|
||||
* IO conifguration for the spawned server child process. If unspecified,
|
||||
* the child process' IO output will be ignored.
|
||||
*
|
||||
* @type {(string|!Array<string|number|!stream.Stream|null|undefined>|
|
||||
* undefined)}
|
||||
* @see <https://nodejs.org/dist/latest-v8.x/docs/api/child_process.html#child_process_options_stdio>
|
||||
*/
|
||||
this.stdio
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a command's termination conditions.
|
||||
*/
|
||||
class Result {
|
||||
/**
|
||||
* @param {?number} code The exit code, or {@code null} if the command did not
|
||||
* exit normally.
|
||||
* @param {?string} signal The signal used to kill the command, or
|
||||
* {@code null}.
|
||||
*/
|
||||
constructor(code, signal) {
|
||||
/** @type {?number} */
|
||||
this.code = code
|
||||
|
||||
/** @type {?string} */
|
||||
this.signal = signal
|
||||
}
|
||||
|
||||
/** @override */
|
||||
toString() {
|
||||
return `Result(code=${this.code}, signal=${this.signal})`
|
||||
}
|
||||
}
|
||||
|
||||
const COMMAND_RESULT =
|
||||
/** !WeakMap<!Command, !Promise<!Result>> */ new WeakMap()
|
||||
const KILL_HOOK = /** !WeakMap<!Command, function(string)> */ new WeakMap()
|
||||
|
||||
/**
|
||||
* Represents a command running in a sub-process.
|
||||
*/
|
||||
class Command {
|
||||
/**
|
||||
* @param {!Promise<!Result>} result The command result.
|
||||
* @param {function(string)} onKill The function to call when {@link #kill()}
|
||||
* is called.
|
||||
*/
|
||||
constructor(result, onKill) {
|
||||
COMMAND_RESULT.set(this, result)
|
||||
KILL_HOOK.set(this, onKill)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Result>} A promise for the result of this
|
||||
* command.
|
||||
*/
|
||||
result() {
|
||||
return /** @type {!Promise<!Result>} */ (COMMAND_RESULT.get(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a signal to the underlying process.
|
||||
* @param {string=} opt_signal The signal to send; defaults to `SIGTERM`.
|
||||
*/
|
||||
kill(opt_signal) {
|
||||
KILL_HOOK.get(this)(opt_signal || 'SIGTERM')
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
/**
|
||||
* Spawns a child process. The returned {@link Command} may be used to wait
|
||||
* for the process result or to send signals to the process.
|
||||
*
|
||||
* @param {string} command The executable to spawn.
|
||||
* @param {Options=} opt_options The command options.
|
||||
* @return {!Command} The launched command.
|
||||
*/
|
||||
function exec(command, opt_options) {
|
||||
const options = opt_options || {}
|
||||
|
||||
let proc = childProcess.spawn(command, options.args || [], {
|
||||
env: options.env || process.env,
|
||||
stdio: options.stdio || 'ignore',
|
||||
})
|
||||
|
||||
// This process should not wait on the spawned child, however, we do
|
||||
// want to ensure the child is killed when this process exits.
|
||||
proc.unref()
|
||||
process.once('exit', onProcessExit)
|
||||
|
||||
const result = new Promise((resolve) => {
|
||||
proc.once('exit', (code, signal) => {
|
||||
proc = null
|
||||
process.removeListener('exit', onProcessExit)
|
||||
resolve(new Result(code, signal))
|
||||
})
|
||||
})
|
||||
return new Command(result, killCommand)
|
||||
|
||||
function onProcessExit() {
|
||||
killCommand('SIGTERM')
|
||||
}
|
||||
|
||||
function killCommand(signal) {
|
||||
process.removeListener('exit', onProcessExit)
|
||||
if (proc) {
|
||||
proc.kill(signal)
|
||||
proc = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Exported to improve generated API documentation.
|
||||
|
||||
module.exports = {
|
||||
Command,
|
||||
Options,
|
||||
Result,
|
||||
exec,
|
||||
}
|
||||
351
node_modules/selenium-webdriver/io/index.js
generated
vendored
Normal file
351
node_modules/selenium-webdriver/io/index.js
generated
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const tmp = require('tmp')
|
||||
|
||||
/**
|
||||
* @param {!Function} fn .
|
||||
* @return {!Promise<T>} .
|
||||
* @template T
|
||||
*/
|
||||
function checkedCall(fn) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
fn((err, value) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(value)
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively removes a directory and all of its contents. This is equivalent
|
||||
* to {@code rm -rf} on a POSIX system.
|
||||
* @param {string} dirPath Path to the directory to remove.
|
||||
* @return {!Promise} A promise to be resolved when the operation has
|
||||
* completed.
|
||||
*/
|
||||
function rmDir(dirPath) {
|
||||
return new Promise(function (fulfill, reject) {
|
||||
fs.rm(dirPath, { recursive: true, maxRetries: 2 }, function (err) {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
fulfill()
|
||||
} else if (err) {
|
||||
reject(err)
|
||||
}
|
||||
fulfill()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies one file to another.
|
||||
* @param {string} src The source file.
|
||||
* @param {string} dst The destination file.
|
||||
* @return {!Promise<string>} A promise for the copied file's path.
|
||||
*/
|
||||
function copy(src, dst) {
|
||||
return new Promise(function (fulfill, reject) {
|
||||
const rs = fs.createReadStream(src)
|
||||
rs.on('error', reject)
|
||||
|
||||
const ws = fs.createWriteStream(dst)
|
||||
ws.on('error', reject)
|
||||
ws.on('close', () => fulfill(dst))
|
||||
|
||||
rs.pipe(ws)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copies the contents of one directory to another.
|
||||
* @param {string} src The source directory to copy.
|
||||
* @param {string} dst The directory to copy into.
|
||||
* @param {(RegExp|function(string): boolean)=} opt_exclude An exclusion filter
|
||||
* as either a regex or predicate function. All files matching this filter
|
||||
* will not be copied.
|
||||
* @return {!Promise<string>} A promise for the destination
|
||||
* directory's path once all files have been copied.
|
||||
*/
|
||||
function copyDir(src, dst, opt_exclude) {
|
||||
let predicate = opt_exclude
|
||||
if (opt_exclude && typeof opt_exclude !== 'function') {
|
||||
predicate = function (p) {
|
||||
return !opt_exclude.test(p)
|
||||
}
|
||||
}
|
||||
|
||||
if (!fs.existsSync(dst)) {
|
||||
fs.mkdirSync(dst)
|
||||
}
|
||||
|
||||
let files = fs.readdirSync(src)
|
||||
files = files.map(function (file) {
|
||||
return path.join(src, file)
|
||||
})
|
||||
|
||||
if (predicate) {
|
||||
files = files.filter(/** @type {function(string): boolean} */ (predicate))
|
||||
}
|
||||
|
||||
const results = []
|
||||
files.forEach(function (file) {
|
||||
const stats = fs.statSync(file)
|
||||
const target = path.join(dst, path.basename(file))
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
if (!fs.existsSync(target)) {
|
||||
fs.mkdirSync(target, stats.mode)
|
||||
}
|
||||
results.push(copyDir(file, target, predicate))
|
||||
} else {
|
||||
results.push(copy(file, target))
|
||||
}
|
||||
})
|
||||
|
||||
return Promise.all(results).then(() => dst)
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a file path exists.
|
||||
* @param {string} aPath The path to test.
|
||||
* @return {!Promise<boolean>} A promise for whether the file exists.
|
||||
*/
|
||||
function exists(aPath) {
|
||||
return new Promise(function (fulfill, reject) {
|
||||
let type = typeof aPath
|
||||
if (type !== 'string') {
|
||||
reject(TypeError(`expected string path, but got ${type}`))
|
||||
} else {
|
||||
fulfill(fs.existsSync(aPath))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `stat(2)`.
|
||||
* @param {string} aPath The path to stat.
|
||||
* @return {!Promise<!fs.Stats>} A promise for the file stats.
|
||||
*/
|
||||
function stat(aPath) {
|
||||
return checkedCall((callback) => fs.stat(aPath, callback))
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a name from the filesystem and possibly the file it refers to. Has
|
||||
* no effect if the file does not exist.
|
||||
* @param {string} aPath The path to remove.
|
||||
* @return {!Promise} A promise for when the file has been removed.
|
||||
*/
|
||||
function unlink(aPath) {
|
||||
return new Promise(function (fulfill, reject) {
|
||||
const exists = fs.existsSync(aPath)
|
||||
if (exists) {
|
||||
fs.unlink(aPath, function (err) {
|
||||
;(err && reject(err)) || fulfill()
|
||||
})
|
||||
} else {
|
||||
fulfill()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<string>} A promise for the path to a temporary directory.
|
||||
* @see https://www.npmjs.org/package/tmp
|
||||
*/
|
||||
function tmpDir() {
|
||||
return checkedCall((callback) => tmp.dir({ unsafeCleanup: true }, callback))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{postfix: string}=} opt_options Temporary file options.
|
||||
* @return {!Promise<string>} A promise for the path to a temporary file.
|
||||
* @see https://www.npmjs.org/package/tmp
|
||||
*/
|
||||
function tmpFile(opt_options) {
|
||||
return checkedCall((callback) => {
|
||||
/** check fixed in v > 0.2.1 if
|
||||
* (typeof options === 'function') {
|
||||
* return [{}, options];
|
||||
* }
|
||||
*/
|
||||
tmp.file(opt_options, callback)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the {@code PATH} environment variable for the given file.
|
||||
* @param {string} file The file to locate on the PATH.
|
||||
* @param {boolean=} opt_checkCwd Whether to always start with the search with
|
||||
* the current working directory, regardless of whether it is explicitly
|
||||
* listed on the PATH.
|
||||
* @return {?string} Path to the located file, or {@code null} if it could
|
||||
* not be found.
|
||||
*/
|
||||
function findInPath(file, opt_checkCwd) {
|
||||
const dirs = []
|
||||
if (opt_checkCwd) {
|
||||
dirs.push(process.cwd())
|
||||
}
|
||||
dirs.push.apply(dirs, process.env['PATH'].split(path.delimiter))
|
||||
|
||||
let foundInDir = dirs.find((dir) => {
|
||||
let tmp = path.join(dir, file)
|
||||
try {
|
||||
let stats = fs.statSync(tmp)
|
||||
return stats.isFile() && !stats.isDirectory()
|
||||
} catch (ex) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
return foundInDir ? path.join(foundInDir, file) : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of the given file.
|
||||
*
|
||||
* @param {string} aPath Path to the file to read.
|
||||
* @return {!Promise<!Buffer>} A promise that will resolve with a buffer of the
|
||||
* file contents.
|
||||
*/
|
||||
function read(aPath) {
|
||||
return checkedCall((callback) => fs.readFile(aPath, callback))
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes to a file.
|
||||
*
|
||||
* @param {string} aPath Path to the file to write to.
|
||||
* @param {(string|!Buffer)} data The data to write.
|
||||
* @return {!Promise} A promise that will resolve when the operation has
|
||||
* completed.
|
||||
*/
|
||||
function write(aPath, data) {
|
||||
return checkedCall((callback) => fs.writeFile(aPath, data, callback))
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory.
|
||||
*
|
||||
* @param {string} aPath The directory path.
|
||||
* @return {!Promise<string>} A promise that will resolve with the path of the
|
||||
* created directory.
|
||||
*/
|
||||
function mkdir(aPath) {
|
||||
return checkedCall((callback) => {
|
||||
fs.mkdir(aPath, undefined, (err) => {
|
||||
if (err && err.code !== 'EEXIST') {
|
||||
callback(err)
|
||||
} else {
|
||||
callback(null, aPath)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively creates a directory and any ancestors that do not yet exist.
|
||||
*
|
||||
* @param {string} dir The directory path to create.
|
||||
* @return {!Promise<string>} A promise that will resolve with the path of the
|
||||
* created directory.
|
||||
*/
|
||||
function mkdirp(dir) {
|
||||
return checkedCall((callback) => {
|
||||
fs.mkdir(dir, undefined, (err) => {
|
||||
if (!err) {
|
||||
callback(null, dir)
|
||||
return
|
||||
}
|
||||
|
||||
switch (err.code) {
|
||||
case 'EEXIST':
|
||||
callback(null, dir)
|
||||
return
|
||||
case 'ENOENT':
|
||||
return mkdirp(path.dirname(dir))
|
||||
.then(() => mkdirp(dir))
|
||||
.then(
|
||||
() => callback(null, dir),
|
||||
(err) => callback(err)
|
||||
)
|
||||
default:
|
||||
callback(err)
|
||||
return
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively walks a directory, returning a promise that will resolve with
|
||||
* a list of all files/directories seen.
|
||||
*
|
||||
* @param {string} rootPath the directory to walk.
|
||||
* @return {!Promise<!Array<{path: string, dir: boolean}>>} a promise that will
|
||||
* resolve with a list of entries seen. For each entry, the recorded path
|
||||
* will be relative to `rootPath`.
|
||||
*/
|
||||
function walkDir(rootPath) {
|
||||
const seen = []
|
||||
return (function walk(dir) {
|
||||
return checkedCall((callback) => fs.readdir(dir, callback)).then((files) =>
|
||||
Promise.all(
|
||||
files.map((file) => {
|
||||
file = path.join(dir, file)
|
||||
return checkedCall((cb) => fs.stat(file, cb)).then((stats) => {
|
||||
seen.push({
|
||||
path: path.relative(rootPath, file),
|
||||
dir: stats.isDirectory(),
|
||||
})
|
||||
return stats.isDirectory() && walk(file)
|
||||
})
|
||||
})
|
||||
)
|
||||
)
|
||||
})(rootPath).then(() => seen)
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = {
|
||||
walkDir,
|
||||
rmDir,
|
||||
mkdirp,
|
||||
mkdir,
|
||||
write,
|
||||
read,
|
||||
findInPath,
|
||||
tmpFile,
|
||||
tmpDir,
|
||||
unlink,
|
||||
copy,
|
||||
copyDir,
|
||||
exists,
|
||||
stat,
|
||||
}
|
||||
221
node_modules/selenium-webdriver/io/zip.js
generated
vendored
Normal file
221
node_modules/selenium-webdriver/io/zip.js
generated
vendored
Normal file
@@ -0,0 +1,221 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const jszip = require('jszip')
|
||||
const path = require('path')
|
||||
|
||||
const io = require('./index')
|
||||
const { InvalidArgumentError } = require('../lib/error')
|
||||
|
||||
/**
|
||||
* Manages a zip archive.
|
||||
*/
|
||||
class Zip {
|
||||
constructor() {
|
||||
/** @private @const */
|
||||
this.z_ = new jszip()
|
||||
|
||||
/** @private @const {!Set<!Promise<?>>} */
|
||||
this.pendingAdds_ = new Set()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a file to this zip.
|
||||
*
|
||||
* @param {string} filePath path to the file to add.
|
||||
* @param {string=} zipPath path to the file in the zip archive, defaults
|
||||
* to the basename of `filePath`.
|
||||
* @return {!Promise<?>} a promise that will resolve when added.
|
||||
*/
|
||||
addFile(filePath, zipPath = path.basename(filePath)) {
|
||||
let add = io
|
||||
.read(filePath)
|
||||
.then((buffer) =>
|
||||
this.z_.file(
|
||||
/** @type {string} */ (zipPath.replace(/\\/g, '/')),
|
||||
buffer
|
||||
)
|
||||
)
|
||||
this.pendingAdds_.add(add)
|
||||
return add.then(
|
||||
() => this.pendingAdds_.delete(add),
|
||||
(e) => {
|
||||
this.pendingAdds_.delete(add)
|
||||
throw e
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively adds a directory and all of its contents to this archive.
|
||||
*
|
||||
* @param {string} dirPath path to the directory to add.
|
||||
* @param {string=} zipPath path to the folder in the archive to add the
|
||||
* directory contents to. Defaults to the root folder.
|
||||
* @return {!Promise<?>} returns a promise that will resolve when
|
||||
* the operation is complete.
|
||||
*/
|
||||
addDir(dirPath, zipPath = '') {
|
||||
return io.walkDir(dirPath).then((entries) => {
|
||||
let archive = this.z_
|
||||
if (zipPath) {
|
||||
archive = archive.folder(zipPath)
|
||||
}
|
||||
|
||||
let files = []
|
||||
entries.forEach((spec) => {
|
||||
if (spec.dir) {
|
||||
archive.folder(spec.path)
|
||||
} else {
|
||||
files.push(
|
||||
this.addFile(
|
||||
path.join(dirPath, spec.path),
|
||||
path.join(zipPath, spec.path)
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
return Promise.all(files)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path File path to test for within the archive.
|
||||
* @return {boolean} Whether this zip archive contains an entry with the given
|
||||
* path.
|
||||
*/
|
||||
has(path) {
|
||||
return this.z_.file(path) !== null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the file in this zip archive with the given `path`.
|
||||
* The returned promise will be rejected with an {@link InvalidArgumentError}
|
||||
* if either `path` does not exist within the archive, or if `path` refers
|
||||
* to a directory.
|
||||
*
|
||||
* @param {string} path the path to the file whose contents to return.
|
||||
* @return {!Promise<!Buffer>} a promise that will be resolved with the file's
|
||||
* contents as a buffer.
|
||||
*/
|
||||
getFile(path) {
|
||||
let file = this.z_.file(path)
|
||||
if (!file) {
|
||||
return Promise.reject(
|
||||
new InvalidArgumentError(`No such file in zip archive: ${path}`)
|
||||
)
|
||||
}
|
||||
|
||||
if (file.dir) {
|
||||
return Promise.reject(
|
||||
new InvalidArgumentError(`The requested file is a directory: ${path}`)
|
||||
)
|
||||
}
|
||||
|
||||
return Promise.resolve(file.async('nodebuffer'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the compressed data for this archive in a buffer. _This method will
|
||||
* not wait for any outstanding {@link #addFile add}
|
||||
* {@link #addDir operations} before encoding the archive._
|
||||
*
|
||||
* @param {string} compression The desired compression.
|
||||
* Must be `STORE` (the default) or `DEFLATE`.
|
||||
* @return {!Promise<!Buffer>} a promise that will resolve with this archive
|
||||
* as a buffer.
|
||||
*/
|
||||
toBuffer(compression = 'STORE') {
|
||||
if (compression !== 'STORE' && compression !== 'DEFLATE') {
|
||||
return Promise.reject(
|
||||
new InvalidArgumentError(
|
||||
`compression must be one of {STORE, DEFLATE}, got ${compression}`
|
||||
)
|
||||
)
|
||||
}
|
||||
return Promise.resolve(
|
||||
this.z_.generateAsync({ compression, type: 'nodebuffer' })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously opens a zip archive.
|
||||
*
|
||||
* @param {string} path to the zip archive to load.
|
||||
* @return {!Promise<!Zip>} a promise that will resolve with the opened
|
||||
* archive.
|
||||
*/
|
||||
function load(path) {
|
||||
return io.read(path).then((data) => {
|
||||
let zip = new Zip()
|
||||
return zip.z_.loadAsync(data).then(() => zip)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously unzips an archive file.
|
||||
*
|
||||
* @param {string} src path to the source file to unzip.
|
||||
* @param {string} dst path to the destination directory.
|
||||
* @return {!Promise<string>} a promise that will resolve with `dst` once the
|
||||
* archive has been unzipped.
|
||||
*/
|
||||
function unzip(src, dst) {
|
||||
return load(src).then((zip) => {
|
||||
const promisedDirs = new Map()
|
||||
const promises = []
|
||||
|
||||
zip.z_.forEach((relPath, file) => {
|
||||
let p
|
||||
if (file.dir) {
|
||||
p = createDir(relPath)
|
||||
} else {
|
||||
let dirname = path.dirname(relPath)
|
||||
if (dirname === '.') {
|
||||
p = writeFile(relPath, file)
|
||||
} else {
|
||||
p = createDir(dirname).then(() => writeFile(relPath, file))
|
||||
}
|
||||
}
|
||||
promises.push(p)
|
||||
})
|
||||
|
||||
return Promise.all(promises).then(() => dst)
|
||||
|
||||
function createDir(dir) {
|
||||
let p = promisedDirs.get(dir)
|
||||
if (!p) {
|
||||
p = io.mkdirp(path.join(dst, dir))
|
||||
promisedDirs.set(dir, p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
function writeFile(relPath, file) {
|
||||
return file
|
||||
.async('nodebuffer')
|
||||
.then((buffer) => io.write(path.join(dst, relPath), buffer))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = { Zip, load, unzip }
|
||||
123
node_modules/selenium-webdriver/lib/atoms/find-elements.js
generated
vendored
Normal file
123
node_modules/selenium-webdriver/lib/atoms/find-elements.js
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// GENERATED CODE - DO NOT EDIT
|
||||
module.exports = function(){return (function(){var aa=this||self;function ba(a){return"string"==typeof a}function ca(a,b){a=a.split(".");var c=aa;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b}
|
||||
function da(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
|
||||
else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function ea(a){return"function"==da(a)}function ha(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ia(a,b,c){return a.call.apply(a.bind,arguments)}
|
||||
function ja(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var e=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(e,d);return a.apply(b,e)}}return function(){return a.apply(b,arguments)}}function ka(a,b,c){Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ka=ia:ka=ja;return ka.apply(null,arguments)}
|
||||
function la(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var d=c.slice();d.push.apply(d,arguments);return a.apply(this,d)}}function k(a,b){function c(){}c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a};/*
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2007 Cybozu Labs, Inc.
|
||||
Copyright (c) 2012 Google Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
function ma(a,b,c){this.a=a;this.b=b||1;this.f=c||1};var na;var oa=Array.prototype.indexOf?function(a,b){return Array.prototype.indexOf.call(a,b,void 0)}:function(a,b){if("string"===typeof a)return"string"!==typeof b||1!=b.length?-1:a.indexOf(b,0);for(var c=0;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},l=Array.prototype.forEach?function(a,b,c){Array.prototype.forEach.call(a,b,c)}:function(a,b,c){for(var d=a.length,e="string"===typeof a?a.split(""):a,f=0;f<d;f++)f in e&&b.call(c,e[f],f,a)},pa=Array.prototype.filter?function(a,b){return Array.prototype.filter.call(a,
|
||||
b,void 0)}:function(a,b){for(var c=a.length,d=[],e=0,f="string"===typeof a?a.split(""):a,g=0;g<c;g++)if(g in f){var h=f[g];b.call(void 0,h,g,a)&&(d[e++]=h)}return d},qa=Array.prototype.map?function(a,b){return Array.prototype.map.call(a,b,void 0)}:function(a,b){for(var c=a.length,d=Array(c),e="string"===typeof a?a.split(""):a,f=0;f<c;f++)f in e&&(d[f]=b.call(void 0,e[f],f,a));return d},ra=Array.prototype.reduce?function(a,b,c){return Array.prototype.reduce.call(a,b,c)}:function(a,b,c){var d=c;l(a,
|
||||
function(e,f){d=b.call(void 0,d,e,f,a)});return d},sa=Array.prototype.some?function(a,b){return Array.prototype.some.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a))return!0;return!1},ta=Array.prototype.every?function(a,b,c){return Array.prototype.every.call(a,b,c)}:function(a,b,c){for(var d=a.length,e="string"===typeof a?a.split(""):a,f=0;f<d;f++)if(f in e&&!b.call(c,e[f],f,a))return!1;return!0};
|
||||
function ua(a,b){a:{for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:"string"===typeof a?a.charAt(b):a[b]}function va(a){return Array.prototype.concat.apply([],arguments)}function wa(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)}function xa(a,b){a.sort(b||ya)}function ya(a,b){return a>b?1:a<b?-1:0};function za(a){var b=a.length-1;return 0<=b&&a.indexOf(" ",b)==b}var Aa=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\s\xa0]*([\s\S]*?)[\s\xa0]*$/.exec(a)[1]};
|
||||
function Ba(a,b){var c=0;a=Aa(String(a)).split(".");b=Aa(String(b)).split(".");for(var d=Math.max(a.length,b.length),e=0;0==c&&e<d;e++){var f=a[e]||"",g=b[e]||"";do{f=/(\d*)(\D*)(.*)/.exec(f)||["","","",""];g=/(\d*)(\D*)(.*)/.exec(g)||["","","",""];if(0==f[0].length&&0==g[0].length)break;c=Ca(0==f[1].length?0:parseInt(f[1],10),0==g[1].length?0:parseInt(g[1],10))||Ca(0==f[2].length,0==g[2].length)||Ca(f[2],g[2]);f=f[3];g=g[3]}while(0==c)}return c}function Ca(a,b){return a<b?-1:a>b?1:0};var q;a:{var Da=aa.navigator;if(Da){var Ea=Da.userAgent;if(Ea){q=Ea;break a}}q=""}function r(a){return-1!=q.indexOf(a)};function Fa(){return r("Firefox")||r("FxiOS")}function Ga(){return(r("Chrome")||r("CriOS"))&&!r("Edge")};function Ha(a){return String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()})};function Ia(){return r("iPhone")&&!r("iPod")&&!r("iPad")};function Ja(a,b){var c=Ka;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var La=r("Opera"),t=r("Trident")||r("MSIE"),Ma=r("Edge"),Na=r("Gecko")&&!(-1!=q.toLowerCase().indexOf("webkit")&&!r("Edge"))&&!(r("Trident")||r("MSIE"))&&!r("Edge"),Oa=-1!=q.toLowerCase().indexOf("webkit")&&!r("Edge");function Pa(){var a=aa.document;return a?a.documentMode:void 0}var Qa;
|
||||
a:{var Ra="",Sa=function(){var a=q;if(Na)return/rv:([^\);]+)(\)|;)/.exec(a);if(Ma)return/Edge\/([\d\.]+)/.exec(a);if(t)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Oa)return/WebKit\/(\S+)/.exec(a);if(La)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Sa&&(Ra=Sa?Sa[1]:"");if(t){var Ta=Pa();if(null!=Ta&&Ta>parseFloat(Ra)){Qa=String(Ta);break a}}Qa=Ra}var Ka={};function Ua(a){return Ja(a,function(){return 0<=Ba(Qa,a)})}var w;w=aa.document&&t?Pa():void 0;var x=t&&!(9<=Number(w)),Va=t&&!(8<=Number(w));function Wa(a,b,c,d){this.a=a;this.nodeName=c;this.nodeValue=d;this.nodeType=2;this.parentNode=this.ownerElement=b}function Xa(a,b){var c=Va&&"href"==b.nodeName?a.getAttribute(b.nodeName,2):b.nodeValue;return new Wa(b,a,b.nodeName,c)};function Ya(a){this.b=a;this.a=0}function Za(a){a=a.match($a);for(var b=0;b<a.length;b++)ab.test(a[b])&&a.splice(b,1);return new Ya(a)}var $a=/\$?(?:(?![0-9-\.])(?:\*|[\w-\.]+):)?(?![0-9-\.])(?:\*|[\w-\.]+)|\/\/|\.\.|::|\d+(?:\.\d*)?|\.\d+|"[^"]*"|'[^']*'|[!<>]=|\s+|./g,ab=/^\s/;function y(a,b){return a.b[a.a+(b||0)]}function z(a){return a.b[a.a++]}function bb(a){return a.b.length<=a.a};function cb(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}cb.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};cb.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};cb.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function db(a,b){this.width=a;this.height=b}db.prototype.aspectRatio=function(){return this.width/this.height};db.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};db.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};db.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function eb(a){return a?new fb(A(a)):na||(na=new fb)}function gb(a){for(;a&&1!=a.nodeType;)a=a.previousSibling;return a}function hb(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}
|
||||
function ib(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(t&&!(9<=Number(w))){if(9==a.nodeType)return-1;if(9==b.nodeType)return 1}if("sourceIndex"in a||a.parentNode&&"sourceIndex"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?jb(a,b):!c&&hb(e,b)?-1*kb(a,b):!d&&hb(f,a)?kb(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=A(a);c=d.createRange();
|
||||
c.selectNode(a);c.collapse(!0);a=d.createRange();a.selectNode(b);a.collapse(!0);return c.compareBoundaryPoints(aa.Range.START_TO_END,a)}function kb(a,b){var c=a.parentNode;if(c==b)return-1;for(;b.parentNode!=c;)b=b.parentNode;return jb(b,a)}function jb(a,b){for(;b=b.previousSibling;)if(b==a)return-1;return 1}function A(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function lb(a,b){a&&(a=a.parentNode);for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}
|
||||
function fb(a){this.a=a||aa.document||document}fb.prototype.getElementsByTagName=function(a,b){return(b||this.a).getElementsByTagName(String(a))};
|
||||
function mb(a,b,c,d){a=d||a.a;var e=b&&"*"!=b?String(b).toUpperCase():"";if(a.querySelectorAll&&a.querySelector&&(e||c))c=a.querySelectorAll(e+(c?"."+c:""));else if(c&&a.getElementsByClassName)if(b=a.getElementsByClassName(c),e){a={};for(var f=d=0,g;g=b[f];f++)e==g.nodeName&&(a[d++]=g);a.length=d;c=a}else c=b;else if(b=a.getElementsByTagName(e||"*"),c){a={};for(f=d=0;g=b[f];f++){e=g.className;var h;if(h="function"==typeof e.split)h=0<=oa(e.split(/\s+/),c);h&&(a[d++]=g)}a.length=d;c=a}else c=b;return c}
|
||||
;function B(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?"":b);if("string"!=typeof b)if(x&&"title"==a.nodeName.toLowerCase()&&1==c)b=a.text;else if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;c=0;var d=[];for(b="";a;){do 1!=a.nodeType&&(b+=a.nodeValue),x&&"title"==a.nodeName.toLowerCase()&&(b+=a.text),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return b}
|
||||
function nb(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}Va&&"class"==b&&(b="className");return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function ob(a,b,c,d,e){return(x?pb:qb).call(null,a,b,ba(c)?c:null,ba(d)?d:null,e||new C)}
|
||||
function pb(a,b,c,d,e){if(a instanceof rb||8==a.b||c&&null===a.b){var f=b.all;if(!f)return e;a=sb(a);if("*"!=a&&(f=b.getElementsByTagName(a),!f))return e;if(c){for(var g=[],h=0;b=f[h++];)nb(b,c,d)&&g.push(b);f=g}for(h=0;b=f[h++];)"*"==a&&"!"==b.tagName||e.add(b);return e}tb(a,b,c,d,e);return e}
|
||||
function qb(a,b,c,d,e){b.getElementsByName&&d&&"name"==c&&!t?(b=b.getElementsByName(d),l(b,function(f){a.a(f)&&e.add(f)})):b.getElementsByClassName&&d&&"class"==c?(b=b.getElementsByClassName(d),l(b,function(f){f.className==d&&a.a(f)&&e.add(f)})):a instanceof D?tb(a,b,c,d,e):b.getElementsByTagName&&(b=b.getElementsByTagName(a.f()),l(b,function(f){nb(f,c,d)&&e.add(f)}));return e}
|
||||
function ub(a,b,c,d,e){var f;if((a instanceof rb||8==a.b||c&&null===a.b)&&(f=b.childNodes)){var g=sb(a);if("*"!=g&&(f=pa(f,function(h){return h.tagName&&h.tagName.toLowerCase()==g}),!f))return e;c&&(f=pa(f,function(h){return nb(h,c,d)}));l(f,function(h){"*"==g&&("!"==h.tagName||"*"==g&&1!=h.nodeType)||e.add(h)});return e}return vb(a,b,c,d,e)}function vb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)nb(b,c,d)&&a.a(b)&&e.add(b);return e}
|
||||
function tb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)nb(b,c,d)&&a.a(b)&&e.add(b),tb(a,b,c,d,e)}function sb(a){if(a instanceof D){if(8==a.b)return"!";if(null===a.b)return"*"}return a.f()};function C(){this.b=this.a=null;this.m=0}function wb(a){this.f=a;this.a=this.b=null}function xb(a,b){if(!a.a)return b;if(!b.a)return a;var c=a.a;b=b.a;for(var d=null,e,f=0;c&&b;){e=c.f;var g=b.f;e==g||e instanceof Wa&&g instanceof Wa&&e.a==g.a?(e=c,c=c.a,b=b.a):0<ib(c.f,b.f)?(e=b,b=b.a):(e=c,c=c.a);(e.b=d)?d.a=e:a.a=e;d=e;f++}for(e=c||b;e;)e.b=d,d=d.a=e,f++,e=e.a;a.b=d;a.m=f;return a}function yb(a,b){b=new wb(b);b.a=a.a;a.b?a.a.b=b:a.a=a.b=b;a.a=b;a.m++}
|
||||
C.prototype.add=function(a){a=new wb(a);a.b=this.b;this.a?this.b.a=a:this.a=this.b=a;this.b=a;this.m++};function zb(a){return(a=a.a)?a.f:null}function Bb(a){return(a=zb(a))?B(a):""}function Cb(a,b){return new Db(a,!!b)}function Db(a,b){this.f=a;this.b=(this.A=b)?a.b:a.a;this.a=null}function E(a){var b=a.b;if(null==b)return null;var c=a.a=b;a.b=a.A?b.b:b.a;return c.f};function F(a){this.i=a;this.b=this.g=!1;this.f=null}function H(a){return"\n "+a.toString().split("\n").join("\n ")}function Eb(a,b){a.g=b}function Fb(a,b){a.b=b}function I(a,b){a=a.a(b);return a instanceof C?+Bb(a):+a}function J(a,b){a=a.a(b);return a instanceof C?Bb(a):""+a}function Gb(a,b){a=a.a(b);return a instanceof C?!!a.m:!!a};function Hb(a,b,c){F.call(this,a.i);this.c=a;this.h=b;this.v=c;this.g=b.g||c.g;this.b=b.b||c.b;this.c==Ib&&(c.b||c.g||4==c.i||0==c.i||!b.f?b.b||b.g||4==b.i||0==b.i||!c.f||(this.f={name:c.f.name,B:b}):this.f={name:b.f.name,B:c})}k(Hb,F);
|
||||
function Jb(a,b,c,d,e){b=b.a(d);c=c.a(d);var f;if(b instanceof C&&c instanceof C){b=Cb(b);for(d=E(b);d;d=E(b))for(e=Cb(c),f=E(e);f;f=E(e))if(a(B(d),B(f)))return!0;return!1}if(b instanceof C||c instanceof C){b instanceof C?(e=b,d=c):(e=c,d=b);f=Cb(e);for(var g=typeof d,h=E(f);h;h=E(f)){switch(g){case "number":h=+B(h);break;case "boolean":h=!!B(h);break;case "string":h=B(h);break;default:throw Error("Illegal primitive type for comparison.");}if(e==b&&a(h,d)||e==c&&a(d,h))return!0}return!1}return e?
|
||||
"boolean"==typeof b||"boolean"==typeof c?a(!!b,!!c):"number"==typeof b||"number"==typeof c?a(+b,+c):a(b,c):a(+b,+c)}Hb.prototype.a=function(a){return this.c.s(this.h,this.v,a)};Hb.prototype.toString=function(){var a="Binary Expression: "+this.c;a+=H(this.h);return a+=H(this.v)};function Kb(a,b,c,d){this.$=a;this.M=b;this.i=c;this.s=d}Kb.prototype.toString=function(){return this.$};var Lb={};
|
||||
function K(a,b,c,d){if(Lb.hasOwnProperty(a))throw Error("Binary operator already created: "+a);a=new Kb(a,b,c,d);return Lb[a.toString()]=a}K("div",6,1,function(a,b,c){return I(a,c)/I(b,c)});K("mod",6,1,function(a,b,c){return I(a,c)%I(b,c)});K("*",6,1,function(a,b,c){return I(a,c)*I(b,c)});K("+",5,1,function(a,b,c){return I(a,c)+I(b,c)});K("-",5,1,function(a,b,c){return I(a,c)-I(b,c)});K("<",4,2,function(a,b,c){return Jb(function(d,e){return d<e},a,b,c)});
|
||||
K(">",4,2,function(a,b,c){return Jb(function(d,e){return d>e},a,b,c)});K("<=",4,2,function(a,b,c){return Jb(function(d,e){return d<=e},a,b,c)});K(">=",4,2,function(a,b,c){return Jb(function(d,e){return d>=e},a,b,c)});var Ib=K("=",3,2,function(a,b,c){return Jb(function(d,e){return d==e},a,b,c,!0)});K("!=",3,2,function(a,b,c){return Jb(function(d,e){return d!=e},a,b,c,!0)});K("and",2,2,function(a,b,c){return Gb(a,c)&&Gb(b,c)});K("or",1,2,function(a,b,c){return Gb(a,c)||Gb(b,c)});function Mb(a,b){if(b.a.length&&4!=a.i)throw Error("Primary expression must evaluate to nodeset if filter has predicate(s).");F.call(this,a.i);this.c=a;this.h=b;this.g=a.g;this.b=a.b}k(Mb,F);Mb.prototype.a=function(a){a=this.c.a(a);return Nb(this.h,a)};Mb.prototype.toString=function(){var a="Filter:"+H(this.c);return a+=H(this.h)};function Ob(a,b){if(b.length<a.L)throw Error("Function "+a.l+" expects at least"+a.L+" arguments, "+b.length+" given");if(null!==a.H&&b.length>a.H)throw Error("Function "+a.l+" expects at most "+a.H+" arguments, "+b.length+" given");a.Z&&l(b,function(c,d){if(4!=c.i)throw Error("Argument "+d+" to function "+a.l+" is not of type Nodeset: "+c);});F.call(this,a.i);this.D=a;this.c=b;Eb(this,a.g||sa(b,function(c){return c.g}));Fb(this,a.Y&&!b.length||a.X&&!!b.length||sa(b,function(c){return c.b}))}
|
||||
k(Ob,F);Ob.prototype.a=function(a){return this.D.s.apply(null,va(a,this.c))};Ob.prototype.toString=function(){var a="Function: "+this.D;if(this.c.length){var b=ra(this.c,function(c,d){return c+H(d)},"Arguments:");a+=H(b)}return a};function Pb(a,b,c,d,e,f,g,h){this.l=a;this.i=b;this.g=c;this.Y=d;this.X=!1;this.s=e;this.L=f;this.H=void 0!==g?g:f;this.Z=!!h}Pb.prototype.toString=function(){return this.l};var Qb={};
|
||||
function L(a,b,c,d,e,f,g,h){if(Qb.hasOwnProperty(a))throw Error("Function already created: "+a+".");Qb[a]=new Pb(a,b,c,d,e,f,g,h)}L("boolean",2,!1,!1,function(a,b){return Gb(b,a)},1);L("ceiling",1,!1,!1,function(a,b){return Math.ceil(I(b,a))},1);L("concat",3,!1,!1,function(a,b){return ra(wa(arguments,1),function(c,d){return c+J(d,a)},"")},2,null);L("contains",2,!1,!1,function(a,b,c){b=J(b,a);a=J(c,a);return a=-1!=b.indexOf(a)},2);L("count",1,!1,!1,function(a,b){return b.a(a).m},1,1,!0);
|
||||
L("false",2,!1,!1,function(){return!1},0);L("floor",1,!1,!1,function(a,b){return Math.floor(I(b,a))},1);L("id",4,!1,!1,function(a,b){function c(h){if(x){var n=e.all[h];if(n){if(n.nodeType&&h==n.id)return n;if(n.length)return ua(n,function(u){return h==u.id})}return null}return e.getElementById(h)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument;a=J(b,a).split(/\s+/);var f=[];l(a,function(h){h=c(h);!h||0<=oa(f,h)||f.push(h)});f.sort(ib);var g=new C;l(f,function(h){g.add(h)});return g},1);
|
||||
L("lang",2,!1,!1,function(){return!1},1);L("last",1,!0,!1,function(a){if(1!=arguments.length)throw Error("Function last expects ()");return a.f},0);L("local-name",3,!1,!0,function(a,b){return(a=b?zb(b.a(a)):a.a)?a.localName||a.nodeName.toLowerCase():""},0,1,!0);L("name",3,!1,!0,function(a,b){return(a=b?zb(b.a(a)):a.a)?a.nodeName.toLowerCase():""},0,1,!0);L("namespace-uri",3,!0,!1,function(){return""},0,1,!0);
|
||||
L("normalize-space",3,!1,!0,function(a,b){return(b?J(b,a):B(a.a)).replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"")},0,1);L("not",2,!1,!1,function(a,b){return!Gb(b,a)},1);L("number",1,!1,!0,function(a,b){return b?I(b,a):+B(a.a)},0,1);L("position",1,!0,!1,function(a){return a.b},0);L("round",1,!1,!1,function(a,b){return Math.round(I(b,a))},1);L("starts-with",2,!1,!1,function(a,b,c){b=J(b,a);a=J(c,a);return 0==b.lastIndexOf(a,0)},2);L("string",3,!1,!0,function(a,b){return b?J(b,a):B(a.a)},0,1);
|
||||
L("string-length",1,!1,!0,function(a,b){return(b?J(b,a):B(a.a)).length},0,1);L("substring",3,!1,!1,function(a,b,c,d){c=I(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return"";d=d?I(d,a):Infinity;if(isNaN(d)||-Infinity===d)return"";c=Math.round(c)-1;var e=Math.max(c,0);a=J(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);L("substring-after",3,!1,!1,function(a,b,c){b=J(b,a);a=J(c,a);c=b.indexOf(a);return-1==c?"":b.substring(c+a.length)},2);
|
||||
L("substring-before",3,!1,!1,function(a,b,c){b=J(b,a);a=J(c,a);a=b.indexOf(a);return-1==a?"":b.substring(0,a)},2);L("sum",1,!1,!1,function(a,b){a=Cb(b.a(a));b=0;for(var c=E(a);c;c=E(a))b+=+B(c);return b},1,1,!0);L("translate",3,!1,!1,function(a,b,c,d){b=J(b,a);c=J(c,a);var e=J(d,a);a={};for(d=0;d<c.length;d++){var f=c.charAt(d);f in a||(a[f]=e.charAt(d))}c="";for(d=0;d<b.length;d++)f=b.charAt(d),c+=f in a?a[f]:f;return c},3);L("true",2,!1,!1,function(){return!0},0);function D(a,b){this.h=a;this.c=void 0!==b?b:null;this.b=null;switch(a){case "comment":this.b=8;break;case "text":this.b=3;break;case "processing-instruction":this.b=7;break;case "node":break;default:throw Error("Unexpected argument");}}function Rb(a){return"comment"==a||"text"==a||"processing-instruction"==a||"node"==a}D.prototype.a=function(a){return null===this.b||this.b==a.nodeType};D.prototype.f=function(){return this.h};
|
||||
D.prototype.toString=function(){var a="Kind Test: "+this.h;null===this.c||(a+=H(this.c));return a};function Sb(a){F.call(this,3);this.c=a.substring(1,a.length-1)}k(Sb,F);Sb.prototype.a=function(){return this.c};Sb.prototype.toString=function(){return"Literal: "+this.c};function rb(a,b){this.l=a.toLowerCase();a="*"==this.l?"*":"http://www.w3.org/1999/xhtml";this.c=b?b.toLowerCase():a}rb.prototype.a=function(a){var b=a.nodeType;if(1!=b&&2!=b)return!1;b=void 0!==a.localName?a.localName:a.nodeName;return"*"!=this.l&&this.l!=b.toLowerCase()?!1:"*"==this.c?!0:this.c==(a.namespaceURI?a.namespaceURI.toLowerCase():"http://www.w3.org/1999/xhtml")};rb.prototype.f=function(){return this.l};
|
||||
rb.prototype.toString=function(){return"Name Test: "+("http://www.w3.org/1999/xhtml"==this.c?"":this.c+":")+this.l};function Tb(a){F.call(this,1);this.c=a}k(Tb,F);Tb.prototype.a=function(){return this.c};Tb.prototype.toString=function(){return"Number: "+this.c};function Ub(a,b){F.call(this,a.i);this.h=a;this.c=b;this.g=a.g;this.b=a.b;1==this.c.length&&(a=this.c[0],a.G||a.c!=Vb||(a=a.v,"*"!=a.f()&&(this.f={name:a.f(),B:null})))}k(Ub,F);function Wb(){F.call(this,4)}k(Wb,F);Wb.prototype.a=function(a){var b=new C;a=a.a;9==a.nodeType?b.add(a):b.add(a.ownerDocument);return b};Wb.prototype.toString=function(){return"Root Helper Expression"};function Xb(){F.call(this,4)}k(Xb,F);Xb.prototype.a=function(a){var b=new C;b.add(a.a);return b};Xb.prototype.toString=function(){return"Context Helper Expression"};
|
||||
function Yb(a){return"/"==a||"//"==a}Ub.prototype.a=function(a){var b=this.h.a(a);if(!(b instanceof C))throw Error("Filter expression must evaluate to nodeset.");a=this.c;for(var c=0,d=a.length;c<d&&b.m;c++){var e=a[c],f=Cb(b,e.c.A);if(e.g||e.c!=Zb)if(e.g||e.c!=$b){var g=E(f);for(b=e.a(new ma(g));null!=(g=E(f));)g=e.a(new ma(g)),b=xb(b,g)}else g=E(f),b=e.a(new ma(g));else{for(g=E(f);(b=E(f))&&(!g.contains||g.contains(b))&&b.compareDocumentPosition(g)&8;g=b);b=e.a(new ma(g))}}return b};
|
||||
Ub.prototype.toString=function(){var a="Path Expression:"+H(this.h);if(this.c.length){var b=ra(this.c,function(c,d){return c+H(d)},"Steps:");a+=H(b)}return a};function ac(a,b){this.a=a;this.A=!!b}
|
||||
function Nb(a,b,c){for(c=c||0;c<a.a.length;c++)for(var d=a.a[c],e=Cb(b),f=b.m,g,h=0;g=E(e);h++){var n=a.A?f-h:h+1;g=d.a(new ma(g,n,f));if("number"==typeof g)n=n==g;else if("string"==typeof g||"boolean"==typeof g)n=!!g;else if(g instanceof C)n=0<g.m;else throw Error("Predicate.evaluate returned an unexpected type.");if(!n){n=e;g=n.f;var u=n.a;if(!u)throw Error("Next must be called at least once before remove.");var p=u.b;u=u.a;p?p.a=u:g.a=u;u?u.b=p:g.b=p;g.m--;n.a=null}}return b}
|
||||
ac.prototype.toString=function(){return ra(this.a,function(a,b){return a+H(b)},"Predicates:")};function bc(a,b,c,d){F.call(this,4);this.c=a;this.v=b;this.h=c||new ac([]);this.G=!!d;b=this.h;b=0<b.a.length?b.a[0].f:null;a.ca&&b&&(a=b.name,a=x?a.toLowerCase():a,this.f={name:a,B:b.B});a:{a=this.h;for(b=0;b<a.a.length;b++)if(c=a.a[b],c.g||1==c.i||0==c.i){a=!0;break a}a=!1}this.g=a}k(bc,F);
|
||||
bc.prototype.a=function(a){var b=a.a,c=this.f,d=null,e=null,f=0;c&&(d=c.name,e=c.B?J(c.B,a):null,f=1);if(this.G)if(this.g||this.c!=cc)if(b=Cb((new bc(dc,new D("node"))).a(a)),c=E(b))for(a=this.s(c,d,e,f);null!=(c=E(b));)a=xb(a,this.s(c,d,e,f));else a=new C;else a=ob(this.v,b,d,e),a=Nb(this.h,a,f);else a=this.s(a.a,d,e,f);return a};bc.prototype.s=function(a,b,c,d){a=this.c.D(this.v,a,b,c);return a=Nb(this.h,a,d)};
|
||||
bc.prototype.toString=function(){var a="Step:"+H("Operator: "+(this.G?"//":"/"));this.c.l&&(a+=H("Axis: "+this.c));a+=H(this.v);if(this.h.a.length){var b=ra(this.h.a,function(c,d){return c+H(d)},"Predicates:");a+=H(b)}return a};function ec(a,b,c,d){this.l=a;this.D=b;this.A=c;this.ca=d}ec.prototype.toString=function(){return this.l};var fc={};function M(a,b,c,d){if(fc.hasOwnProperty(a))throw Error("Axis already created: "+a);b=new ec(a,b,c,!!d);return fc[a]=b}
|
||||
M("ancestor",function(a,b){for(var c=new C;b=b.parentNode;)a.a(b)&&yb(c,b);return c},!0);M("ancestor-or-self",function(a,b){var c=new C;do a.a(b)&&yb(c,b);while(b=b.parentNode);return c},!0);
|
||||
var Vb=M("attribute",function(a,b){var c=new C,d=a.f();if("style"==d&&x&&b.style)return c.add(new Wa(b.style,b,"style",b.style.cssText)),c;var e=b.attributes;if(e)if(a instanceof D&&null===a.b||"*"==d)for(a=0;d=e[a];a++)x?d.nodeValue&&c.add(Xa(b,d)):c.add(d);else(d=e.getNamedItem(d))&&(x?d.nodeValue&&c.add(Xa(b,d)):c.add(d));return c},!1),cc=M("child",function(a,b,c,d,e){return(x?ub:vb).call(null,a,b,ba(c)?c:null,ba(d)?d:null,e||new C)},!1,!0);M("descendant",ob,!1,!0);
|
||||
var dc=M("descendant-or-self",function(a,b,c,d){var e=new C;nb(b,c,d)&&a.a(b)&&e.add(b);return ob(a,b,c,d,e)},!1,!0),Zb=M("following",function(a,b,c,d){var e=new C;do for(var f=b;f=f.nextSibling;)nb(f,c,d)&&a.a(f)&&e.add(f),e=ob(a,f,c,d,e);while(b=b.parentNode);return e},!1,!0);M("following-sibling",function(a,b){for(var c=new C;b=b.nextSibling;)a.a(b)&&c.add(b);return c},!1);M("namespace",function(){return new C},!1);
|
||||
var gc=M("parent",function(a,b){var c=new C;if(9==b.nodeType)return c;if(2==b.nodeType)return c.add(b.ownerElement),c;b=b.parentNode;a.a(b)&&c.add(b);return c},!1),$b=M("preceding",function(a,b,c,d){var e=new C,f=[];do f.unshift(b);while(b=b.parentNode);for(var g=1,h=f.length;g<h;g++){var n=[];for(b=f[g];b=b.previousSibling;)n.unshift(b);for(var u=0,p=n.length;u<p;u++)b=n[u],nb(b,c,d)&&a.a(b)&&e.add(b),e=ob(a,b,c,d,e)}return e},!0,!0);
|
||||
M("preceding-sibling",function(a,b){for(var c=new C;b=b.previousSibling;)a.a(b)&&yb(c,b);return c},!0);var hc=M("self",function(a,b){var c=new C;a.a(b)&&c.add(b);return c},!1);function ic(a){F.call(this,1);this.c=a;this.g=a.g;this.b=a.b}k(ic,F);ic.prototype.a=function(a){return-I(this.c,a)};ic.prototype.toString=function(){return"Unary Expression: -"+H(this.c)};function jc(a){F.call(this,4);this.c=a;Eb(this,sa(this.c,function(b){return b.g}));Fb(this,sa(this.c,function(b){return b.b}))}k(jc,F);jc.prototype.a=function(a){var b=new C;l(this.c,function(c){c=c.a(a);if(!(c instanceof C))throw Error("Path expression must evaluate to NodeSet.");b=xb(b,c)});return b};jc.prototype.toString=function(){return ra(this.c,function(a,b){return a+H(b)},"Union Expression:")};function kc(a,b){this.a=a;this.b=b}function lc(a){for(var b,c=[];;){N(a,"Missing right hand side of binary expression.");b=mc(a);var d=z(a.a);if(!d)break;var e=(d=Lb[d]||null)&&d.M;if(!e){a.a.a--;break}for(;c.length&&e<=c[c.length-1].M;)b=new Hb(c.pop(),c.pop(),b);c.push(b,d)}for(;c.length;)b=new Hb(c.pop(),c.pop(),b);return b}function N(a,b){if(bb(a.a))throw Error(b);}function nc(a,b){a=z(a.a);if(a!=b)throw Error("Bad token, expected: "+b+" got: "+a);}
|
||||
function oc(a){a=z(a.a);if(")"!=a)throw Error("Bad token: "+a);}function rc(a){a=z(a.a);if(2>a.length)throw Error("Unclosed literal string");return new Sb(a)}
|
||||
function sc(a){var b=[];if(Yb(y(a.a))){var c=z(a.a);var d=y(a.a);if("/"==c&&(bb(a.a)||"."!=d&&".."!=d&&"@"!=d&&"*"!=d&&!/(?![0-9])[\w]/.test(d)))return new Wb;d=new Wb;N(a,"Missing next location step.");c=tc(a,c);b.push(c)}else{a:{c=y(a.a);d=c.charAt(0);switch(d){case "$":throw Error("Variable reference not allowed in HTML XPath");case "(":z(a.a);c=lc(a);N(a,'unclosed "("');nc(a,")");break;case '"':case "'":c=rc(a);break;default:if(isNaN(+c))if(!Rb(c)&&/(?![0-9])[\w]/.test(d)&&"("==y(a.a,1)){c=z(a.a);
|
||||
c=Qb[c]||null;z(a.a);for(d=[];")"!=y(a.a);){N(a,"Missing function argument list.");d.push(lc(a));if(","!=y(a.a))break;z(a.a)}N(a,"Unclosed function argument list.");oc(a);c=new Ob(c,d)}else{c=null;break a}else c=new Tb(+z(a.a))}"["==y(a.a)&&(d=new ac(uc(a)),c=new Mb(c,d))}if(c)if(Yb(y(a.a)))d=c;else return c;else c=tc(a,"/"),d=new Xb,b.push(c)}for(;Yb(y(a.a));)c=z(a.a),N(a,"Missing next location step."),c=tc(a,c),b.push(c);return new Ub(d,b)}
|
||||
function tc(a,b){if("/"!=b&&"//"!=b)throw Error('Step op should be "/" or "//"');if("."==y(a.a)){var c=new bc(hc,new D("node"));z(a.a);return c}if(".."==y(a.a))return c=new bc(gc,new D("node")),z(a.a),c;if("@"==y(a.a)){var d=Vb;z(a.a);N(a,"Missing attribute name")}else if("::"==y(a.a,1)){if(!/(?![0-9])[\w]/.test(y(a.a).charAt(0)))throw Error("Bad token: "+z(a.a));var e=z(a.a);d=fc[e]||null;if(!d)throw Error("No axis with name: "+e);z(a.a);N(a,"Missing node name")}else d=cc;e=y(a.a);if(/(?![0-9])[\w\*]/.test(e.charAt(0)))if("("==
|
||||
y(a.a,1)){if(!Rb(e))throw Error("Invalid node type: "+e);e=z(a.a);if(!Rb(e))throw Error("Invalid type name: "+e);nc(a,"(");N(a,"Bad nodetype");var f=y(a.a).charAt(0),g=null;if('"'==f||"'"==f)g=rc(a);N(a,"Bad nodetype");oc(a);e=new D(e,g)}else if(e=z(a.a),f=e.indexOf(":"),-1==f)e=new rb(e);else{g=e.substring(0,f);if("*"==g)var h="*";else if(h=a.b(g),!h)throw Error("Namespace prefix not declared: "+g);e=e.substr(f+1);e=new rb(e,h)}else throw Error("Bad token: "+z(a.a));a=new ac(uc(a),d.A);return c||
|
||||
new bc(d,e,a,"//"==b)}function uc(a){for(var b=[];"["==y(a.a);){z(a.a);N(a,"Missing predicate expression.");var c=lc(a);b.push(c);N(a,"Unclosed predicate expression.");nc(a,"]")}return b}function mc(a){if("-"==y(a.a))return z(a.a),new ic(mc(a));var b=sc(a);if("|"!=y(a.a))a=b;else{for(b=[b];"|"==z(a.a);)N(a,"Missing next union location path."),b.push(sc(a));a.a.a--;a=new jc(b)}return a};function vc(a){switch(a.nodeType){case 1:return la(wc,a);case 9:return vc(a.documentElement);case 11:case 10:case 6:case 12:return xc;default:return a.parentNode?vc(a.parentNode):xc}}function xc(){return null}function wc(a,b){if(a.prefix==b)return a.namespaceURI||"http://www.w3.org/1999/xhtml";var c=a.getAttributeNode("xmlns:"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?wc(a.parentNode,b):null};function yc(a,b){if(!a.length)throw Error("Empty XPath expression.");a=Za(a);if(bb(a))throw Error("Invalid XPath expression.");b?ea(b)||(b=ka(b.lookupNamespaceURI,b)):b=function(){return null};var c=lc(new kc(a,b));if(!bb(a))throw Error("Bad token: "+z(a));this.evaluate=function(d,e){d=c.a(new ma(d));return new O(d,e)}}
|
||||
function O(a,b){if(0==b)if(a instanceof C)b=4;else if("string"==typeof a)b=2;else if("number"==typeof a)b=1;else if("boolean"==typeof a)b=3;else throw Error("Unexpected evaluation result.");if(2!=b&&1!=b&&3!=b&&!(a instanceof C))throw Error("value could not be converted to the specified type");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof C?Bb(a):""+a;break;case 1:this.numberValue=a instanceof C?+Bb(a):+a;break;case 3:this.booleanValue=a instanceof C?0<a.m:!!a;break;case 4:case 5:case 6:case 7:var c=
|
||||
Cb(a);var d=[];for(var e=E(c);e;e=E(c))d.push(e instanceof Wa?e.a:e);this.snapshotLength=a.m;this.invalidIteratorState=!1;break;case 8:case 9:a=zb(a);this.singleNodeValue=a instanceof Wa?a.a:a;break;default:throw Error("Unknown XPathResult type.");}var f=0;this.iterateNext=function(){if(4!=b&&5!=b)throw Error("iterateNext called with wrong result type");return f>=d.length?null:d[f++]};this.snapshotItem=function(g){if(6!=b&&7!=b)throw Error("snapshotItem called with wrong result type");return g>=d.length||
|
||||
0>g?null:d[g]}}O.ANY_TYPE=0;O.NUMBER_TYPE=1;O.STRING_TYPE=2;O.BOOLEAN_TYPE=3;O.UNORDERED_NODE_ITERATOR_TYPE=4;O.ORDERED_NODE_ITERATOR_TYPE=5;O.UNORDERED_NODE_SNAPSHOT_TYPE=6;O.ORDERED_NODE_SNAPSHOT_TYPE=7;O.ANY_UNORDERED_NODE_TYPE=8;O.FIRST_ORDERED_NODE_TYPE=9;function zc(a){this.lookupNamespaceURI=vc(a)}
|
||||
function Ac(a,b){a=a||aa;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=O,c.evaluate=function(d,e,f,g){return(new yc(d,f)).evaluate(e,g)},c.createExpression=function(d,e){return new yc(d,e)},c.createNSResolver=function(d){return new zc(d)}}ca("wgxpath.install",Ac);ca("wgxpath.install",Ac);var Bc=window;function P(a,b){this.code=a;this.a=Q[a]||Cc;this.message=b||"";a=this.a.replace(/((?:^|\s+)[a-z])/g,function(c){return c.toUpperCase().replace(/^[\s\xa0]+/g,"")});b=a.length-5;if(0>b||a.indexOf("Error",b)!=b)a+="Error";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||""}k(P,Error);var Cc="unknown error",Q={15:"element not selectable",11:"element not visible"};Q[31]=Cc;Q[30]=Cc;Q[24]="invalid cookie domain";Q[29]="invalid element coordinates";Q[12]="invalid element state";
|
||||
Q[32]="invalid selector";Q[51]="invalid selector";Q[52]="invalid selector";Q[17]="javascript error";Q[405]="unsupported operation";Q[34]="move target out of bounds";Q[27]="no such alert";Q[7]="no such element";Q[8]="no such frame";Q[23]="no such window";Q[28]="script timeout";Q[33]="session not created";Q[10]="stale element reference";Q[21]="timeout";Q[25]="unable to set cookie";Q[26]="unexpected alert open";Q[13]=Cc;Q[9]="unknown command";var Dc={C:function(a){return!(!a.querySelectorAll||!a.querySelector)},o:function(a,b){if(!a)throw new P(32,"No class name specified");a=Aa(a);if(-1!==a.indexOf(" "))throw new P(32,"Compound class names not permitted");if(Dc.C(b))try{return b.querySelector("."+a.replace(/\./g,"\\."))||null}catch(c){throw new P(32,"An invalid or illegal class name was specified");}a=mb(eb(b),"*",a,b);return a.length?a[0]:null},j:function(a,b){if(!a)throw new P(32,"No class name specified");a=Aa(a);if(-1!==a.indexOf(" "))throw new P(32,
|
||||
"Compound class names not permitted");if(Dc.C(b))try{return b.querySelectorAll("."+a.replace(/\./g,"\\."))}catch(c){throw new P(32,"An invalid or illegal class name was specified");}return mb(eb(b),"*",a,b)}};var Ec=Fa(),Fc=Ia()||r("iPod"),Gc=r("iPad"),Hc=r("Android")&&!(Ga()||Fa()||r("Opera")||r("Silk")),Ic=Ga(),Jc=r("Safari")&&!(Ga()||r("Coast")||r("Opera")||r("Edge")||r("Edg/")||r("OPR")||Fa()||r("Silk")||r("Android"))&&!(Ia()||r("iPad")||r("iPod"));function Kc(a){return(a=a.exec(q))?a[1]:""}(function(){if(Ec)return Kc(/Firefox\/([0-9.]+)/);if(t||Ma||La)return Qa;if(Ic)return Ia()||r("iPad")||r("iPod")?Kc(/CriOS\/([0-9.]+)/):Kc(/Chrome\/([0-9.]+)/);if(Jc&&!(Ia()||r("iPad")||r("iPod")))return Kc(/Version\/([0-9.]+)/);if(Fc||Gc){var a=/Version\/(\S+).*Mobile\/(\S+)/.exec(q);if(a)return a[1]+"."+a[2]}else if(Hc)return(a=Kc(/Android\s+([0-9.]+)/))?a:Kc(/Version\/([0-9.]+)/);return""})();var Lc=t&&!(8<=Number(w)),Mc=t&&!(9<=Number(w));var Nc={o:function(a,b){if(!ea(b.querySelector)&&t&&(t?0<=Ba(w,8):Ua(8))&&!ha(b.querySelector))throw Error("CSS selection is not supported");if(!a)throw new P(32,"No selector specified");a=Aa(a);try{var c=b.querySelector(a)}catch(d){throw new P(32,"An invalid or illegal selector was specified");}return c&&1==c.nodeType?c:null},j:function(a,b){if(!ea(b.querySelectorAll)&&t&&(t?0<=Ba(w,8):Ua(8))&&!ha(b.querySelector))throw Error("CSS selection is not supported");if(!a)throw new P(32,"No selector specified");
|
||||
a=Aa(a);try{return b.querySelectorAll(a)}catch(c){throw new P(32,"An invalid or illegal selector was specified");}}};var Oc={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",
|
||||
darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",
|
||||
ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",
|
||||
lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",
|
||||
moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",
|
||||
seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"};var Pc="backgroundColor borderTopColor borderRightColor borderBottomColor borderLeftColor color outlineColor".split(" "),Qc=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/,Rc=/^#(?:[0-9a-f]{3}){1,2}$/i,Sc=/^(?:rgba)?\((\d{1,3}),\s?(\d{1,3}),\s?(\d{1,3}),\s?(0|1|0\.\d*)\)$/i,Tc=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;function Uc(a,b){b=b.toLowerCase();return"style"==b?Vc(a.style.cssText):Lc&&"value"==b&&S(a,"INPUT")?a.value:Mc&&!0===a[b]?String(a.getAttribute(b)):(a=a.getAttributeNode(b))&&a.specified?a.value:null}var Wc=/[;]+(?=(?:(?:[^"]*"){2})*[^"]*$)(?=(?:(?:[^']*'){2})*[^']*$)(?=(?:[^()]*\([^()]*\))*[^()]*$)/;
|
||||
function Vc(a){var b=[];l(a.split(Wc),function(c){var d=c.indexOf(":");0<d&&(c=[c.slice(0,d),c.slice(d+1)],2==c.length&&b.push(c[0].toLowerCase(),":",c[1],";"))});b=b.join("");return b=";"==b.charAt(b.length-1)?b:b+";"}function S(a,b){b&&"string"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)};var T={};T.I=function(){var a={da:"http://www.w3.org/2000/svg"};return function(b){return a[b]||null}}();
|
||||
T.s=function(a,b,c){var d=A(a);if(!d.documentElement)return null;(t||Hc)&&Ac(d?d.parentWindow||d.defaultView:window);try{var e=d.createNSResolver?d.createNSResolver(d.documentElement):T.I;if(t&&!Ua(7))return d.evaluate.call(d,b,a,e,c,null);if(!t||9<=Number(w)){for(var f={},g=d.getElementsByTagName("*"),h=0;h<g.length;++h){var n=g[h],u=n.namespaceURI;if(u&&!f[u]){var p=n.lookupPrefix(u);if(!p){var G=u.match(".*/(\\w+)/?$");p=G?G[1]:"xhtml"}f[u]=p}}var R={},fa;for(fa in f)R[f[fa]]=fa;e=function(m){return R[m]||
|
||||
null}}try{return d.evaluate(b,a,e,c,null)}catch(m){if("TypeError"===m.name)return e=d.createNSResolver?d.createNSResolver(d.documentElement):T.I,d.evaluate(b,a,e,c,null);throw m;}}catch(m){if(!Na||"NS_ERROR_ILLEGAL_VALUE"!=m.name)throw new P(32,"Unable to locate an element with the xpath expression "+b+" because of the following error:\n"+m);}};T.J=function(a,b){if(!a||1!=a.nodeType)throw new P(32,'The result of the xpath expression "'+b+'" is: '+a+". It should be an element.");};
|
||||
T.o=function(a,b){var c=function(){var d=T.s(b,a,9);return d?d.singleNodeValue||null:b.selectSingleNode?(d=A(b),d.setProperty&&d.setProperty("SelectionLanguage","XPath"),b.selectSingleNode(a)):null}();null===c||T.J(c,a);return c};
|
||||
T.j=function(a,b){var c=function(){var d=T.s(b,a,7);if(d){for(var e=d.snapshotLength,f=[],g=0;g<e;++g)f.push(d.snapshotItem(g));return f}return b.selectNodes?(d=A(b),d.setProperty&&d.setProperty("SelectionLanguage","XPath"),b.selectNodes(a)):[]}();l(c,function(d){T.J(d,a)});return c};function Xc(a,b,c,d){this.c=a;this.a=b;this.b=c;this.f=d}Xc.prototype.ceil=function(){this.c=Math.ceil(this.c);this.a=Math.ceil(this.a);this.b=Math.ceil(this.b);this.f=Math.ceil(this.f);return this};Xc.prototype.floor=function(){this.c=Math.floor(this.c);this.a=Math.floor(this.a);this.b=Math.floor(this.b);this.f=Math.floor(this.f);return this};Xc.prototype.round=function(){this.c=Math.round(this.c);this.a=Math.round(this.a);this.b=Math.round(this.b);this.f=Math.round(this.f);return this};function U(a,b,c,d){this.a=a;this.b=b;this.width=c;this.height=d}U.prototype.ceil=function(){this.a=Math.ceil(this.a);this.b=Math.ceil(this.b);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};U.prototype.floor=function(){this.a=Math.floor(this.a);this.b=Math.floor(this.b);this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};
|
||||
U.prototype.round=function(){this.a=Math.round(this.a);this.b=Math.round(this.b);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};var Yc="function"===typeof ShadowRoot;function Zc(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return S(a)?a:null}
|
||||
function V(a,b){b=Ha(b);if("float"==b||"cssFloat"==b||"styleFloat"==b)b=Mc?"styleFloat":"cssFloat";a:{var c=b;var d=A(a);if(d.defaultView&&d.defaultView.getComputedStyle&&(d=d.defaultView.getComputedStyle(a,null))){c=d[c]||d.getPropertyValue(c)||"";break a}c=""}a=c||$c(a,b);if(null===a)a=null;else if(0<=oa(Pc,b)){b:{var e=a.match(Sc);if(e&&(b=Number(e[1]),c=Number(e[2]),d=Number(e[3]),e=Number(e[4]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d&&0<=e&&1>=e)){b=[b,c,d,e];break b}b=null}if(!b)b:{if(d=a.match(Tc))if(b=
|
||||
Number(d[1]),c=Number(d[2]),d=Number(d[3]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d){b=[b,c,d,1];break b}b=null}if(!b)b:{b=a.toLowerCase();c=Oc[b.toLowerCase()];if(!c&&(c="#"==b.charAt(0)?b:"#"+b,4==c.length&&(c=c.replace(Qc,"#$1$1$2$2$3$3")),!Rc.test(c))){b=null;break b}b=[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16),1]}a=b?"rgba("+b.join(", ")+")":a}return a}
|
||||
function $c(a,b){var c=a.currentStyle||a.style,d=c[b];void 0===d&&ea(c.getPropertyValue)&&(d=c.getPropertyValue(b));return"inherit"!=d?void 0!==d?d:null:(a=Zc(a))?$c(a,b):null}
|
||||
function ad(a,b,c){function d(g){var h=W(g);return 0<h.height&&0<h.width?!0:S(g,"PATH")&&(0<h.height||0<h.width)?(g=V(g,"stroke-width"),!!g&&0<parseInt(g,10)):"hidden"!=V(g,"overflow")&&sa(g.childNodes,function(n){return 3==n.nodeType||S(n)&&d(n)})}function e(g){return bd(g)==X&&ta(g.childNodes,function(h){return!S(h)||e(h)||!d(h)})}if(!S(a))throw Error("Argument to isShown must be of type Element");if(S(a,"BODY"))return!0;if(S(a,"OPTION")||S(a,"OPTGROUP"))return a=lb(a,function(g){return S(g,"SELECT")}),
|
||||
!!a&&ad(a,!0,c);var f=cd(a);if(f)return!!f.image&&0<f.rect.width&&0<f.rect.height&&ad(f.image,b,c);if(S(a,"INPUT")&&"hidden"==a.type.toLowerCase()||S(a,"NOSCRIPT"))return!1;f=V(a,"visibility");return"collapse"!=f&&"hidden"!=f&&c(a)&&(b||0!=dd(a))&&d(a)?!e(a):!1}
|
||||
function ed(a){function b(c){if(S(c)&&"none"==V(c,"display"))return!1;var d;if((d=c.parentNode)&&d.shadowRoot&&void 0!==c.assignedSlot)d=c.assignedSlot?c.assignedSlot.parentNode:null;else if(c.getDestinationInsertionPoints){var e=c.getDestinationInsertionPoints();0<e.length&&(d=e[e.length-1])}if(Yc&&d instanceof ShadowRoot){if(d.host.shadowRoot!==d)return!1;d=d.host}return!d||9!=d.nodeType&&11!=d.nodeType?d&&S(d,"DETAILS")&&!d.open&&!S(c,"SUMMARY")?!1:!!d&&b(d):!0}return ad(a,!1,b)}var X="hidden";
|
||||
function bd(a){function b(m){function v(Ab){if(Ab==g)return!0;var pc=V(Ab,"display");return 0==pc.lastIndexOf("inline",0)||"contents"==pc||"absolute"==qc&&"static"==V(Ab,"position")?!1:!0}var qc=V(m,"position");if("fixed"==qc)return u=!0,m==g?null:g;for(m=Zc(m);m&&!v(m);)m=Zc(m);return m}function c(m){var v=m;if("visible"==n)if(m==g&&h)v=h;else if(m==h)return{x:"visible",y:"visible"};v={x:V(v,"overflow-x"),y:V(v,"overflow-y")};m==g&&(v.x="visible"==v.x?"auto":v.x,v.y="visible"==v.y?"auto":v.y);return v}
|
||||
function d(m){if(m==g){var v=(new fb(f)).a;m=v.scrollingElement?v.scrollingElement:Oa||"CSS1Compat"!=v.compatMode?v.body||v.documentElement:v.documentElement;v=v.parentWindow||v.defaultView;m=t&&Ua("10")&&v.pageYOffset!=m.scrollTop?new cb(m.scrollLeft,m.scrollTop):new cb(v.pageXOffset||m.scrollLeft,v.pageYOffset||m.scrollTop)}else m=new cb(m.scrollLeft,m.scrollTop);return m}var e=fd(a),f=A(a),g=f.documentElement,h=f.body,n=V(g,"overflow"),u;for(a=b(a);a;a=b(a)){var p=c(a);if("visible"!=p.x||"visible"!=
|
||||
p.y){var G=W(a);if(0==G.width||0==G.height)return X;var R=e.a<G.a,fa=e.b<G.b;if(R&&"hidden"==p.x||fa&&"hidden"==p.y)return X;if(R&&"visible"!=p.x||fa&&"visible"!=p.y){R=d(a);fa=e.b<G.b-R.y;if(e.a<G.a-R.x&&"visible"!=p.x||fa&&"visible"!=p.x)return X;e=bd(a);return e==X?X:"scroll"}R=e.f>=G.a+G.width;G=e.c>=G.b+G.height;if(R&&"hidden"==p.x||G&&"hidden"==p.y)return X;if(R&&"visible"!=p.x||G&&"visible"!=p.y){if(u&&(p=d(a),e.f>=g.scrollWidth-p.x||e.a>=g.scrollHeight-p.y))return X;e=bd(a);return e==X?X:
|
||||
"scroll"}}}return"none"}
|
||||
function W(a){var b=cd(a);if(b)return b.rect;if(S(a,"HTML"))return a=A(a),a=((a?a.parentWindow||a.defaultView:window)||window).document,a="CSS1Compat"==a.compatMode?a.documentElement:a.body,a=new db(a.clientWidth,a.clientHeight),new U(0,0,a.width,a.height);try{var c=a.getBoundingClientRect()}catch(d){return new U(0,0,0,0)}b=new U(c.left,c.top,c.right-c.left,c.bottom-c.top);t&&a.ownerDocument.body&&(a=A(a),b.a-=a.documentElement.clientLeft+a.body.clientLeft,b.b-=a.documentElement.clientTop+a.body.clientTop);
|
||||
return b}function cd(a){var b=S(a,"MAP");if(!b&&!S(a,"AREA"))return null;var c=b?a:S(a.parentNode,"MAP")?a.parentNode:null,d=null,e=null;c&&c.name&&(d=T.o('/descendant::*[@usemap = "#'+c.name+'"]',A(c)))&&(e=W(d),b||"default"==a.shape.toLowerCase()||(a=gd(a),b=Math.min(Math.max(a.a,0),e.width),c=Math.min(Math.max(a.b,0),e.height),e=new U(b+e.a,c+e.b,Math.min(a.width,e.width-b),Math.min(a.height,e.height-c))));return{image:d,rect:e||new U(0,0,0,0)}}
|
||||
function gd(a){var b=a.shape.toLowerCase();a=a.coords.split(",");if("rect"==b&&4==a.length){b=a[0];var c=a[1];return new U(b,c,a[2]-b,a[3]-c)}if("circle"==b&&3==a.length)return b=a[2],new U(a[0]-b,a[1]-b,2*b,2*b);if("poly"==b&&2<a.length){b=a[0];c=a[1];for(var d=b,e=c,f=2;f+1<a.length;f+=2)b=Math.min(b,a[f]),d=Math.max(d,a[f]),c=Math.min(c,a[f+1]),e=Math.max(e,a[f+1]);return new U(b,c,d-b,e-c)}return new U(0,0,0,0)}function fd(a){a=W(a);return new Xc(a.b,a.a+a.width,a.b+a.height,a.a)}
|
||||
function hd(a){return a.replace(/^[^\S\xa0]+|[^\S\xa0]+$/g,"")}function id(a){var b=[];Yc?jd(a,b):kd(a,b);a=qa(b,hd);return hd(a.join("\n")).replace(/\xa0/g," ")}
|
||||
function ld(a,b,c){if(S(a,"BR"))b.push("");else{var d=S(a,"TD"),e=V(a,"display"),f=!d&&!(0<=oa(md,e)),g=void 0!==a.previousElementSibling?a.previousElementSibling:gb(a.previousSibling);g=g?V(g,"display"):"";var h=V(a,"float")||V(a,"cssFloat")||V(a,"styleFloat");!f||"run-in"==g&&"none"==h||/^[\s\xa0]*$/.test(b[b.length-1]||"")||b.push("");var n=ed(a),u=null,p=null;n&&(u=V(a,"white-space"),p=V(a,"text-transform"));l(a.childNodes,function(G){c(G,b,n,u,p)});a=b[b.length-1]||"";!d&&"table-cell"!=e||!a||
|
||||
za(a)||(b[b.length-1]+=" ");f&&"run-in"!=e&&!/^[\s\xa0]*$/.test(a)&&b.push("")}}function kd(a,b){ld(a,b,function(c,d,e,f,g){3==c.nodeType&&e?nd(c,d,f,g):S(c)&&kd(c,d)})}var md="inline inline-block inline-table none table-cell table-column table-column-group".split(" ");
|
||||
function nd(a,b,c,d){a=a.nodeValue.replace(/[\u200b\u200e\u200f]/g,"");a=a.replace(/(\r\n|\r|\n)/g,"\n");if("normal"==c||"nowrap"==c)a=a.replace(/\n/g," ");a="pre"==c||"pre-wrap"==c?a.replace(/[ \f\t\v\u2028\u2029]/g,"\u00a0"):a.replace(/[ \f\t\v\u2028\u2029]+/g," ");"capitalize"==d?a=a.replace(t?/(^|\s|\b)(\S)/g:/(^|[^\d\p{L}\p{S}])([\p{Ll}|\p{S}])/gu,function(e,f,g){return f+g.toUpperCase()}):"uppercase"==d?a=a.toUpperCase():"lowercase"==d&&(a=a.toLowerCase());c=b.pop()||"";za(c)&&0==a.lastIndexOf(" ",
|
||||
0)&&(a=a.substr(1));b.push(c+a)}function dd(a){if(Mc){if("relative"==V(a,"position"))return 1;a=V(a,"filter");return(a=a.match(/^alpha\(opacity=(\d*)\)/)||a.match(/^progid:DXImageTransform.Microsoft.Alpha\(Opacity=(\d*)\)/))?Number(a[1])/100:1}return od(a)}function od(a){var b=1,c=V(a,"opacity");c&&(b=Number(c));(a=Zc(a))&&(b*=od(a));return b}
|
||||
function pd(a,b,c,d,e){if(3==a.nodeType&&c)nd(a,b,d,e);else if(S(a))if(S(a,"CONTENT")||S(a,"SLOT")){for(var f=a;f.parentNode;)f=f.parentNode;f instanceof ShadowRoot?(a=S(a,"CONTENT")?a.getDistributedNodes():a.assignedNodes(),l(a,function(g){pd(g,b,c,d,e)})):jd(a,b)}else if(S(a,"SHADOW")){for(f=a;f.parentNode;)f=f.parentNode;if(f instanceof ShadowRoot&&(a=f))for(a=a.olderShadowRoot;a;)l(a.childNodes,function(g){pd(g,b,c,d,e)}),a=a.olderShadowRoot}else jd(a,b)}
|
||||
function jd(a,b){a.shadowRoot&&l(a.shadowRoot.childNodes,function(c){pd(c,b,!0,null,null)});ld(a,b,function(c,d,e,f,g){var h=null;1==c.nodeType?h=c:3==c.nodeType&&(h=c);null!=h&&(null!=h.assignedSlot||h.getDestinationInsertionPoints&&0<h.getDestinationInsertionPoints().length)||pd(c,d,e,f,g)})};var qd={C:function(a,b){return!(!a.querySelectorAll||!a.querySelector)&&!/^\d.*/.test(b)},o:function(a,b){var c=eb(b),d="string"===typeof a?c.a.getElementById(a):a;return d?Uc(d,"id")==a&&b!=d&&hb(b,d)?d:ua(mb(c,"*"),function(e){return Uc(e,"id")==a&&b!=e&&hb(b,e)}):null},j:function(a,b){if(!a)return[];if(qd.C(b,a))try{return b.querySelectorAll("#"+qd.T(a))}catch(c){return[]}b=mb(eb(b),"*",null,b);return pa(b,function(c){return Uc(c,"id")==a})},T:function(a){return a.replace(/([\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-\/\[\]\(\)])/g,
|
||||
"\\$1")}};var Y={},rd={};Y.N=function(a,b,c){try{var d=Nc.j("a",b)}catch(e){d=mb(eb(b),"A",null,b)}return ua(d,function(e){e=id(e);e=e.replace(/^[\s]+|[\s]+$/g,"");return c&&-1!=e.indexOf(a)||e==a})};Y.K=function(a,b,c){try{var d=Nc.j("a",b)}catch(e){d=mb(eb(b),"A",null,b)}return pa(d,function(e){e=id(e);e=e.replace(/^[\s]+|[\s]+$/g,"");return c&&-1!=e.indexOf(a)||e==a})};Y.o=function(a,b){return Y.N(a,b,!1)};Y.j=function(a,b){return Y.K(a,b,!1)};rd.o=function(a,b){return Y.N(a,b,!0)};
|
||||
rd.j=function(a,b){return Y.K(a,b,!0)};var Z={F:function(a,b){return function(c){var d=Z.u(a);d=W(d);c=W(c);return b.call(null,d,c)}},R:function(a){return Z.F(a,function(b,c){return c.b+c.height<b.b})},S:function(a){return Z.F(a,function(b,c){return b.b+b.height<c.b})},V:function(a){return Z.F(a,function(b,c){return c.a+c.width<b.a})},aa:function(a){return Z.F(a,function(b,c){return b.a+b.width<c.a})},W:function(a,b){var c;b?c=b:"number"==typeof a.distance&&(c=a.distance);c||(c=100);return function(d){var e=Z.u(a);if(e===d)return!1;e=
|
||||
W(e);d=W(d);var f=Math.abs(e.a+e.width-d.a),g=Math.abs(e.b+e.height-d.b);g=Math.abs(e.b-(d.b+d.height))<=c||g<=c;return(Math.abs(e.a-(d.a+d.width))<=c||f<=c)&&g?!0:Math.sqrt(Math.pow(Math.abs(e.a+e.width/2-(d.a+d.width/2)),2)+Math.pow(Math.abs(e.b+e.height/2-(d.b+d.height/2)),2))<=c}},u:function(a){if(ha(a)&&1==a.nodeType)return a;if(ea(a))return Z.u(a.call(null));if(ha(a)){var b;a:{if(b=sd(a)){var c=td[b];if(c&&ea(c.o)){b=c.o(a[b],Bc.document);break a}}throw new P(61,"Unsupported locator strategy: "+
|
||||
b);}if(!b)throw new P(7,"No element has been found by "+JSON.stringify(a));return b}throw new P(61,"Selector is of wrong type: "+JSON.stringify(a));}};Z.P={left:Z.V,right:Z.aa,above:Z.R,below:Z.S,near:Z.W};Z.O={left:Z.u,right:Z.u,above:Z.u,below:Z.u,near:Z.u};
|
||||
Z.U=function(a,b){var c=[];l(a,function(e){e&&ta(b,function(f){var g=f.kind,h=Z.P[g];if(!h)throw new P(61,"Cannot find filter suitable for "+g);return h.apply(null,f.args)(e)},null)&&c.push(e)},null);a=b[b.length-1];var d=Z.O[a?a.kind:"unknown"];return d?(a=d.apply(null,a.args))?Z.ba(a,c):c:c};
|
||||
Z.ba=function(a,b){function c(f){f=W(f);return Math.sqrt(Math.pow(d-(f.a+Math.max(1,f.width)/2),2)+Math.pow(e-(f.b+Math.max(1,f.height)/2),2))}a=W(a);var d=a.a+Math.max(1,a.width)/2,e=a.b+Math.max(1,a.height)/2;xa(b,function(f,g){return c(f)-c(g)});return b};Z.o=function(a,b){a=Z.j(a,b);return 0==a.length?null:a[0]};
|
||||
Z.j=function(a,b){if(!a.hasOwnProperty("root")||!a.hasOwnProperty("filters"))throw new P(61,"Locator not suitable for relative locators: "+JSON.stringify(a));var c=a.filters,d=da(c);if("array"!=d&&("object"!=d||"number"!=typeof c.length))throw new P(61,"Targets should be an array: "+JSON.stringify(a));var e;S(a.root)?e=[a.root]:e=ud(a.root,b);return 0==e.length?[]:Z.U(e,a.filters)};var vd={o:function(a,b){if(""===a)throw new P(32,'Unable to locate an element with the tagName ""');return b.getElementsByTagName(a)[0]||null},j:function(a,b){if(""===a)throw new P(32,'Unable to locate an element with the tagName ""');return b.getElementsByTagName(a)}};var td={className:Dc,"class name":Dc,css:Nc,"css selector":Nc,relative:Z,id:qd,linkText:Y,"link text":Y,name:{o:function(a,b){b=mb(eb(b),"*",null,b);return ua(b,function(c){return Uc(c,"name")==a})},j:function(a,b){b=mb(eb(b),"*",null,b);return pa(b,function(c){return Uc(c,"name")==a})}},partialLinkText:rd,"partial link text":rd,tagName:vd,"tag name":vd,xpath:T};function sd(a){for(var b in a)if(a.hasOwnProperty(b))return b;return null}
|
||||
function ud(a,b){var c=sd(a);if(c){var d=td[c];if(d&&ea(d.j))return d.j(a[c],b||Bc.document)}throw new P(61,"Unsupported locator strategy: "+c);};ca("_",ud);; return this._.apply(null,arguments);}).apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);};
|
||||
101
node_modules/selenium-webdriver/lib/atoms/get-attribute.js
generated
vendored
Normal file
101
node_modules/selenium-webdriver/lib/atoms/get-attribute.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// GENERATED CODE - DO NOT EDIT
|
||||
module.exports = function(){return (function(){var h=this||self;function aa(a){return"string"==typeof a}function ba(a,b){a=a.split(".");var c=h;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b}
|
||||
function ca(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
|
||||
else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function da(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function ea(a,b,c){return a.call.apply(a.bind,arguments)}function fa(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var e=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(e,d);return a.apply(b,e)}}return function(){return a.apply(b,arguments)}}
|
||||
function ha(a,b,c){Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ha=ea:ha=fa;return ha.apply(null,arguments)}function ia(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var d=c.slice();d.push.apply(d,arguments);return a.apply(this,d)}}function m(a,b){function c(){}c.prototype=b.prototype;a.H=b.prototype;a.prototype=new c;a.prototype.constructor=a}
|
||||
function ja(a,b,c){var d=arguments.callee.caller;if("undefined"!==typeof d.H){for(var e=Array(arguments.length-1),f=1;f<arguments.length;f++)e[f-1]=arguments[f];d.H.constructor.apply(a,e)}else{if("string"!=typeof b&&"symbol"!=typeof b)throw Error("method names provided to goog.base must be a string or a symbol");e=Array(arguments.length-2);for(f=2;f<arguments.length;f++)e[f-2]=arguments[f];f=!1;for(var g=a.constructor.prototype;g;g=Object.getPrototypeOf(g))if(g[b]===d)f=!0;else if(f){g[b].apply(a,
|
||||
e);return}if(a[b]===d)a.constructor.prototype[b].apply(a,e);else throw Error("goog.base called from a method of one name to a method of a different name");}};/*
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2007 Cybozu Labs, Inc.
|
||||
Copyright (c) 2012 Google Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
function ka(a,b,c){this.a=a;this.b=b||1;this.f=c||1};var la;var ma=Array.prototype.indexOf?function(a,b){return Array.prototype.indexOf.call(a,b,void 0)}:function(a,b){if("string"===typeof a)return"string"!==typeof b||1!=b.length?-1:a.indexOf(b,0);for(var c=0;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},n=Array.prototype.forEach?function(a,b){Array.prototype.forEach.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)e in d&&b.call(void 0,d[e],e,a)},na=Array.prototype.filter?function(a,b){return Array.prototype.filter.call(a,
|
||||
b,void 0)}:function(a,b){for(var c=a.length,d=[],e=0,f="string"===typeof a?a.split(""):a,g=0;g<c;g++)if(g in f){var k=f[g];b.call(void 0,k,g,a)&&(d[e++]=k)}return d},oa=Array.prototype.map?function(a,b){return Array.prototype.map.call(a,b,void 0)}:function(a,b){for(var c=a.length,d=Array(c),e="string"===typeof a?a.split(""):a,f=0;f<c;f++)f in e&&(d[f]=b.call(void 0,e[f],f,a));return d},pa=Array.prototype.reduce?function(a,b,c){return Array.prototype.reduce.call(a,b,c)}:function(a,b,c){var d=c;n(a,
|
||||
function(e,f){d=b.call(void 0,d,e,f,a)});return d},qa=Array.prototype.some?function(a,b){return Array.prototype.some.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a))return!0;return!1};function ra(a,b){a:{for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:"string"===typeof a?a.charAt(b):a[b]}
|
||||
function sa(a){return Array.prototype.concat.apply([],arguments)}function ta(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var ua=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\s\xa0]*([\s\S]*?)[\s\xa0]*$/.exec(a)[1]};
|
||||
function va(a,b){var c=0;a=ua(String(a)).split(".");b=ua(String(b)).split(".");for(var d=Math.max(a.length,b.length),e=0;0==c&&e<d;e++){var f=a[e]||"",g=b[e]||"";do{f=/(\d*)(\D*)(.*)/.exec(f)||["","","",""];g=/(\d*)(\D*)(.*)/.exec(g)||["","","",""];if(0==f[0].length&&0==g[0].length)break;c=wa(0==f[1].length?0:parseInt(f[1],10),0==g[1].length?0:parseInt(g[1],10))||wa(0==f[2].length,0==g[2].length)||wa(f[2],g[2]);f=f[3];g=g[3]}while(0==c)}return c}function wa(a,b){return a<b?-1:a>b?1:0};var p;a:{var xa=h.navigator;if(xa){var ya=xa.userAgent;if(ya){p=ya;break a}}p=""}function q(a){return-1!=p.indexOf(a)};function za(){return q("Firefox")||q("FxiOS")}function Aa(){return(q("Chrome")||q("CriOS"))&&!q("Edge")};function Ba(){return q("iPhone")&&!q("iPod")&&!q("iPad")};function Ca(a,b){var c=Da;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var Ea=q("Opera"),r=q("Trident")||q("MSIE"),Fa=q("Edge"),t=q("Gecko")&&!(-1!=p.toLowerCase().indexOf("webkit")&&!q("Edge"))&&!(q("Trident")||q("MSIE"))&&!q("Edge"),Ga=-1!=p.toLowerCase().indexOf("webkit")&&!q("Edge"),Ha=q("Macintosh"),Ia=q("Windows");function Ja(){var a=h.document;return a?a.documentMode:void 0}var Ka;
|
||||
a:{var La="",Ma=function(){var a=p;if(t)return/rv:([^\);]+)(\)|;)/.exec(a);if(Fa)return/Edge\/([\d\.]+)/.exec(a);if(r)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Ga)return/WebKit\/(\S+)/.exec(a);if(Ea)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Ma&&(La=Ma?Ma[1]:"");if(r){var Na=Ja();if(null!=Na&&Na>parseFloat(La)){Ka=String(Na);break a}}Ka=La}var Da={};function Oa(a){return Ca(a,function(){return 0<=va(Ka,a)})}var w;w=h.document&&r?Ja():void 0;var x=r&&!(9<=Number(w)),Pa=r&&!(8<=Number(w));function Qa(a,b,c,d){this.a=a;this.nodeName=c;this.nodeValue=d;this.nodeType=2;this.parentNode=this.ownerElement=b}function Sa(a,b){var c=Pa&&"href"==b.nodeName?a.getAttribute(b.nodeName,2):b.nodeValue;return new Qa(b,a,b.nodeName,c)};function Ta(a){this.b=a;this.a=0}function Ua(a){a=a.match(Va);for(var b=0;b<a.length;b++)Wa.test(a[b])&&a.splice(b,1);return new Ta(a)}var Va=/\$?(?:(?![0-9-\.])(?:\*|[\w-\.]+):)?(?![0-9-\.])(?:\*|[\w-\.]+)|\/\/|\.\.|::|\d+(?:\.\d*)?|\.\d+|"[^"]*"|'[^']*'|[!<>]=|\s+|./g,Wa=/^\s/;function y(a,b){return a.b[a.a+(b||0)]}function z(a){return a.b[a.a++]}function Xa(a){return a.b.length<=a.a};function Ya(a){return a.scrollingElement?a.scrollingElement:Ga||"CSS1Compat"!=a.compatMode?a.body||a.documentElement:a.documentElement}function Za(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}
|
||||
function $a(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(r&&!(9<=Number(w))){if(9==a.nodeType)return-1;if(9==b.nodeType)return 1}if("sourceIndex"in a||a.parentNode&&"sourceIndex"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?ab(a,b):!c&&Za(e,b)?-1*bb(a,b):!d&&Za(f,a)?bb(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=A(a);c=d.createRange();
|
||||
c.selectNode(a);c.collapse(!0);a=d.createRange();a.selectNode(b);a.collapse(!0);return c.compareBoundaryPoints(h.Range.START_TO_END,a)}function bb(a,b){var c=a.parentNode;if(c==b)return-1;for(;b.parentNode!=c;)b=b.parentNode;return ab(b,a)}function ab(a,b){for(;b=b.previousSibling;)if(b==a)return-1;return 1}function A(a){return 9==a.nodeType?a:a.ownerDocument||a.document}var cb={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},db={IMG:" ",BR:"\n"};
|
||||
function eb(a,b,c){if(!(a.nodeName in cb))if(3==a.nodeType)c?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in db)b.push(db[a.nodeName]);else for(a=a.firstChild;a;)eb(a,b,c),a=a.nextSibling}function fb(a){this.a=a||h.document||document}fb.prototype.getElementsByTagName=function(a,b){return(b||this.a).getElementsByTagName(String(a))};function B(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?"":b);if("string"!=typeof b)if(x&&"title"==a.nodeName.toLowerCase()&&1==c)b=a.text;else if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;c=0;var d=[];for(b="";a;){do 1!=a.nodeType&&(b+=a.nodeValue),x&&"title"==a.nodeName.toLowerCase()&&(b+=a.text),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return b}
|
||||
function C(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}Pa&&"class"==b&&(b="className");return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function gb(a,b,c,d,e){return(x?hb:ib).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new D)}
|
||||
function hb(a,b,c,d,e){if(a instanceof jb||8==a.b||c&&null===a.b){var f=b.all;if(!f)return e;a=kb(a);if("*"!=a&&(f=b.getElementsByTagName(a),!f))return e;if(c){for(var g=[],k=0;b=f[k++];)C(b,c,d)&&g.push(b);f=g}for(k=0;b=f[k++];)"*"==a&&"!"==b.tagName||e.add(b);return e}lb(a,b,c,d,e);return e}
|
||||
function ib(a,b,c,d,e){b.getElementsByName&&d&&"name"==c&&!r?(b=b.getElementsByName(d),n(b,function(f){a.a(f)&&e.add(f)})):b.getElementsByClassName&&d&&"class"==c?(b=b.getElementsByClassName(d),n(b,function(f){f.className==d&&a.a(f)&&e.add(f)})):a instanceof E?lb(a,b,c,d,e):b.getElementsByTagName&&(b=b.getElementsByTagName(a.f()),n(b,function(f){C(f,c,d)&&e.add(f)}));return e}
|
||||
function mb(a,b,c,d,e){var f;if((a instanceof jb||8==a.b||c&&null===a.b)&&(f=b.childNodes)){var g=kb(a);if("*"!=g&&(f=na(f,function(k){return k.tagName&&k.tagName.toLowerCase()==g}),!f))return e;c&&(f=na(f,function(k){return C(k,c,d)}));n(f,function(k){"*"==g&&("!"==k.tagName||"*"==g&&1!=k.nodeType)||e.add(k)});return e}return nb(a,b,c,d,e)}function nb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b);return e}
|
||||
function lb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b),lb(a,b,c,d,e)}function kb(a){if(a instanceof E){if(8==a.b)return"!";if(null===a.b)return"*"}return a.f()};function D(){this.b=this.a=null;this.o=0}function ob(a){this.f=a;this.a=this.b=null}function pb(a,b){if(!a.a)return b;if(!b.a)return a;var c=a.a;b=b.a;for(var d=null,e,f=0;c&&b;){e=c.f;var g=b.f;e==g||e instanceof Qa&&g instanceof Qa&&e.a==g.a?(e=c,c=c.a,b=b.a):0<$a(c.f,b.f)?(e=b,b=b.a):(e=c,c=c.a);(e.b=d)?d.a=e:a.a=e;d=e;f++}for(e=c||b;e;)e.b=d,d=d.a=e,f++,e=e.a;a.b=d;a.o=f;return a}function qb(a,b){b=new ob(b);b.a=a.a;a.b?a.a.b=b:a.a=a.b=b;a.a=b;a.o++}
|
||||
D.prototype.add=function(a){a=new ob(a);a.b=this.b;this.a?this.b.a=a:this.a=this.b=a;this.b=a;this.o++};function rb(a){return(a=a.a)?a.f:null}function sb(a){return(a=rb(a))?B(a):""}function F(a,b){return new tb(a,!!b)}function tb(a,b){this.f=a;this.b=(this.v=b)?a.b:a.a;this.a=null}function G(a){var b=a.b;if(null==b)return null;var c=a.a=b;a.b=a.v?b.b:b.a;return c.f};function H(a){this.l=a;this.b=this.i=!1;this.f=null}function I(a){return"\n "+a.toString().split("\n").join("\n ")}function ub(a,b){a.i=b}function vb(a,b){a.b=b}function J(a,b){a=a.a(b);return a instanceof D?+sb(a):+a}function L(a,b){a=a.a(b);return a instanceof D?sb(a):""+a}function wb(a,b){a=a.a(b);return a instanceof D?!!a.o:!!a};function xb(a,b,c){H.call(this,a.l);this.c=a;this.j=b;this.u=c;this.i=b.i||c.i;this.b=b.b||c.b;this.c==yb&&(c.b||c.i||4==c.l||0==c.l||!b.f?b.b||b.i||4==b.l||0==b.l||!c.f||(this.f={name:c.f.name,A:b}):this.f={name:b.f.name,A:c})}m(xb,H);
|
||||
function zb(a,b,c,d,e){b=b.a(d);c=c.a(d);var f;if(b instanceof D&&c instanceof D){b=F(b);for(d=G(b);d;d=G(b))for(e=F(c),f=G(e);f;f=G(e))if(a(B(d),B(f)))return!0;return!1}if(b instanceof D||c instanceof D){b instanceof D?(e=b,d=c):(e=c,d=b);f=F(e);for(var g=typeof d,k=G(f);k;k=G(f)){switch(g){case "number":k=+B(k);break;case "boolean":k=!!B(k);break;case "string":k=B(k);break;default:throw Error("Illegal primitive type for comparison.");}if(e==b&&a(k,d)||e==c&&a(d,k))return!0}return!1}return e?"boolean"==
|
||||
typeof b||"boolean"==typeof c?a(!!b,!!c):"number"==typeof b||"number"==typeof c?a(+b,+c):a(b,c):a(+b,+c)}xb.prototype.a=function(a){return this.c.s(this.j,this.u,a)};xb.prototype.toString=function(){var a="Binary Expression: "+this.c;a+=I(this.j);return a+=I(this.u)};function Ab(a,b,c,d){this.L=a;this.G=b;this.l=c;this.s=d}Ab.prototype.toString=function(){return this.L};var Bb={};
|
||||
function M(a,b,c,d){if(Bb.hasOwnProperty(a))throw Error("Binary operator already created: "+a);a=new Ab(a,b,c,d);return Bb[a.toString()]=a}M("div",6,1,function(a,b,c){return J(a,c)/J(b,c)});M("mod",6,1,function(a,b,c){return J(a,c)%J(b,c)});M("*",6,1,function(a,b,c){return J(a,c)*J(b,c)});M("+",5,1,function(a,b,c){return J(a,c)+J(b,c)});M("-",5,1,function(a,b,c){return J(a,c)-J(b,c)});M("<",4,2,function(a,b,c){return zb(function(d,e){return d<e},a,b,c)});
|
||||
M(">",4,2,function(a,b,c){return zb(function(d,e){return d>e},a,b,c)});M("<=",4,2,function(a,b,c){return zb(function(d,e){return d<=e},a,b,c)});M(">=",4,2,function(a,b,c){return zb(function(d,e){return d>=e},a,b,c)});var yb=M("=",3,2,function(a,b,c){return zb(function(d,e){return d==e},a,b,c,!0)});M("!=",3,2,function(a,b,c){return zb(function(d,e){return d!=e},a,b,c,!0)});M("and",2,2,function(a,b,c){return wb(a,c)&&wb(b,c)});M("or",1,2,function(a,b,c){return wb(a,c)||wb(b,c)});function Cb(a,b){if(b.a.length&&4!=a.l)throw Error("Primary expression must evaluate to nodeset if filter has predicate(s).");H.call(this,a.l);this.c=a;this.j=b;this.i=a.i;this.b=a.b}m(Cb,H);Cb.prototype.a=function(a){a=this.c.a(a);return Db(this.j,a)};Cb.prototype.toString=function(){var a="Filter:"+I(this.c);return a+=I(this.j)};function Eb(a,b){if(b.length<a.F)throw Error("Function "+a.m+" expects at least"+a.F+" arguments, "+b.length+" given");if(null!==a.D&&b.length>a.D)throw Error("Function "+a.m+" expects at most "+a.D+" arguments, "+b.length+" given");a.K&&n(b,function(c,d){if(4!=c.l)throw Error("Argument "+d+" to function "+a.m+" is not of type Nodeset: "+c);});H.call(this,a.l);this.B=a;this.c=b;ub(this,a.i||qa(b,function(c){return c.i}));vb(this,a.J&&!b.length||a.I&&!!b.length||qa(b,function(c){return c.b}))}
|
||||
m(Eb,H);Eb.prototype.a=function(a){return this.B.s.apply(null,sa(a,this.c))};Eb.prototype.toString=function(){var a="Function: "+this.B;if(this.c.length){var b=pa(this.c,function(c,d){return c+I(d)},"Arguments:");a+=I(b)}return a};function Fb(a,b,c,d,e,f,g,k){this.m=a;this.l=b;this.i=c;this.J=d;this.I=!1;this.s=e;this.F=f;this.D=void 0!==g?g:f;this.K=!!k}Fb.prototype.toString=function(){return this.m};var Gb={};
|
||||
function N(a,b,c,d,e,f,g,k){if(Gb.hasOwnProperty(a))throw Error("Function already created: "+a+".");Gb[a]=new Fb(a,b,c,d,e,f,g,k)}N("boolean",2,!1,!1,function(a,b){return wb(b,a)},1);N("ceiling",1,!1,!1,function(a,b){return Math.ceil(J(b,a))},1);N("concat",3,!1,!1,function(a,b){return pa(ta(arguments,1),function(c,d){return c+L(d,a)},"")},2,null);N("contains",2,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);return-1!=b.indexOf(a)},2);N("count",1,!1,!1,function(a,b){return b.a(a).o},1,1,!0);
|
||||
N("false",2,!1,!1,function(){return!1},0);N("floor",1,!1,!1,function(a,b){return Math.floor(J(b,a))},1);N("id",4,!1,!1,function(a,b){function c(k){if(x){var l=e.all[k];if(l){if(l.nodeType&&k==l.id)return l;if(l.length)return ra(l,function(u){return k==u.id})}return null}return e.getElementById(k)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument;a=L(b,a).split(/\s+/);var f=[];n(a,function(k){k=c(k);!k||0<=ma(f,k)||f.push(k)});f.sort($a);var g=new D;n(f,function(k){g.add(k)});return g},1);
|
||||
N("lang",2,!1,!1,function(){return!1},1);N("last",1,!0,!1,function(a){if(1!=arguments.length)throw Error("Function last expects ()");return a.f},0);N("local-name",3,!1,!0,function(a,b){return(a=b?rb(b.a(a)):a.a)?a.localName||a.nodeName.toLowerCase():""},0,1,!0);N("name",3,!1,!0,function(a,b){return(a=b?rb(b.a(a)):a.a)?a.nodeName.toLowerCase():""},0,1,!0);N("namespace-uri",3,!0,!1,function(){return""},0,1,!0);
|
||||
N("normalize-space",3,!1,!0,function(a,b){return(b?L(b,a):B(a.a)).replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"")},0,1);N("not",2,!1,!1,function(a,b){return!wb(b,a)},1);N("number",1,!1,!0,function(a,b){return b?J(b,a):+B(a.a)},0,1);N("position",1,!0,!1,function(a){return a.b},0);N("round",1,!1,!1,function(a,b){return Math.round(J(b,a))},1);N("starts-with",2,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);return 0==b.lastIndexOf(a,0)},2);N("string",3,!1,!0,function(a,b){return b?L(b,a):B(a.a)},0,1);
|
||||
N("string-length",1,!1,!0,function(a,b){return(b?L(b,a):B(a.a)).length},0,1);N("substring",3,!1,!1,function(a,b,c,d){c=J(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return"";d=d?J(d,a):Infinity;if(isNaN(d)||-Infinity===d)return"";c=Math.round(c)-1;var e=Math.max(c,0);a=L(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);N("substring-after",3,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);c=b.indexOf(a);return-1==c?"":b.substring(c+a.length)},2);
|
||||
N("substring-before",3,!1,!1,function(a,b,c){b=L(b,a);a=L(c,a);a=b.indexOf(a);return-1==a?"":b.substring(0,a)},2);N("sum",1,!1,!1,function(a,b){a=F(b.a(a));b=0;for(var c=G(a);c;c=G(a))b+=+B(c);return b},1,1,!0);N("translate",3,!1,!1,function(a,b,c,d){b=L(b,a);c=L(c,a);var e=L(d,a);a={};for(d=0;d<c.length;d++){var f=c.charAt(d);f in a||(a[f]=e.charAt(d))}c="";for(d=0;d<b.length;d++)f=b.charAt(d),c+=f in a?a[f]:f;return c},3);N("true",2,!1,!1,function(){return!0},0);function E(a,b){this.j=a;this.c=void 0!==b?b:null;this.b=null;switch(a){case "comment":this.b=8;break;case "text":this.b=3;break;case "processing-instruction":this.b=7;break;case "node":break;default:throw Error("Unexpected argument");}}function Hb(a){return"comment"==a||"text"==a||"processing-instruction"==a||"node"==a}E.prototype.a=function(a){return null===this.b||this.b==a.nodeType};E.prototype.f=function(){return this.j};
|
||||
E.prototype.toString=function(){var a="Kind Test: "+this.j;null===this.c||(a+=I(this.c));return a};function Ib(a){H.call(this,3);this.c=a.substring(1,a.length-1)}m(Ib,H);Ib.prototype.a=function(){return this.c};Ib.prototype.toString=function(){return"Literal: "+this.c};function jb(a,b){this.m=a.toLowerCase();a="*"==this.m?"*":"http://www.w3.org/1999/xhtml";this.c=b?b.toLowerCase():a}jb.prototype.a=function(a){var b=a.nodeType;if(1!=b&&2!=b)return!1;b=void 0!==a.localName?a.localName:a.nodeName;return"*"!=this.m&&this.m!=b.toLowerCase()?!1:"*"==this.c?!0:this.c==(a.namespaceURI?a.namespaceURI.toLowerCase():"http://www.w3.org/1999/xhtml")};jb.prototype.f=function(){return this.m};
|
||||
jb.prototype.toString=function(){return"Name Test: "+("http://www.w3.org/1999/xhtml"==this.c?"":this.c+":")+this.m};function Jb(a){H.call(this,1);this.c=a}m(Jb,H);Jb.prototype.a=function(){return this.c};Jb.prototype.toString=function(){return"Number: "+this.c};function Kb(a,b){H.call(this,a.l);this.j=a;this.c=b;this.i=a.i;this.b=a.b;1==this.c.length&&(a=this.c[0],a.C||a.c!=Lb||(a=a.u,"*"!=a.f()&&(this.f={name:a.f(),A:null})))}m(Kb,H);function Mb(){H.call(this,4)}m(Mb,H);Mb.prototype.a=function(a){var b=new D;a=a.a;9==a.nodeType?b.add(a):b.add(a.ownerDocument);return b};Mb.prototype.toString=function(){return"Root Helper Expression"};function Nb(){H.call(this,4)}m(Nb,H);Nb.prototype.a=function(a){var b=new D;b.add(a.a);return b};Nb.prototype.toString=function(){return"Context Helper Expression"};
|
||||
function Ob(a){return"/"==a||"//"==a}Kb.prototype.a=function(a){var b=this.j.a(a);if(!(b instanceof D))throw Error("Filter expression must evaluate to nodeset.");a=this.c;for(var c=0,d=a.length;c<d&&b.o;c++){var e=a[c],f=F(b,e.c.v);if(e.i||e.c!=Pb)if(e.i||e.c!=Qb){var g=G(f);for(b=e.a(new ka(g));null!=(g=G(f));)g=e.a(new ka(g)),b=pb(b,g)}else g=G(f),b=e.a(new ka(g));else{for(g=G(f);(b=G(f))&&(!g.contains||g.contains(b))&&b.compareDocumentPosition(g)&8;g=b);b=e.a(new ka(g))}}return b};
|
||||
Kb.prototype.toString=function(){var a="Path Expression:"+I(this.j);if(this.c.length){var b=pa(this.c,function(c,d){return c+I(d)},"Steps:");a+=I(b)}return a};function Rb(a,b){this.a=a;this.v=!!b}
|
||||
function Db(a,b,c){for(c=c||0;c<a.a.length;c++)for(var d=a.a[c],e=F(b),f=b.o,g,k=0;g=G(e);k++){var l=a.v?f-k:k+1;g=d.a(new ka(g,l,f));if("number"==typeof g)l=l==g;else if("string"==typeof g||"boolean"==typeof g)l=!!g;else if(g instanceof D)l=0<g.o;else throw Error("Predicate.evaluate returned an unexpected type.");if(!l){l=e;g=l.f;var u=l.a;if(!u)throw Error("Next must be called at least once before remove.");var K=u.b;u=u.a;K?K.a=u:g.a=u;u?u.b=K:g.b=K;g.o--;l.a=null}}return b}
|
||||
Rb.prototype.toString=function(){return pa(this.a,function(a,b){return a+I(b)},"Predicates:")};function O(a,b,c,d){H.call(this,4);this.c=a;this.u=b;this.j=c||new Rb([]);this.C=!!d;b=this.j;b=0<b.a.length?b.a[0].f:null;a.M&&b&&(a=b.name,a=x?a.toLowerCase():a,this.f={name:a,A:b.A});a:{a=this.j;for(b=0;b<a.a.length;b++)if(c=a.a[b],c.i||1==c.l||0==c.l){a=!0;break a}a=!1}this.i=a}m(O,H);
|
||||
O.prototype.a=function(a){var b=a.a,c=this.f,d=null,e=null,f=0;c&&(d=c.name,e=c.A?L(c.A,a):null,f=1);if(this.C)if(this.i||this.c!=Sb)if(b=F((new O(Tb,new E("node"))).a(a)),c=G(b))for(a=this.s(c,d,e,f);null!=(c=G(b));)a=pb(a,this.s(c,d,e,f));else a=new D;else a=gb(this.u,b,d,e),a=Db(this.j,a,f);else a=this.s(a.a,d,e,f);return a};O.prototype.s=function(a,b,c,d){a=this.c.B(this.u,a,b,c);return a=Db(this.j,a,d)};
|
||||
O.prototype.toString=function(){var a="Step:"+I("Operator: "+(this.C?"//":"/"));this.c.m&&(a+=I("Axis: "+this.c));a+=I(this.u);if(this.j.a.length){var b=pa(this.j.a,function(c,d){return c+I(d)},"Predicates:");a+=I(b)}return a};function Ub(a,b,c,d){this.m=a;this.B=b;this.v=c;this.M=d}Ub.prototype.toString=function(){return this.m};var Vb={};function P(a,b,c,d){if(Vb.hasOwnProperty(a))throw Error("Axis already created: "+a);b=new Ub(a,b,c,!!d);return Vb[a]=b}
|
||||
P("ancestor",function(a,b){for(var c=new D;b=b.parentNode;)a.a(b)&&qb(c,b);return c},!0);P("ancestor-or-self",function(a,b){var c=new D;do a.a(b)&&qb(c,b);while(b=b.parentNode);return c},!0);
|
||||
var Lb=P("attribute",function(a,b){var c=new D,d=a.f();if("style"==d&&x&&b.style)return c.add(new Qa(b.style,b,"style",b.style.cssText)),c;var e=b.attributes;if(e)if(a instanceof E&&null===a.b||"*"==d)for(a=0;d=e[a];a++)x?d.nodeValue&&c.add(Sa(b,d)):c.add(d);else(d=e.getNamedItem(d))&&(x?d.nodeValue&&c.add(Sa(b,d)):c.add(d));return c},!1),Sb=P("child",function(a,b,c,d,e){return(x?mb:nb).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new D)},!1,!0);P("descendant",gb,!1,!0);
|
||||
var Tb=P("descendant-or-self",function(a,b,c,d){var e=new D;C(b,c,d)&&a.a(b)&&e.add(b);return gb(a,b,c,d,e)},!1,!0),Pb=P("following",function(a,b,c,d){var e=new D;do for(var f=b;f=f.nextSibling;)C(f,c,d)&&a.a(f)&&e.add(f),e=gb(a,f,c,d,e);while(b=b.parentNode);return e},!1,!0);P("following-sibling",function(a,b){for(var c=new D;b=b.nextSibling;)a.a(b)&&c.add(b);return c},!1);P("namespace",function(){return new D},!1);
|
||||
var Wb=P("parent",function(a,b){var c=new D;if(9==b.nodeType)return c;if(2==b.nodeType)return c.add(b.ownerElement),c;b=b.parentNode;a.a(b)&&c.add(b);return c},!1),Qb=P("preceding",function(a,b,c,d){var e=new D,f=[];do f.unshift(b);while(b=b.parentNode);for(var g=1,k=f.length;g<k;g++){var l=[];for(b=f[g];b=b.previousSibling;)l.unshift(b);for(var u=0,K=l.length;u<K;u++)b=l[u],C(b,c,d)&&a.a(b)&&e.add(b),e=gb(a,b,c,d,e)}return e},!0,!0);
|
||||
P("preceding-sibling",function(a,b){for(var c=new D;b=b.previousSibling;)a.a(b)&&qb(c,b);return c},!0);var Xb=P("self",function(a,b){var c=new D;a.a(b)&&c.add(b);return c},!1);function Yb(a){H.call(this,1);this.c=a;this.i=a.i;this.b=a.b}m(Yb,H);Yb.prototype.a=function(a){return-J(this.c,a)};Yb.prototype.toString=function(){return"Unary Expression: -"+I(this.c)};function Zb(a){H.call(this,4);this.c=a;ub(this,qa(this.c,function(b){return b.i}));vb(this,qa(this.c,function(b){return b.b}))}m(Zb,H);Zb.prototype.a=function(a){var b=new D;n(this.c,function(c){c=c.a(a);if(!(c instanceof D))throw Error("Path expression must evaluate to NodeSet.");b=pb(b,c)});return b};Zb.prototype.toString=function(){return pa(this.c,function(a,b){return a+I(b)},"Union Expression:")};function $b(a,b){this.a=a;this.b=b}function ac(a){for(var b,c=[];;){Q(a,"Missing right hand side of binary expression.");b=bc(a);var d=z(a.a);if(!d)break;var e=(d=Bb[d]||null)&&d.G;if(!e){a.a.a--;break}for(;c.length&&e<=c[c.length-1].G;)b=new xb(c.pop(),c.pop(),b);c.push(b,d)}for(;c.length;)b=new xb(c.pop(),c.pop(),b);return b}function Q(a,b){if(Xa(a.a))throw Error(b);}function cc(a,b){a=z(a.a);if(a!=b)throw Error("Bad token, expected: "+b+" got: "+a);}
|
||||
function dc(a){a=z(a.a);if(")"!=a)throw Error("Bad token: "+a);}function ec(a){a=z(a.a);if(2>a.length)throw Error("Unclosed literal string");return new Ib(a)}
|
||||
function fc(a){var b=[];if(Ob(y(a.a))){var c=z(a.a);var d=y(a.a);if("/"==c&&(Xa(a.a)||"."!=d&&".."!=d&&"@"!=d&&"*"!=d&&!/(?![0-9])[\w]/.test(d)))return new Mb;d=new Mb;Q(a,"Missing next location step.");c=gc(a,c);b.push(c)}else{a:{c=y(a.a);d=c.charAt(0);switch(d){case "$":throw Error("Variable reference not allowed in HTML XPath");case "(":z(a.a);c=ac(a);Q(a,'unclosed "("');cc(a,")");break;case '"':case "'":c=ec(a);break;default:if(isNaN(+c))if(!Hb(c)&&/(?![0-9])[\w]/.test(d)&&"("==y(a.a,1)){c=z(a.a);
|
||||
c=Gb[c]||null;z(a.a);for(d=[];")"!=y(a.a);){Q(a,"Missing function argument list.");d.push(ac(a));if(","!=y(a.a))break;z(a.a)}Q(a,"Unclosed function argument list.");dc(a);c=new Eb(c,d)}else{c=null;break a}else c=new Jb(+z(a.a))}"["==y(a.a)&&(d=new Rb(hc(a)),c=new Cb(c,d))}if(c)if(Ob(y(a.a)))d=c;else return c;else c=gc(a,"/"),d=new Nb,b.push(c)}for(;Ob(y(a.a));)c=z(a.a),Q(a,"Missing next location step."),c=gc(a,c),b.push(c);return new Kb(d,b)}
|
||||
function gc(a,b){if("/"!=b&&"//"!=b)throw Error('Step op should be "/" or "//"');if("."==y(a.a)){var c=new O(Xb,new E("node"));z(a.a);return c}if(".."==y(a.a))return c=new O(Wb,new E("node")),z(a.a),c;if("@"==y(a.a)){var d=Lb;z(a.a);Q(a,"Missing attribute name")}else if("::"==y(a.a,1)){if(!/(?![0-9])[\w]/.test(y(a.a).charAt(0)))throw Error("Bad token: "+z(a.a));var e=z(a.a);d=Vb[e]||null;if(!d)throw Error("No axis with name: "+e);z(a.a);Q(a,"Missing node name")}else d=Sb;e=y(a.a);if(/(?![0-9])[\w\*]/.test(e.charAt(0)))if("("==
|
||||
y(a.a,1)){if(!Hb(e))throw Error("Invalid node type: "+e);e=z(a.a);if(!Hb(e))throw Error("Invalid type name: "+e);cc(a,"(");Q(a,"Bad nodetype");var f=y(a.a).charAt(0),g=null;if('"'==f||"'"==f)g=ec(a);Q(a,"Bad nodetype");dc(a);e=new E(e,g)}else if(e=z(a.a),f=e.indexOf(":"),-1==f)e=new jb(e);else{g=e.substring(0,f);if("*"==g)var k="*";else if(k=a.b(g),!k)throw Error("Namespace prefix not declared: "+g);e=e.substr(f+1);e=new jb(e,k)}else throw Error("Bad token: "+z(a.a));a=new Rb(hc(a),d.v);return c||
|
||||
new O(d,e,a,"//"==b)}function hc(a){for(var b=[];"["==y(a.a);){z(a.a);Q(a,"Missing predicate expression.");var c=ac(a);b.push(c);Q(a,"Unclosed predicate expression.");cc(a,"]")}return b}function bc(a){if("-"==y(a.a))return z(a.a),new Yb(bc(a));var b=fc(a);if("|"!=y(a.a))a=b;else{for(b=[b];"|"==z(a.a);)Q(a,"Missing next union location path."),b.push(fc(a));a.a.a--;a=new Zb(b)}return a};function ic(a){switch(a.nodeType){case 1:return ia(jc,a);case 9:return ic(a.documentElement);case 11:case 10:case 6:case 12:return kc;default:return a.parentNode?ic(a.parentNode):kc}}function kc(){return null}function jc(a,b){if(a.prefix==b)return a.namespaceURI||"http://www.w3.org/1999/xhtml";var c=a.getAttributeNode("xmlns:"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?jc(a.parentNode,b):null};function lc(a,b){if(!a.length)throw Error("Empty XPath expression.");a=Ua(a);if(Xa(a))throw Error("Invalid XPath expression.");b?"function"==ca(b)||(b=ha(b.lookupNamespaceURI,b)):b=function(){return null};var c=ac(new $b(a,b));if(!Xa(a))throw Error("Bad token: "+z(a));this.evaluate=function(d,e){d=c.a(new ka(d));return new T(d,e)}}
|
||||
function T(a,b){if(0==b)if(a instanceof D)b=4;else if("string"==typeof a)b=2;else if("number"==typeof a)b=1;else if("boolean"==typeof a)b=3;else throw Error("Unexpected evaluation result.");if(2!=b&&1!=b&&3!=b&&!(a instanceof D))throw Error("value could not be converted to the specified type");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof D?sb(a):""+a;break;case 1:this.numberValue=a instanceof D?+sb(a):+a;break;case 3:this.booleanValue=a instanceof D?0<a.o:!!a;break;case 4:case 5:case 6:case 7:var c=
|
||||
F(a);var d=[];for(var e=G(c);e;e=G(c))d.push(e instanceof Qa?e.a:e);this.snapshotLength=a.o;this.invalidIteratorState=!1;break;case 8:case 9:a=rb(a);this.singleNodeValue=a instanceof Qa?a.a:a;break;default:throw Error("Unknown XPathResult type.");}var f=0;this.iterateNext=function(){if(4!=b&&5!=b)throw Error("iterateNext called with wrong result type");return f>=d.length?null:d[f++]};this.snapshotItem=function(g){if(6!=b&&7!=b)throw Error("snapshotItem called with wrong result type");return g>=d.length||
|
||||
0>g?null:d[g]}}T.ANY_TYPE=0;T.NUMBER_TYPE=1;T.STRING_TYPE=2;T.BOOLEAN_TYPE=3;T.UNORDERED_NODE_ITERATOR_TYPE=4;T.ORDERED_NODE_ITERATOR_TYPE=5;T.UNORDERED_NODE_SNAPSHOT_TYPE=6;T.ORDERED_NODE_SNAPSHOT_TYPE=7;T.ANY_UNORDERED_NODE_TYPE=8;T.FIRST_ORDERED_NODE_TYPE=9;function mc(a){this.lookupNamespaceURI=ic(a)}
|
||||
function nc(a,b){a=a||h;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=T,c.evaluate=function(d,e,f,g){return(new lc(d,f)).evaluate(e,g)},c.createExpression=function(d,e){return new lc(d,e)},c.createNSResolver=function(d){return new mc(d)}}ba("wgxpath.install",nc);ba("wgxpath.install",nc);var oc=window;function U(a,b){this.code=a;this.a=V[a]||pc;this.message=b||"";a=this.a.replace(/((?:^|\s+)[a-z])/g,function(c){return c.toUpperCase().replace(/^[\s\xa0]+/g,"")});b=a.length-5;if(0>b||a.indexOf("Error",b)!=b)a+="Error";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||""}m(U,Error);var pc="unknown error",V={15:"element not selectable",11:"element not visible"};V[31]=pc;V[30]=pc;V[24]="invalid cookie domain";V[29]="invalid element coordinates";V[12]="invalid element state";
|
||||
V[32]="invalid selector";V[51]="invalid selector";V[52]="invalid selector";V[17]="javascript error";V[405]="unsupported operation";V[34]="move target out of bounds";V[27]="no such alert";V[7]="no such element";V[8]="no such frame";V[23]="no such window";V[28]="script timeout";V[33]="session not created";V[10]="stale element reference";V[21]="timeout";V[25]="unable to set cookie";V[26]="unexpected alert open";V[13]=pc;V[9]="unknown command";var qc=za(),rc=Ba()||q("iPod"),sc=q("iPad"),tc=q("Android")&&!(Aa()||za()||q("Opera")||q("Silk")),uc=Aa(),vc=q("Safari")&&!(Aa()||q("Coast")||q("Opera")||q("Edge")||q("Edg/")||q("OPR")||za()||q("Silk")||q("Android"))&&!(Ba()||q("iPad")||q("iPod"));function wc(a){return(a=a.exec(p))?a[1]:""}var xc=function(){if(qc)return wc(/Firefox\/([0-9.]+)/);if(r||Fa||Ea)return Ka;if(uc)return Ba()||q("iPad")||q("iPod")?wc(/CriOS\/([0-9.]+)/):wc(/Chrome\/([0-9.]+)/);if(vc&&!(Ba()||q("iPad")||q("iPod")))return wc(/Version\/([0-9.]+)/);if(rc||sc){var a=/Version\/(\S+).*Mobile\/(\S+)/.exec(p);if(a)return a[1]+"."+a[2]}else if(tc)return(a=wc(/Android\s+([0-9.]+)/))?a:wc(/Version\/([0-9.]+)/);return""}();var yc=sc||rc,zc;if(tc){var Ac=/Android\s+([0-9\.]+)/.exec(p);zc=Ac?Ac[1]:"0"}else zc="0";var Bc=zc,Cc=r&&!(8<=Number(w)),Dc=r&&!(9<=Number(w));function Ec(a,b){b=b.toLowerCase();return"style"==b?Fc(a.style.cssText):Cc&&"value"==b&&Gc(a,"INPUT")?a.value:Dc&&!0===a[b]?String(a.getAttribute(b)):(a=a.getAttributeNode(b))&&a.specified?a.value:null}var Hc=/[;]+(?=(?:(?:[^"]*"){2})*[^"]*$)(?=(?:(?:[^']*'){2})*[^']*$)(?=(?:[^()]*\([^()]*\))*[^()]*$)/;
|
||||
function Fc(a){var b=[];n(a.split(Hc),function(c){var d=c.indexOf(":");0<d&&(c=[c.slice(0,d),c.slice(d+1)],2==c.length&&b.push(c[0].toLowerCase(),":",c[1],";"))});b=b.join("");return b=";"==b.charAt(b.length-1)?b:b+";"}function Ic(a,b){Cc&&"value"==b&&Gc(a,"OPTION")&&null===Ec(a,"value")?(b=[],eb(a,b,!1),a=b.join("")):a=a[b];return a}function Gc(a,b){b&&"string"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}
|
||||
function Jc(a){return Gc(a,"OPTION")?!0:Gc(a,"INPUT")?(a=a.type.toLowerCase(),"checkbox"==a||"radio"==a):!1};function Kc(a){a=a?A(a):document;return!r||9<=Number(w)||"CSS1Compat"==(a?new fb(A(a)):la||(la=new fb)).a.compatMode?a.documentElement:a.body};var Lc=!(r&&!(r?0<=va(w,10):Oa(10))),Mc=tc?!(tc?0<=va(Bc,4):0<=va(xc,4)):!yc,Nc=r&&oc.navigator.msPointerEnabled;function Oc(a,b,c){this.a=a;this.b=b;this.f=c}Oc.prototype.create=function(a){a=A(a).createEvent("HTMLEvents");a.initEvent(this.a,this.b,this.f);return a};Oc.prototype.toString=function(){return this.a};function W(a,b,c){ja(this,a,b,c)}m(W,Oc);
|
||||
W.prototype.create=function(a,b){if(!t&&this==Pc)throw new U(9,"Browser does not support a mouse pixel scroll event.");var c=A(a),d=c?c.parentWindow||c.defaultView:window;var e=c.createEvent("MouseEvents");var f=1;this==Qc&&(t||(e.wheelDelta=b.wheelDelta),t&&(f=b.wheelDelta/-40));t&&this==Pc&&(f=b.wheelDelta);e.initMouseEvent(this.a,this.b,this.f,d,f,b.clientX,b.clientY,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget);if(r&&0===e.pageX&&0===e.pageY&&Object.defineProperty){a=
|
||||
Ya((a?new fb(A(a)):la||(la=new fb)).a);c=Kc(c);var g=b.clientX+a.scrollLeft-c.clientLeft,k=b.clientY+a.scrollTop-c.clientTop;Object.defineProperty(e,"pageX",{get:function(){return g}});Object.defineProperty(e,"pageY",{get:function(){return k}})}return e};function Rc(a,b,c){ja(this,a,b,c)}m(Rc,Oc);
|
||||
Rc.prototype.create=function(a,b){var c=A(a);if(t&&!(r?0<=va(w,93):Oa(93))){a=c?c.parentWindow||c.defaultView:window;var d=b.charCode?0:b.keyCode;c=c.createEvent("KeyboardEvent");c.initKeyEvent(this.a,this.b,this.f,a,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,d,b.charCode);this.a==Sc&&b.preventDefault&&c.preventDefault()}else if(c=c.createEvent("Events"),c.initEvent(this.a,this.b,this.f),c.altKey=b.altKey,c.ctrlKey=b.ctrlKey,c.metaKey=b.metaKey,c.shiftKey=b.shiftKey,c.keyCode=b.charCode||b.keyCode,Ga||
|
||||
Fa)c.charCode=this==Sc?c.keyCode:0;return c};function Tc(a,b,c){ja(this,a,b,c)}m(Tc,Oc);
|
||||
Tc.prototype.create=function(a,b){function c(R){R=oa(R,function(v){return g.createTouch(k,a,v.identifier,v.pageX,v.pageY,v.screenX,v.screenY)});return g.createTouchList.apply(g,R)}function d(R){var v=oa(R,function(S){return{identifier:S.identifier,screenX:S.screenX,screenY:S.screenY,clientX:S.clientX,clientY:S.clientY,pageX:S.pageX,pageY:S.pageY,target:a}});v.item=function(S){return v[S]};return v}function e(R){return oa(R,function(v){return new Touch({identifier:v.identifier,screenX:v.screenX,screenY:v.screenY,
|
||||
clientX:v.clientX,clientY:v.clientY,pageX:v.pageX,pageY:v.pageY,target:a})})}function f(R,v){switch(R){case 1:return d(v);case 2:return c(v);case 3:return e(v)}return null}if(!Lc)throw new U(9,"Browser does not support firing touch events.");var g=A(a),k=g?g.parentWindow||g.defaultView:window;if(Mc)var l=1;else if(TouchEvent.prototype.initTouchEvent)l=2;else if(TouchEvent&&0<TouchEvent.length)l=3;else throw new U(9,"Not able to create touch events in this browser");var u=f(l,b.changedTouches),K=b.touches==
|
||||
b.changedTouches?u:f(l,b.touches),Ra=b.targetTouches==b.changedTouches?u:f(l,b.targetTouches);if(1==l)l=g.createEvent("MouseEvents"),l.initMouseEvent(this.a,this.b,this.f,k,1,0,0,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,0,b.relatedTarget),l.touches=K,l.targetTouches=Ra,l.changedTouches=u,l.scale=b.scale,l.rotation=b.rotation;else if(2==l)l=g.createEvent("TouchEvent"),0==l.initTouchEvent.length?l.initTouchEvent(K,Ra,u,this.a,k,0,0,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,
|
||||
b.metaKey):l.initTouchEvent(this.a,this.b,this.f,k,1,0,0,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,K,Ra,u,b.scale,b.rotation),l.relatedTarget=b.relatedTarget;else if(3==l)l=new TouchEvent(this.a,{touches:K,targetTouches:Ra,changedTouches:u,bubbles:this.b,cancelable:this.f,ctrlKey:b.ctrlKey,shiftKey:b.shiftKey,altKey:b.altKey,metaKey:b.metaKey});else throw new U(9,"Illegal TouchEventStrategy_ value (this is a bug)");return l};function X(a,b,c){ja(this,a,b,c)}m(X,Oc);
|
||||
X.prototype.create=function(a,b){if(!Nc)throw new U(9,"Browser does not support MSGesture events.");var c=A(a);a=c?c.parentWindow||c.defaultView:window;c=c.createEvent("MSGestureEvent");c.initGestureEvent(this.a,this.b,this.f,a,1,0,0,b.clientX,b.clientY,0,0,b.translationX,b.translationY,b.scale,b.expansion,b.rotation,b.velocityX,b.velocityY,b.velocityExpansion,b.velocityAngular,(new Date).getTime(),b.relatedTarget);return c};function Y(a,b,c){ja(this,a,b,c)}m(Y,Oc);
|
||||
Y.prototype.create=function(a,b){if(!Nc)throw new U(9,"Browser does not support MSPointer events.");var c=A(a);a=c?c.parentWindow||c.defaultView:window;c=c.createEvent("MSPointerEvent");c.initPointerEvent(this.a,this.b,this.f,a,0,0,0,b.clientX,b.clientY,b.ctrlKey,b.altKey,b.shiftKey,b.metaKey,b.button,b.relatedTarget,0,0,b.width,b.height,b.pressure,b.rotation,b.tiltX,b.tiltY,b.pointerId,b.pointerType,0,b.isPrimary);return c};new W("click",!0,!0);new W("contextmenu",!0,!0);new W("dblclick",!0,!0);
|
||||
new W("mousedown",!0,!0);new W("mousemove",!0,!1);new W("mouseout",!0,!0);new W("mouseover",!0,!0);new W("mouseup",!0,!0);var Qc=new W(t?"DOMMouseScroll":"mousewheel",!0,!0),Pc=new W("MozMousePixelScroll",!0,!0);new Rc("keydown",!0,!0);var Sc=new Rc("keypress",!0,!0);new Rc("keyup",!0,!0);new Tc("touchend",!0,!0);new Tc("touchmove",!0,!0);new Tc("touchstart",!0,!0);new X("MSGestureChange",!0,!0);new X("MSGestureEnd",!0,!0);new X("MSGestureHold",!0,!0);new X("MSGestureStart",!0,!0);
|
||||
new X("MSGestureTap",!0,!0);new X("MSInertiaStart",!0,!0);new Y("MSGotPointerCapture",!0,!1);new Y("MSLostPointerCapture",!0,!1);new Y("MSPointerCancel",!0,!0);new Y("MSPointerDown",!0,!0);new Y("MSPointerMove",!0,!0);new Y("MSPointerOver",!0,!0);new Y("MSPointerOut",!0,!0);new Y("MSPointerUp",!0,!0);function Uc(a,b){this.b={};this.a=[];this.f=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else if(a)if(a instanceof Uc)for(c=Vc(a),d=0;d<c.length;d++)this.set(c[d],a.get(c[d]));else for(d in a)this.set(d,a[d])}
|
||||
function Vc(a){if(a.f!=a.a.length){for(var b=0,c=0;b<a.a.length;){var d=a.a[b];Object.prototype.hasOwnProperty.call(a.b,d)&&(a.a[c++]=d);b++}a.a.length=c}if(a.f!=a.a.length){var e={};for(c=b=0;b<a.a.length;)d=a.a[b],Object.prototype.hasOwnProperty.call(e,d)||(a.a[c++]=d,e[d]=1),b++;a.a.length=c}return a.a.concat()}Uc.prototype.get=function(a,b){return Object.prototype.hasOwnProperty.call(this.b,a)?this.b[a]:b};
|
||||
Uc.prototype.set=function(a,b){Object.prototype.hasOwnProperty.call(this.b,a)||(this.f++,this.a.push(a));this.b[a]=b};var Wc={};function Z(a,b,c){da(a)&&(a=t?a.g:a.h);a=new Xc(a);!b||b in Wc&&!c||(Wc[b]={key:a,shift:!1},c&&(Wc[c]={key:a,shift:!0}));return a}function Xc(a){this.code=a}Z(8);Z(9);Z(13);var Yc=Z(16),Zc=Z(17),$c=Z(18);Z(19);Z(20);Z(27);Z(32," ");Z(33);Z(34);Z(35);Z(36);Z(37);Z(38);Z(39);Z(40);Z(44);Z(45);Z(46);Z(48,"0",")");Z(49,"1","!");Z(50,"2","@");Z(51,"3","#");Z(52,"4","$");Z(53,"5","%");Z(54,"6","^");Z(55,"7","&");Z(56,"8","*");Z(57,"9","(");Z(65,"a","A");Z(66,"b","B");Z(67,"c","C");Z(68,"d","D");
|
||||
Z(69,"e","E");Z(70,"f","F");Z(71,"g","G");Z(72,"h","H");Z(73,"i","I");Z(74,"j","J");Z(75,"k","K");Z(76,"l","L");Z(77,"m","M");Z(78,"n","N");Z(79,"o","O");Z(80,"p","P");Z(81,"q","Q");Z(82,"r","R");Z(83,"s","S");Z(84,"t","T");Z(85,"u","U");Z(86,"v","V");Z(87,"w","W");Z(88,"x","X");Z(89,"y","Y");Z(90,"z","Z");var ad=Z(Ia?{g:91,h:91}:Ha?{g:224,h:91}:{g:0,h:91});Z(Ia?{g:92,h:92}:Ha?{g:224,h:93}:{g:0,h:92});Z(Ia?{g:93,h:93}:Ha?{g:0,h:0}:{g:93,h:null});Z({g:96,h:96},"0");Z({g:97,h:97},"1");
|
||||
Z({g:98,h:98},"2");Z({g:99,h:99},"3");Z({g:100,h:100},"4");Z({g:101,h:101},"5");Z({g:102,h:102},"6");Z({g:103,h:103},"7");Z({g:104,h:104},"8");Z({g:105,h:105},"9");Z({g:106,h:106},"*");Z({g:107,h:107},"+");Z({g:109,h:109},"-");Z({g:110,h:110},".");Z({g:111,h:111},"/");Z(144);Z(112);Z(113);Z(114);Z(115);Z(116);Z(117);Z(118);Z(119);Z(120);Z(121);Z(122);Z(123);Z({g:107,h:187},"=","+");Z(108,",");Z({g:109,h:189},"-","_");Z(188,",","<");Z(190,".",">");Z(191,"/","?");Z(192,"`","~");Z(219,"[","{");
|
||||
Z(220,"\\","|");Z(221,"]","}");Z({g:59,h:186},";",":");Z(222,"'",'"');var bd=new Uc;bd.set(1,Yc);bd.set(2,Zc);bd.set(4,$c);bd.set(8,ad);(function(a){var b=new Uc;n(Vc(a),function(c){b.set(a.get(c).code,c)});return b})(bd);var cd={"class":"className",readonly:"readOnly"},dd="allowfullscreen allowpaymentrequest allowusermedia async autofocus autoplay checked compact complete controls declare default defaultchecked defaultselected defer disabled ended formnovalidate hidden indeterminate iscontenteditable ismap itemscope loop multiple muted nohref nomodule noresize noshade novalidate nowrap open paused playsinline pubdate readonly required reversed scoped seamless seeking selected truespeed typemustmatch willvalidate".split(" ");ba("_",function(a,b){var c=null,d=b.toLowerCase();if("style"==d)return(c=a.style)&&!aa(c)&&(c=c.cssText),c;if(("selected"==d||"checked"==d)&&Jc(a)){if(!Jc(a))throw new U(15,"Element is not selectable");b="selected";c=a.type&&a.type.toLowerCase();if("checkbox"==c||"radio"==c)b="checked";return Ic(a,b)?"true":null}var e=Gc(a,"A");if(Gc(a,"IMG")&&"src"==d||e&&"href"==d)return(c=Ec(a,d))&&(c=Ic(a,d)),c;if("spellcheck"==d){c=Ec(a,d);if(null!==c){if("false"==c.toLowerCase())return"false";if("true"==c.toLowerCase())return"true"}return Ic(a,
|
||||
d)+""}e=cd[b]||b;if(0<=ma(dd,d))return(c=null!==Ec(a,b)||Ic(a,e))?"true":null;try{var f=Ic(a,e)}catch(g){}null==f||da(f)?c=Ec(a,b):c=f;return null!=c?c.toString():null});; return this._.apply(null,arguments);}).apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);};
|
||||
101
node_modules/selenium-webdriver/lib/atoms/is-displayed.js
generated
vendored
Normal file
101
node_modules/selenium-webdriver/lib/atoms/is-displayed.js
generated
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
// GENERATED CODE - DO NOT EDIT
|
||||
module.exports = function(){return (function(){var k=this||self;function aa(a){return"string"==typeof a}function ba(a,b){a=a.split(".");var c=k;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b}
|
||||
function ca(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null";
|
||||
else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function da(a,b,c){return a.call.apply(a.bind,arguments)}function ea(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var e=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(e,d);return a.apply(b,e)}}return function(){return a.apply(b,arguments)}}
|
||||
function fa(a,b,c){Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?fa=da:fa=ea;return fa.apply(null,arguments)}function ha(a,b){var c=Array.prototype.slice.call(arguments,1);return function(){var d=c.slice();d.push.apply(d,arguments);return a.apply(this,d)}}function l(a,b){function c(){}c.prototype=b.prototype;a.prototype=new c;a.prototype.constructor=a};/*
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2007 Cybozu Labs, Inc.
|
||||
Copyright (c) 2012 Google Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
*/
|
||||
function ia(a,b,c){this.a=a;this.b=b||1;this.f=c||1};var ja=Array.prototype.indexOf?function(a,b){return Array.prototype.indexOf.call(a,b,void 0)}:function(a,b){if("string"===typeof a)return"string"!==typeof b||1!=b.length?-1:a.indexOf(b,0);for(var c=0;c<a.length;c++)if(c in a&&a[c]===b)return c;return-1},n=Array.prototype.forEach?function(a,b){Array.prototype.forEach.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)e in d&&b.call(void 0,d[e],e,a)},ka=Array.prototype.filter?function(a,b){return Array.prototype.filter.call(a,
|
||||
b,void 0)}:function(a,b){for(var c=a.length,d=[],e=0,f="string"===typeof a?a.split(""):a,g=0;g<c;g++)if(g in f){var h=f[g];b.call(void 0,h,g,a)&&(d[e++]=h)}return d},la=Array.prototype.reduce?function(a,b,c){return Array.prototype.reduce.call(a,b,c)}:function(a,b,c){var d=c;n(a,function(e,f){d=b.call(void 0,d,e,f,a)});return d},ma=Array.prototype.some?function(a,b){return Array.prototype.some.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in
|
||||
d&&b.call(void 0,d[e],e,a))return!0;return!1},na=Array.prototype.every?function(a,b){return Array.prototype.every.call(a,b,void 0)}:function(a,b){for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&!b.call(void 0,d[e],e,a))return!1;return!0};function oa(a,b){a:{for(var c=a.length,d="string"===typeof a?a.split(""):a,e=0;e<c;e++)if(e in d&&b.call(void 0,d[e],e,a)){b=e;break a}b=-1}return 0>b?null:"string"===typeof a?a.charAt(b):a[b]}
|
||||
function pa(a){return Array.prototype.concat.apply([],arguments)}function qa(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var ra=String.prototype.trim?function(a){return a.trim()}:function(a){return/^[\s\xa0]*([\s\S]*?)[\s\xa0]*$/.exec(a)[1]};function sa(a,b){return a<b?-1:a>b?1:0};var t;a:{var ta=k.navigator;if(ta){var ua=ta.userAgent;if(ua){t=ua;break a}}t=""}function u(a){return-1!=t.indexOf(a)};function va(){return u("Firefox")||u("FxiOS")}function wa(){return(u("Chrome")||u("CriOS"))&&!u("Edge")};function xa(a){return String(a).replace(/\-([a-z])/g,function(b,c){return c.toUpperCase()})};function ya(){return u("iPhone")&&!u("iPod")&&!u("iPad")};function za(a,b){var c=Aa;return Object.prototype.hasOwnProperty.call(c,a)?c[a]:c[a]=b(a)};var Ba=u("Opera"),v=u("Trident")||u("MSIE"),Ca=u("Edge"),Da=u("Gecko")&&!(-1!=t.toLowerCase().indexOf("webkit")&&!u("Edge"))&&!(u("Trident")||u("MSIE"))&&!u("Edge"),Ea=-1!=t.toLowerCase().indexOf("webkit")&&!u("Edge");function Fa(){var a=k.document;return a?a.documentMode:void 0}var Ga;
|
||||
a:{var Ha="",Ia=function(){var a=t;if(Da)return/rv:([^\);]+)(\)|;)/.exec(a);if(Ca)return/Edge\/([\d\.]+)/.exec(a);if(v)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(Ea)return/WebKit\/(\S+)/.exec(a);if(Ba)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Ia&&(Ha=Ia?Ia[1]:"");if(v){var Ja=Fa();if(null!=Ja&&Ja>parseFloat(Ha)){Ga=String(Ja);break a}}Ga=Ha}var Aa={};
|
||||
function Ka(a){return za(a,function(){for(var b=0,c=ra(String(Ga)).split("."),d=ra(String(a)).split("."),e=Math.max(c.length,d.length),f=0;0==b&&f<e;f++){var g=c[f]||"",h=d[f]||"";do{g=/(\d*)(\D*)(.*)/.exec(g)||["","","",""];h=/(\d*)(\D*)(.*)/.exec(h)||["","","",""];if(0==g[0].length&&0==h[0].length)break;b=sa(0==g[1].length?0:parseInt(g[1],10),0==h[1].length?0:parseInt(h[1],10))||sa(0==g[2].length,0==h[2].length)||sa(g[2],h[2]);g=g[3];h=h[3]}while(0==b)}return 0<=b})}var La;
|
||||
La=k.document&&v?Fa():void 0;var x=v&&!(9<=Number(La)),Ma=v&&!(8<=Number(La));function Na(a,b,c,d){this.a=a;this.nodeName=c;this.nodeValue=d;this.nodeType=2;this.parentNode=this.ownerElement=b}function Oa(a,b){var c=Ma&&"href"==b.nodeName?a.getAttribute(b.nodeName,2):b.nodeValue;return new Na(b,a,b.nodeName,c)};function Pa(a){this.b=a;this.a=0}function Qa(a){a=a.match(Ra);for(var b=0;b<a.length;b++)Sa.test(a[b])&&a.splice(b,1);return new Pa(a)}var Ra=/\$?(?:(?![0-9-\.])(?:\*|[\w-\.]+):)?(?![0-9-\.])(?:\*|[\w-\.]+)|\/\/|\.\.|::|\d+(?:\.\d*)?|\.\d+|"[^"]*"|'[^']*'|[!<>]=|\s+|./g,Sa=/^\s/;function y(a,b){return a.b[a.a+(b||0)]}function z(a){return a.b[a.a++]}function Ta(a){return a.b.length<=a.a};function Ua(a,b){this.x=void 0!==a?a:0;this.y=void 0!==b?b:0}Ua.prototype.ceil=function(){this.x=Math.ceil(this.x);this.y=Math.ceil(this.y);return this};Ua.prototype.floor=function(){this.x=Math.floor(this.x);this.y=Math.floor(this.y);return this};Ua.prototype.round=function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this};function Va(a,b){this.width=a;this.height=b}Va.prototype.aspectRatio=function(){return this.width/this.height};Va.prototype.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};Va.prototype.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};Va.prototype.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return this};function Wa(a,b){if(!a||!b)return!1;if(a.contains&&1==b.nodeType)return a==b||a.contains(b);if("undefined"!=typeof a.compareDocumentPosition)return a==b||!!(a.compareDocumentPosition(b)&16);for(;b&&a!=b;)b=b.parentNode;return b==a}
|
||||
function Xa(a,b){if(a==b)return 0;if(a.compareDocumentPosition)return a.compareDocumentPosition(b)&2?1:-1;if(v&&!(9<=Number(La))){if(9==a.nodeType)return-1;if(9==b.nodeType)return 1}if("sourceIndex"in a||a.parentNode&&"sourceIndex"in a.parentNode){var c=1==a.nodeType,d=1==b.nodeType;if(c&&d)return a.sourceIndex-b.sourceIndex;var e=a.parentNode,f=b.parentNode;return e==f?Ya(a,b):!c&&Wa(e,b)?-1*Za(a,b):!d&&Wa(f,a)?Za(b,a):(c?a.sourceIndex:e.sourceIndex)-(d?b.sourceIndex:f.sourceIndex)}d=A(a);c=d.createRange();
|
||||
c.selectNode(a);c.collapse(!0);a=d.createRange();a.selectNode(b);a.collapse(!0);return c.compareBoundaryPoints(k.Range.START_TO_END,a)}function Za(a,b){var c=a.parentNode;if(c==b)return-1;for(;b.parentNode!=c;)b=b.parentNode;return Ya(b,a)}function Ya(a,b){for(;b=b.previousSibling;)if(b==a)return-1;return 1}function A(a){return 9==a.nodeType?a:a.ownerDocument||a.document}function $a(a,b){a&&(a=a.parentNode);for(var c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null}
|
||||
function ab(a){this.a=a||k.document||document}ab.prototype.getElementsByTagName=function(a,b){return(b||this.a).getElementsByTagName(String(a))};function B(a){var b=null,c=a.nodeType;1==c&&(b=a.textContent,b=void 0==b||null==b?a.innerText:b,b=void 0==b||null==b?"":b);if("string"!=typeof b)if(x&&"title"==a.nodeName.toLowerCase()&&1==c)b=a.text;else if(9==c||1==c){a=9==c?a.documentElement:a.firstChild;c=0;var d=[];for(b="";a;){do 1!=a.nodeType&&(b+=a.nodeValue),x&&"title"==a.nodeName.toLowerCase()&&(b+=a.text),d[c++]=a;while(a=a.firstChild);for(;c&&!(a=d[--c].nextSibling););}}else b=a.nodeValue;return b}
|
||||
function C(a,b,c){if(null===b)return!0;try{if(!a.getAttribute)return!1}catch(d){return!1}Ma&&"class"==b&&(b="className");return null==c?!!a.getAttribute(b):a.getAttribute(b,2)==c}function bb(a,b,c,d,e){return(x?cb:db).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new E)}
|
||||
function cb(a,b,c,d,e){if(a instanceof F||8==a.b||c&&null===a.b){var f=b.all;if(!f)return e;a=eb(a);if("*"!=a&&(f=b.getElementsByTagName(a),!f))return e;if(c){for(var g=[],h=0;b=f[h++];)C(b,c,d)&&g.push(b);f=g}for(h=0;b=f[h++];)"*"==a&&"!"==b.tagName||e.add(b);return e}gb(a,b,c,d,e);return e}
|
||||
function db(a,b,c,d,e){b.getElementsByName&&d&&"name"==c&&!v?(b=b.getElementsByName(d),n(b,function(f){a.a(f)&&e.add(f)})):b.getElementsByClassName&&d&&"class"==c?(b=b.getElementsByClassName(d),n(b,function(f){f.className==d&&a.a(f)&&e.add(f)})):a instanceof G?gb(a,b,c,d,e):b.getElementsByTagName&&(b=b.getElementsByTagName(a.f()),n(b,function(f){C(f,c,d)&&e.add(f)}));return e}
|
||||
function hb(a,b,c,d,e){var f;if((a instanceof F||8==a.b||c&&null===a.b)&&(f=b.childNodes)){var g=eb(a);if("*"!=g&&(f=ka(f,function(h){return h.tagName&&h.tagName.toLowerCase()==g}),!f))return e;c&&(f=ka(f,function(h){return C(h,c,d)}));n(f,function(h){"*"==g&&("!"==h.tagName||"*"==g&&1!=h.nodeType)||e.add(h)});return e}return ib(a,b,c,d,e)}function ib(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b);return e}
|
||||
function gb(a,b,c,d,e){for(b=b.firstChild;b;b=b.nextSibling)C(b,c,d)&&a.a(b)&&e.add(b),gb(a,b,c,d,e)}function eb(a){if(a instanceof G){if(8==a.b)return"!";if(null===a.b)return"*"}return a.f()};function E(){this.b=this.a=null;this.l=0}function jb(a){this.f=a;this.a=this.b=null}function kb(a,b){if(!a.a)return b;if(!b.a)return a;var c=a.a;b=b.a;for(var d=null,e,f=0;c&&b;){e=c.f;var g=b.f;e==g||e instanceof Na&&g instanceof Na&&e.a==g.a?(e=c,c=c.a,b=b.a):0<Xa(c.f,b.f)?(e=b,b=b.a):(e=c,c=c.a);(e.b=d)?d.a=e:a.a=e;d=e;f++}for(e=c||b;e;)e.b=d,d=d.a=e,f++,e=e.a;a.b=d;a.l=f;return a}function lb(a,b){b=new jb(b);b.a=a.a;a.b?a.a.b=b:a.a=a.b=b;a.a=b;a.l++}
|
||||
E.prototype.add=function(a){a=new jb(a);a.b=this.b;this.a?this.b.a=a:this.a=this.b=a;this.b=a;this.l++};function mb(a){return(a=a.a)?a.f:null}function nb(a){return(a=mb(a))?B(a):""}function H(a,b){return new ob(a,!!b)}function ob(a,b){this.f=a;this.b=(this.s=b)?a.b:a.a;this.a=null}function I(a){var b=a.b;if(null==b)return null;var c=a.a=b;a.b=a.s?b.b:b.a;return c.f};function J(a){this.i=a;this.b=this.g=!1;this.f=null}function K(a){return"\n "+a.toString().split("\n").join("\n ")}function pb(a,b){a.g=b}function qb(a,b){a.b=b}function N(a,b){a=a.a(b);return a instanceof E?+nb(a):+a}function O(a,b){a=a.a(b);return a instanceof E?nb(a):""+a}function rb(a,b){a=a.a(b);return a instanceof E?!!a.l:!!a};function sb(a,b,c){J.call(this,a.i);this.c=a;this.h=b;this.o=c;this.g=b.g||c.g;this.b=b.b||c.b;this.c==tb&&(c.b||c.g||4==c.i||0==c.i||!b.f?b.b||b.g||4==b.i||0==b.i||!c.f||(this.f={name:c.f.name,u:b}):this.f={name:b.f.name,u:c})}l(sb,J);
|
||||
function ub(a,b,c,d,e){b=b.a(d);c=c.a(d);var f;if(b instanceof E&&c instanceof E){b=H(b);for(d=I(b);d;d=I(b))for(e=H(c),f=I(e);f;f=I(e))if(a(B(d),B(f)))return!0;return!1}if(b instanceof E||c instanceof E){b instanceof E?(e=b,d=c):(e=c,d=b);f=H(e);for(var g=typeof d,h=I(f);h;h=I(f)){switch(g){case "number":h=+B(h);break;case "boolean":h=!!B(h);break;case "string":h=B(h);break;default:throw Error("Illegal primitive type for comparison.");}if(e==b&&a(h,d)||e==c&&a(d,h))return!0}return!1}return e?"boolean"==
|
||||
typeof b||"boolean"==typeof c?a(!!b,!!c):"number"==typeof b||"number"==typeof c?a(+b,+c):a(b,c):a(+b,+c)}sb.prototype.a=function(a){return this.c.m(this.h,this.o,a)};sb.prototype.toString=function(){var a="Binary Expression: "+this.c;a+=K(this.h);return a+=K(this.o)};function vb(a,b,c,d){this.I=a;this.D=b;this.i=c;this.m=d}vb.prototype.toString=function(){return this.I};var wb={};
|
||||
function P(a,b,c,d){if(wb.hasOwnProperty(a))throw Error("Binary operator already created: "+a);a=new vb(a,b,c,d);return wb[a.toString()]=a}P("div",6,1,function(a,b,c){return N(a,c)/N(b,c)});P("mod",6,1,function(a,b,c){return N(a,c)%N(b,c)});P("*",6,1,function(a,b,c){return N(a,c)*N(b,c)});P("+",5,1,function(a,b,c){return N(a,c)+N(b,c)});P("-",5,1,function(a,b,c){return N(a,c)-N(b,c)});P("<",4,2,function(a,b,c){return ub(function(d,e){return d<e},a,b,c)});
|
||||
P(">",4,2,function(a,b,c){return ub(function(d,e){return d>e},a,b,c)});P("<=",4,2,function(a,b,c){return ub(function(d,e){return d<=e},a,b,c)});P(">=",4,2,function(a,b,c){return ub(function(d,e){return d>=e},a,b,c)});var tb=P("=",3,2,function(a,b,c){return ub(function(d,e){return d==e},a,b,c,!0)});P("!=",3,2,function(a,b,c){return ub(function(d,e){return d!=e},a,b,c,!0)});P("and",2,2,function(a,b,c){return rb(a,c)&&rb(b,c)});P("or",1,2,function(a,b,c){return rb(a,c)||rb(b,c)});function xb(a,b){if(b.a.length&&4!=a.i)throw Error("Primary expression must evaluate to nodeset if filter has predicate(s).");J.call(this,a.i);this.c=a;this.h=b;this.g=a.g;this.b=a.b}l(xb,J);xb.prototype.a=function(a){a=this.c.a(a);return yb(this.h,a)};xb.prototype.toString=function(){var a="Filter:"+K(this.c);return a+=K(this.h)};function zb(a,b){if(b.length<a.C)throw Error("Function "+a.j+" expects at least"+a.C+" arguments, "+b.length+" given");if(null!==a.B&&b.length>a.B)throw Error("Function "+a.j+" expects at most "+a.B+" arguments, "+b.length+" given");a.H&&n(b,function(c,d){if(4!=c.i)throw Error("Argument "+d+" to function "+a.j+" is not of type Nodeset: "+c);});J.call(this,a.i);this.v=a;this.c=b;pb(this,a.g||ma(b,function(c){return c.g}));qb(this,a.G&&!b.length||a.F&&!!b.length||ma(b,function(c){return c.b}))}
|
||||
l(zb,J);zb.prototype.a=function(a){return this.v.m.apply(null,pa(a,this.c))};zb.prototype.toString=function(){var a="Function: "+this.v;if(this.c.length){var b=la(this.c,function(c,d){return c+K(d)},"Arguments:");a+=K(b)}return a};function Ab(a,b,c,d,e,f,g,h){this.j=a;this.i=b;this.g=c;this.G=d;this.F=!1;this.m=e;this.C=f;this.B=void 0!==g?g:f;this.H=!!h}Ab.prototype.toString=function(){return this.j};var Bb={};
|
||||
function Q(a,b,c,d,e,f,g,h){if(Bb.hasOwnProperty(a))throw Error("Function already created: "+a+".");Bb[a]=new Ab(a,b,c,d,e,f,g,h)}Q("boolean",2,!1,!1,function(a,b){return rb(b,a)},1);Q("ceiling",1,!1,!1,function(a,b){return Math.ceil(N(b,a))},1);Q("concat",3,!1,!1,function(a,b){return la(qa(arguments,1),function(c,d){return c+O(d,a)},"")},2,null);Q("contains",2,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);return-1!=b.indexOf(a)},2);Q("count",1,!1,!1,function(a,b){return b.a(a).l},1,1,!0);
|
||||
Q("false",2,!1,!1,function(){return!1},0);Q("floor",1,!1,!1,function(a,b){return Math.floor(N(b,a))},1);Q("id",4,!1,!1,function(a,b){function c(h){if(x){var m=e.all[h];if(m){if(m.nodeType&&h==m.id)return m;if(m.length)return oa(m,function(w){return h==w.id})}return null}return e.getElementById(h)}var d=a.a,e=9==d.nodeType?d:d.ownerDocument;a=O(b,a).split(/\s+/);var f=[];n(a,function(h){h=c(h);!h||0<=ja(f,h)||f.push(h)});f.sort(Xa);var g=new E;n(f,function(h){g.add(h)});return g},1);
|
||||
Q("lang",2,!1,!1,function(){return!1},1);Q("last",1,!0,!1,function(a){if(1!=arguments.length)throw Error("Function last expects ()");return a.f},0);Q("local-name",3,!1,!0,function(a,b){return(a=b?mb(b.a(a)):a.a)?a.localName||a.nodeName.toLowerCase():""},0,1,!0);Q("name",3,!1,!0,function(a,b){return(a=b?mb(b.a(a)):a.a)?a.nodeName.toLowerCase():""},0,1,!0);Q("namespace-uri",3,!0,!1,function(){return""},0,1,!0);
|
||||
Q("normalize-space",3,!1,!0,function(a,b){return(b?O(b,a):B(a.a)).replace(/[\s\xa0]+/g," ").replace(/^\s+|\s+$/g,"")},0,1);Q("not",2,!1,!1,function(a,b){return!rb(b,a)},1);Q("number",1,!1,!0,function(a,b){return b?N(b,a):+B(a.a)},0,1);Q("position",1,!0,!1,function(a){return a.b},0);Q("round",1,!1,!1,function(a,b){return Math.round(N(b,a))},1);Q("starts-with",2,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);return 0==b.lastIndexOf(a,0)},2);Q("string",3,!1,!0,function(a,b){return b?O(b,a):B(a.a)},0,1);
|
||||
Q("string-length",1,!1,!0,function(a,b){return(b?O(b,a):B(a.a)).length},0,1);Q("substring",3,!1,!1,function(a,b,c,d){c=N(c,a);if(isNaN(c)||Infinity==c||-Infinity==c)return"";d=d?N(d,a):Infinity;if(isNaN(d)||-Infinity===d)return"";c=Math.round(c)-1;var e=Math.max(c,0);a=O(b,a);return Infinity==d?a.substring(e):a.substring(e,c+Math.round(d))},2,3);Q("substring-after",3,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);c=b.indexOf(a);return-1==c?"":b.substring(c+a.length)},2);
|
||||
Q("substring-before",3,!1,!1,function(a,b,c){b=O(b,a);a=O(c,a);a=b.indexOf(a);return-1==a?"":b.substring(0,a)},2);Q("sum",1,!1,!1,function(a,b){a=H(b.a(a));b=0;for(var c=I(a);c;c=I(a))b+=+B(c);return b},1,1,!0);Q("translate",3,!1,!1,function(a,b,c,d){b=O(b,a);c=O(c,a);var e=O(d,a);a={};for(d=0;d<c.length;d++){var f=c.charAt(d);f in a||(a[f]=e.charAt(d))}c="";for(d=0;d<b.length;d++)f=b.charAt(d),c+=f in a?a[f]:f;return c},3);Q("true",2,!1,!1,function(){return!0},0);function G(a,b){this.h=a;this.c=void 0!==b?b:null;this.b=null;switch(a){case "comment":this.b=8;break;case "text":this.b=3;break;case "processing-instruction":this.b=7;break;case "node":break;default:throw Error("Unexpected argument");}}function Cb(a){return"comment"==a||"text"==a||"processing-instruction"==a||"node"==a}G.prototype.a=function(a){return null===this.b||this.b==a.nodeType};G.prototype.f=function(){return this.h};
|
||||
G.prototype.toString=function(){var a="Kind Test: "+this.h;null===this.c||(a+=K(this.c));return a};function Db(a){J.call(this,3);this.c=a.substring(1,a.length-1)}l(Db,J);Db.prototype.a=function(){return this.c};Db.prototype.toString=function(){return"Literal: "+this.c};function F(a,b){this.j=a.toLowerCase();a="*"==this.j?"*":"http://www.w3.org/1999/xhtml";this.c=b?b.toLowerCase():a}F.prototype.a=function(a){var b=a.nodeType;if(1!=b&&2!=b)return!1;b=void 0!==a.localName?a.localName:a.nodeName;return"*"!=this.j&&this.j!=b.toLowerCase()?!1:"*"==this.c?!0:this.c==(a.namespaceURI?a.namespaceURI.toLowerCase():"http://www.w3.org/1999/xhtml")};F.prototype.f=function(){return this.j};
|
||||
F.prototype.toString=function(){return"Name Test: "+("http://www.w3.org/1999/xhtml"==this.c?"":this.c+":")+this.j};function Eb(a){J.call(this,1);this.c=a}l(Eb,J);Eb.prototype.a=function(){return this.c};Eb.prototype.toString=function(){return"Number: "+this.c};function Fb(a,b){J.call(this,a.i);this.h=a;this.c=b;this.g=a.g;this.b=a.b;1==this.c.length&&(a=this.c[0],a.A||a.c!=Gb||(a=a.o,"*"!=a.f()&&(this.f={name:a.f(),u:null})))}l(Fb,J);function Hb(){J.call(this,4)}l(Hb,J);Hb.prototype.a=function(a){var b=new E;a=a.a;9==a.nodeType?b.add(a):b.add(a.ownerDocument);return b};Hb.prototype.toString=function(){return"Root Helper Expression"};function Ib(){J.call(this,4)}l(Ib,J);Ib.prototype.a=function(a){var b=new E;b.add(a.a);return b};Ib.prototype.toString=function(){return"Context Helper Expression"};
|
||||
function Jb(a){return"/"==a||"//"==a}Fb.prototype.a=function(a){var b=this.h.a(a);if(!(b instanceof E))throw Error("Filter expression must evaluate to nodeset.");a=this.c;for(var c=0,d=a.length;c<d&&b.l;c++){var e=a[c],f=H(b,e.c.s);if(e.g||e.c!=Kb)if(e.g||e.c!=Lb){var g=I(f);for(b=e.a(new ia(g));null!=(g=I(f));)g=e.a(new ia(g)),b=kb(b,g)}else g=I(f),b=e.a(new ia(g));else{for(g=I(f);(b=I(f))&&(!g.contains||g.contains(b))&&b.compareDocumentPosition(g)&8;g=b);b=e.a(new ia(g))}}return b};
|
||||
Fb.prototype.toString=function(){var a="Path Expression:"+K(this.h);if(this.c.length){var b=la(this.c,function(c,d){return c+K(d)},"Steps:");a+=K(b)}return a};function Mb(a,b){this.a=a;this.s=!!b}
|
||||
function yb(a,b,c){for(c=c||0;c<a.a.length;c++)for(var d=a.a[c],e=H(b),f=b.l,g,h=0;g=I(e);h++){var m=a.s?f-h:h+1;g=d.a(new ia(g,m,f));if("number"==typeof g)m=m==g;else if("string"==typeof g||"boolean"==typeof g)m=!!g;else if(g instanceof E)m=0<g.l;else throw Error("Predicate.evaluate returned an unexpected type.");if(!m){m=e;g=m.f;var w=m.a;if(!w)throw Error("Next must be called at least once before remove.");var r=w.b;w=w.a;r?r.a=w:g.a=w;w?w.b=r:g.b=r;g.l--;m.a=null}}return b}
|
||||
Mb.prototype.toString=function(){return la(this.a,function(a,b){return a+K(b)},"Predicates:")};function R(a,b,c,d){J.call(this,4);this.c=a;this.o=b;this.h=c||new Mb([]);this.A=!!d;b=this.h;b=0<b.a.length?b.a[0].f:null;a.J&&b&&(a=b.name,a=x?a.toLowerCase():a,this.f={name:a,u:b.u});a:{a=this.h;for(b=0;b<a.a.length;b++)if(c=a.a[b],c.g||1==c.i||0==c.i){a=!0;break a}a=!1}this.g=a}l(R,J);
|
||||
R.prototype.a=function(a){var b=a.a,c=this.f,d=null,e=null,f=0;c&&(d=c.name,e=c.u?O(c.u,a):null,f=1);if(this.A)if(this.g||this.c!=Nb)if(b=H((new R(Ob,new G("node"))).a(a)),c=I(b))for(a=this.m(c,d,e,f);null!=(c=I(b));)a=kb(a,this.m(c,d,e,f));else a=new E;else a=bb(this.o,b,d,e),a=yb(this.h,a,f);else a=this.m(a.a,d,e,f);return a};R.prototype.m=function(a,b,c,d){a=this.c.v(this.o,a,b,c);return a=yb(this.h,a,d)};
|
||||
R.prototype.toString=function(){var a="Step:"+K("Operator: "+(this.A?"//":"/"));this.c.j&&(a+=K("Axis: "+this.c));a+=K(this.o);if(this.h.a.length){var b=la(this.h.a,function(c,d){return c+K(d)},"Predicates:");a+=K(b)}return a};function Pb(a,b,c,d){this.j=a;this.v=b;this.s=c;this.J=d}Pb.prototype.toString=function(){return this.j};var Qb={};function S(a,b,c,d){if(Qb.hasOwnProperty(a))throw Error("Axis already created: "+a);b=new Pb(a,b,c,!!d);return Qb[a]=b}
|
||||
S("ancestor",function(a,b){for(var c=new E;b=b.parentNode;)a.a(b)&&lb(c,b);return c},!0);S("ancestor-or-self",function(a,b){var c=new E;do a.a(b)&&lb(c,b);while(b=b.parentNode);return c},!0);
|
||||
var Gb=S("attribute",function(a,b){var c=new E,d=a.f();if("style"==d&&x&&b.style)return c.add(new Na(b.style,b,"style",b.style.cssText)),c;var e=b.attributes;if(e)if(a instanceof G&&null===a.b||"*"==d)for(a=0;d=e[a];a++)x?d.nodeValue&&c.add(Oa(b,d)):c.add(d);else(d=e.getNamedItem(d))&&(x?d.nodeValue&&c.add(Oa(b,d)):c.add(d));return c},!1),Nb=S("child",function(a,b,c,d,e){return(x?hb:ib).call(null,a,b,aa(c)?c:null,aa(d)?d:null,e||new E)},!1,!0);S("descendant",bb,!1,!0);
|
||||
var Ob=S("descendant-or-self",function(a,b,c,d){var e=new E;C(b,c,d)&&a.a(b)&&e.add(b);return bb(a,b,c,d,e)},!1,!0),Kb=S("following",function(a,b,c,d){var e=new E;do for(var f=b;f=f.nextSibling;)C(f,c,d)&&a.a(f)&&e.add(f),e=bb(a,f,c,d,e);while(b=b.parentNode);return e},!1,!0);S("following-sibling",function(a,b){for(var c=new E;b=b.nextSibling;)a.a(b)&&c.add(b);return c},!1);S("namespace",function(){return new E},!1);
|
||||
var Rb=S("parent",function(a,b){var c=new E;if(9==b.nodeType)return c;if(2==b.nodeType)return c.add(b.ownerElement),c;b=b.parentNode;a.a(b)&&c.add(b);return c},!1),Lb=S("preceding",function(a,b,c,d){var e=new E,f=[];do f.unshift(b);while(b=b.parentNode);for(var g=1,h=f.length;g<h;g++){var m=[];for(b=f[g];b=b.previousSibling;)m.unshift(b);for(var w=0,r=m.length;w<r;w++)b=m[w],C(b,c,d)&&a.a(b)&&e.add(b),e=bb(a,b,c,d,e)}return e},!0,!0);
|
||||
S("preceding-sibling",function(a,b){for(var c=new E;b=b.previousSibling;)a.a(b)&&lb(c,b);return c},!0);var Sb=S("self",function(a,b){var c=new E;a.a(b)&&c.add(b);return c},!1);function Tb(a){J.call(this,1);this.c=a;this.g=a.g;this.b=a.b}l(Tb,J);Tb.prototype.a=function(a){return-N(this.c,a)};Tb.prototype.toString=function(){return"Unary Expression: -"+K(this.c)};function Ub(a){J.call(this,4);this.c=a;pb(this,ma(this.c,function(b){return b.g}));qb(this,ma(this.c,function(b){return b.b}))}l(Ub,J);Ub.prototype.a=function(a){var b=new E;n(this.c,function(c){c=c.a(a);if(!(c instanceof E))throw Error("Path expression must evaluate to NodeSet.");b=kb(b,c)});return b};Ub.prototype.toString=function(){return la(this.c,function(a,b){return a+K(b)},"Union Expression:")};function Vb(a,b){this.a=a;this.b=b}function Yb(a){for(var b,c=[];;){T(a,"Missing right hand side of binary expression.");b=Zb(a);var d=z(a.a);if(!d)break;var e=(d=wb[d]||null)&&d.D;if(!e){a.a.a--;break}for(;c.length&&e<=c[c.length-1].D;)b=new sb(c.pop(),c.pop(),b);c.push(b,d)}for(;c.length;)b=new sb(c.pop(),c.pop(),b);return b}function T(a,b){if(Ta(a.a))throw Error(b);}function $b(a,b){a=z(a.a);if(a!=b)throw Error("Bad token, expected: "+b+" got: "+a);}
|
||||
function ac(a){a=z(a.a);if(")"!=a)throw Error("Bad token: "+a);}function bc(a){a=z(a.a);if(2>a.length)throw Error("Unclosed literal string");return new Db(a)}
|
||||
function cc(a){var b=[];if(Jb(y(a.a))){var c=z(a.a);var d=y(a.a);if("/"==c&&(Ta(a.a)||"."!=d&&".."!=d&&"@"!=d&&"*"!=d&&!/(?![0-9])[\w]/.test(d)))return new Hb;d=new Hb;T(a,"Missing next location step.");c=dc(a,c);b.push(c)}else{a:{c=y(a.a);d=c.charAt(0);switch(d){case "$":throw Error("Variable reference not allowed in HTML XPath");case "(":z(a.a);c=Yb(a);T(a,'unclosed "("');$b(a,")");break;case '"':case "'":c=bc(a);break;default:if(isNaN(+c))if(!Cb(c)&&/(?![0-9])[\w]/.test(d)&&"("==y(a.a,1)){c=z(a.a);
|
||||
c=Bb[c]||null;z(a.a);for(d=[];")"!=y(a.a);){T(a,"Missing function argument list.");d.push(Yb(a));if(","!=y(a.a))break;z(a.a)}T(a,"Unclosed function argument list.");ac(a);c=new zb(c,d)}else{c=null;break a}else c=new Eb(+z(a.a))}"["==y(a.a)&&(d=new Mb(ec(a)),c=new xb(c,d))}if(c)if(Jb(y(a.a)))d=c;else return c;else c=dc(a,"/"),d=new Ib,b.push(c)}for(;Jb(y(a.a));)c=z(a.a),T(a,"Missing next location step."),c=dc(a,c),b.push(c);return new Fb(d,b)}
|
||||
function dc(a,b){if("/"!=b&&"//"!=b)throw Error('Step op should be "/" or "//"');if("."==y(a.a)){var c=new R(Sb,new G("node"));z(a.a);return c}if(".."==y(a.a))return c=new R(Rb,new G("node")),z(a.a),c;if("@"==y(a.a)){var d=Gb;z(a.a);T(a,"Missing attribute name")}else if("::"==y(a.a,1)){if(!/(?![0-9])[\w]/.test(y(a.a).charAt(0)))throw Error("Bad token: "+z(a.a));var e=z(a.a);d=Qb[e]||null;if(!d)throw Error("No axis with name: "+e);z(a.a);T(a,"Missing node name")}else d=Nb;e=y(a.a);if(/(?![0-9])[\w\*]/.test(e.charAt(0)))if("("==
|
||||
y(a.a,1)){if(!Cb(e))throw Error("Invalid node type: "+e);e=z(a.a);if(!Cb(e))throw Error("Invalid type name: "+e);$b(a,"(");T(a,"Bad nodetype");var f=y(a.a).charAt(0),g=null;if('"'==f||"'"==f)g=bc(a);T(a,"Bad nodetype");ac(a);e=new G(e,g)}else if(e=z(a.a),f=e.indexOf(":"),-1==f)e=new F(e);else{g=e.substring(0,f);if("*"==g)var h="*";else if(h=a.b(g),!h)throw Error("Namespace prefix not declared: "+g);e=e.substr(f+1);e=new F(e,h)}else throw Error("Bad token: "+z(a.a));a=new Mb(ec(a),d.s);return c||new R(d,
|
||||
e,a,"//"==b)}function ec(a){for(var b=[];"["==y(a.a);){z(a.a);T(a,"Missing predicate expression.");var c=Yb(a);b.push(c);T(a,"Unclosed predicate expression.");$b(a,"]")}return b}function Zb(a){if("-"==y(a.a))return z(a.a),new Tb(Zb(a));var b=cc(a);if("|"!=y(a.a))a=b;else{for(b=[b];"|"==z(a.a);)T(a,"Missing next union location path."),b.push(cc(a));a.a.a--;a=new Ub(b)}return a};function fc(a){switch(a.nodeType){case 1:return ha(gc,a);case 9:return fc(a.documentElement);case 11:case 10:case 6:case 12:return hc;default:return a.parentNode?fc(a.parentNode):hc}}function hc(){return null}function gc(a,b){if(a.prefix==b)return a.namespaceURI||"http://www.w3.org/1999/xhtml";var c=a.getAttributeNode("xmlns:"+b);return c&&c.specified?c.value||null:a.parentNode&&9!=a.parentNode.nodeType?gc(a.parentNode,b):null};function ic(a,b){if(!a.length)throw Error("Empty XPath expression.");a=Qa(a);if(Ta(a))throw Error("Invalid XPath expression.");b?"function"==ca(b)||(b=fa(b.lookupNamespaceURI,b)):b=function(){return null};var c=Yb(new Vb(a,b));if(!Ta(a))throw Error("Bad token: "+z(a));this.evaluate=function(d,e){d=c.a(new ia(d));return new U(d,e)}}
|
||||
function U(a,b){if(0==b)if(a instanceof E)b=4;else if("string"==typeof a)b=2;else if("number"==typeof a)b=1;else if("boolean"==typeof a)b=3;else throw Error("Unexpected evaluation result.");if(2!=b&&1!=b&&3!=b&&!(a instanceof E))throw Error("value could not be converted to the specified type");this.resultType=b;switch(b){case 2:this.stringValue=a instanceof E?nb(a):""+a;break;case 1:this.numberValue=a instanceof E?+nb(a):+a;break;case 3:this.booleanValue=a instanceof E?0<a.l:!!a;break;case 4:case 5:case 6:case 7:var c=
|
||||
H(a);var d=[];for(var e=I(c);e;e=I(c))d.push(e instanceof Na?e.a:e);this.snapshotLength=a.l;this.invalidIteratorState=!1;break;case 8:case 9:a=mb(a);this.singleNodeValue=a instanceof Na?a.a:a;break;default:throw Error("Unknown XPathResult type.");}var f=0;this.iterateNext=function(){if(4!=b&&5!=b)throw Error("iterateNext called with wrong result type");return f>=d.length?null:d[f++]};this.snapshotItem=function(g){if(6!=b&&7!=b)throw Error("snapshotItem called with wrong result type");return g>=d.length||
|
||||
0>g?null:d[g]}}U.ANY_TYPE=0;U.NUMBER_TYPE=1;U.STRING_TYPE=2;U.BOOLEAN_TYPE=3;U.UNORDERED_NODE_ITERATOR_TYPE=4;U.ORDERED_NODE_ITERATOR_TYPE=5;U.UNORDERED_NODE_SNAPSHOT_TYPE=6;U.ORDERED_NODE_SNAPSHOT_TYPE=7;U.ANY_UNORDERED_NODE_TYPE=8;U.FIRST_ORDERED_NODE_TYPE=9;function jc(a){this.lookupNamespaceURI=fc(a)}
|
||||
function kc(a,b){a=a||k;var c=a.Document&&a.Document.prototype||a.document;if(!c.evaluate||b)a.XPathResult=U,c.evaluate=function(d,e,f,g){return(new ic(d,f)).evaluate(e,g)},c.createExpression=function(d,e){return new ic(d,e)},c.createNSResolver=function(d){return new jc(d)}}ba("wgxpath.install",kc);ba("wgxpath.install",kc);var lc={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgreen:"#006400",
|
||||
darkgrey:"#a9a9a9",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",
|
||||
ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",green:"#008000",greenyellow:"#adff2f",grey:"#808080",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgreen:"#90ee90",lightgrey:"#d3d3d3",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",
|
||||
lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370db",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",
|
||||
moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#db7093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",
|
||||
seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"};var mc="backgroundColor borderTopColor borderRightColor borderBottomColor borderLeftColor color outlineColor".split(" "),nc=/#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])/,oc=/^#(?:[0-9a-f]{3}){1,2}$/i,pc=/^(?:rgba)?\((\d{1,3}),\s?(\d{1,3}),\s?(\d{1,3}),\s?(0|1|0\.\d*)\)$/i,qc=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i;function rc(a,b){this.code=a;this.a=V[a]||sc;this.message=b||"";a=this.a.replace(/((?:^|\s+)[a-z])/g,function(c){return c.toUpperCase().replace(/^[\s\xa0]+/g,"")});b=a.length-5;if(0>b||a.indexOf("Error",b)!=b)a+="Error";this.name=a;a=Error(this.message);a.name=this.name;this.stack=a.stack||""}l(rc,Error);var sc="unknown error",V={15:"element not selectable",11:"element not visible"};V[31]=sc;V[30]=sc;V[24]="invalid cookie domain";V[29]="invalid element coordinates";V[12]="invalid element state";
|
||||
V[32]="invalid selector";V[51]="invalid selector";V[52]="invalid selector";V[17]="javascript error";V[405]="unsupported operation";V[34]="move target out of bounds";V[27]="no such alert";V[7]="no such element";V[8]="no such frame";V[23]="no such window";V[28]="script timeout";V[33]="session not created";V[10]="stale element reference";V[21]="timeout";V[25]="unable to set cookie";V[26]="unexpected alert open";V[13]=sc;V[9]="unknown command";var tc=va(),uc=ya()||u("iPod"),vc=u("iPad"),wc=u("Android")&&!(wa()||va()||u("Opera")||u("Silk")),xc=wa(),yc=u("Safari")&&!(wa()||u("Coast")||u("Opera")||u("Edge")||u("Edg/")||u("OPR")||va()||u("Silk")||u("Android"))&&!(ya()||u("iPad")||u("iPod"));function zc(a){return(a=a.exec(t))?a[1]:""}(function(){if(tc)return zc(/Firefox\/([0-9.]+)/);if(v||Ca||Ba)return Ga;if(xc)return ya()||u("iPad")||u("iPod")?zc(/CriOS\/([0-9.]+)/):zc(/Chrome\/([0-9.]+)/);if(yc&&!(ya()||u("iPad")||u("iPod")))return zc(/Version\/([0-9.]+)/);if(uc||vc){var a=/Version\/(\S+).*Mobile\/(\S+)/.exec(t);if(a)return a[1]+"."+a[2]}else if(wc)return(a=zc(/Android\s+([0-9.]+)/))?a:zc(/Version\/([0-9.]+)/);return""})();var Ac=v&&!(9<=Number(La));function W(a,b){b&&"string"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)};var Bc=function(){var a={K:"http://www.w3.org/2000/svg"};return function(b){return a[b]||null}}();
|
||||
function Cc(a,b){var c=A(a);if(!c.documentElement)return null;(v||wc)&&kc(c?c.parentWindow||c.defaultView:window);try{var d=c.createNSResolver?c.createNSResolver(c.documentElement):Bc;if(v&&!Ka(7))return c.evaluate.call(c,b,a,d,9,null);if(!v||9<=Number(La)){for(var e={},f=c.getElementsByTagName("*"),g=0;g<f.length;++g){var h=f[g],m=h.namespaceURI;if(m&&!e[m]){var w=h.lookupPrefix(m);if(!w){var r=m.match(".*/(\\w+)/?$");w=r?r[1]:"xhtml"}e[m]=w}}var D={},L;for(L in e)D[e[L]]=L;d=function(M){return D[M]||
|
||||
null}}try{return c.evaluate(b,a,d,9,null)}catch(M){if("TypeError"===M.name)return d=c.createNSResolver?c.createNSResolver(c.documentElement):Bc,c.evaluate(b,a,d,9,null);throw M;}}catch(M){if(!Da||"NS_ERROR_ILLEGAL_VALUE"!=M.name)throw new rc(32,"Unable to locate an element with the xpath expression "+b+" because of the following error:\n"+M);}}
|
||||
function Dc(a,b){var c=function(){var d=Cc(b,a);return d?d.singleNodeValue||null:b.selectSingleNode?(d=A(b),d.setProperty&&d.setProperty("SelectionLanguage","XPath"),b.selectSingleNode(a)):null}();if(null!==c&&(!c||1!=c.nodeType))throw new rc(32,'The result of the xpath expression "'+a+'" is: '+c+". It should be an element.");return c};function Ec(a,b,c,d){this.c=a;this.a=b;this.b=c;this.f=d}Ec.prototype.ceil=function(){this.c=Math.ceil(this.c);this.a=Math.ceil(this.a);this.b=Math.ceil(this.b);this.f=Math.ceil(this.f);return this};Ec.prototype.floor=function(){this.c=Math.floor(this.c);this.a=Math.floor(this.a);this.b=Math.floor(this.b);this.f=Math.floor(this.f);return this};Ec.prototype.round=function(){this.c=Math.round(this.c);this.a=Math.round(this.a);this.b=Math.round(this.b);this.f=Math.round(this.f);return this};function X(a,b,c,d){this.a=a;this.b=b;this.width=c;this.height=d}X.prototype.ceil=function(){this.a=Math.ceil(this.a);this.b=Math.ceil(this.b);this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return this};X.prototype.floor=function(){this.a=Math.floor(this.a);this.b=Math.floor(this.b);this.width=Math.floor(this.width);this.height=Math.floor(this.height);return this};
|
||||
X.prototype.round=function(){this.a=Math.round(this.a);this.b=Math.round(this.b);this.width=Math.round(this.width);this.height=Math.round(this.height);return this};var Fc="function"===typeof ShadowRoot;function Gc(a){for(a=a.parentNode;a&&1!=a.nodeType&&9!=a.nodeType&&11!=a.nodeType;)a=a.parentNode;return W(a)?a:null}
|
||||
function Y(a,b){b=xa(b);if("float"==b||"cssFloat"==b||"styleFloat"==b)b=Ac?"styleFloat":"cssFloat";a:{var c=b;var d=A(a);if(d.defaultView&&d.defaultView.getComputedStyle&&(d=d.defaultView.getComputedStyle(a,null))){c=d[c]||d.getPropertyValue(c)||"";break a}c=""}a=c||Hc(a,b);if(null===a)a=null;else if(0<=ja(mc,b)){b:{var e=a.match(pc);if(e&&(b=Number(e[1]),c=Number(e[2]),d=Number(e[3]),e=Number(e[4]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d&&0<=e&&1>=e)){b=[b,c,d,e];break b}b=null}if(!b)b:{if(d=a.match(qc))if(b=
|
||||
Number(d[1]),c=Number(d[2]),d=Number(d[3]),0<=b&&255>=b&&0<=c&&255>=c&&0<=d&&255>=d){b=[b,c,d,1];break b}b=null}if(!b)b:{b=a.toLowerCase();c=lc[b.toLowerCase()];if(!c&&(c="#"==b.charAt(0)?b:"#"+b,4==c.length&&(c=c.replace(nc,"#$1$1$2$2$3$3")),!oc.test(c))){b=null;break b}b=[parseInt(c.substr(1,2),16),parseInt(c.substr(3,2),16),parseInt(c.substr(5,2),16),1]}a=b?"rgba("+b.join(", ")+")":a}return a}
|
||||
function Hc(a,b){var c=a.currentStyle||a.style,d=c[b];void 0===d&&"function"==ca(c.getPropertyValue)&&(d=c.getPropertyValue(b));return"inherit"!=d?void 0!==d?d:null:(a=Gc(a))?Hc(a,b):null}
|
||||
function Ic(a,b,c){function d(g){var h=Jc(g);return 0<h.height&&0<h.width?!0:W(g,"PATH")&&(0<h.height||0<h.width)?(g=Y(g,"stroke-width"),!!g&&0<parseInt(g,10)):"hidden"!=Y(g,"overflow")&&ma(g.childNodes,function(m){return 3==m.nodeType||W(m)&&d(m)})}function e(g){return Kc(g)==Z&&na(g.childNodes,function(h){return!W(h)||e(h)||!d(h)})}if(!W(a))throw Error("Argument to isShown must be of type Element");if(W(a,"BODY"))return!0;if(W(a,"OPTION")||W(a,"OPTGROUP"))return a=$a(a,function(g){return W(g,"SELECT")}),
|
||||
!!a&&Ic(a,!0,c);var f=Lc(a);if(f)return!!f.image&&0<f.rect.width&&0<f.rect.height&&Ic(f.image,b,c);if(W(a,"INPUT")&&"hidden"==a.type.toLowerCase()||W(a,"NOSCRIPT"))return!1;f=Y(a,"visibility");return"collapse"!=f&&"hidden"!=f&&c(a)&&(b||0!=Mc(a))&&d(a)?!e(a):!1}var Z="hidden";
|
||||
function Kc(a){function b(p){function q(fb){if(fb==g)return!0;var Wb=Y(fb,"display");return 0==Wb.lastIndexOf("inline",0)||"contents"==Wb||"absolute"==Xb&&"static"==Y(fb,"position")?!1:!0}var Xb=Y(p,"position");if("fixed"==Xb)return w=!0,p==g?null:g;for(p=Gc(p);p&&!q(p);)p=Gc(p);return p}function c(p){var q=p;if("visible"==m)if(p==g&&h)q=h;else if(p==h)return{x:"visible",y:"visible"};q={x:Y(q,"overflow-x"),y:Y(q,"overflow-y")};p==g&&(q.x="visible"==q.x?"auto":q.x,q.y="visible"==q.y?"auto":q.y);return q}
|
||||
function d(p){if(p==g){var q=(new ab(f)).a;p=q.scrollingElement?q.scrollingElement:Ea||"CSS1Compat"!=q.compatMode?q.body||q.documentElement:q.documentElement;q=q.parentWindow||q.defaultView;p=v&&Ka("10")&&q.pageYOffset!=p.scrollTop?new Ua(p.scrollLeft,p.scrollTop):new Ua(q.pageXOffset||p.scrollLeft,q.pageYOffset||p.scrollTop)}else p=new Ua(p.scrollLeft,p.scrollTop);return p}var e=Nc(a),f=A(a),g=f.documentElement,h=f.body,m=Y(g,"overflow"),w;for(a=b(a);a;a=b(a)){var r=c(a);if("visible"!=r.x||"visible"!=
|
||||
r.y){var D=Jc(a);if(0==D.width||0==D.height)return Z;var L=e.a<D.a,M=e.b<D.b;if(L&&"hidden"==r.x||M&&"hidden"==r.y)return Z;if(L&&"visible"!=r.x||M&&"visible"!=r.y){L=d(a);M=e.b<D.b-L.y;if(e.a<D.a-L.x&&"visible"!=r.x||M&&"visible"!=r.x)return Z;e=Kc(a);return e==Z?Z:"scroll"}L=e.f>=D.a+D.width;D=e.c>=D.b+D.height;if(L&&"hidden"==r.x||D&&"hidden"==r.y)return Z;if(L&&"visible"!=r.x||D&&"visible"!=r.y){if(w&&(r=d(a),e.f>=g.scrollWidth-r.x||e.a>=g.scrollHeight-r.y))return Z;e=Kc(a);return e==Z?Z:"scroll"}}}return"none"}
|
||||
function Jc(a){var b=Lc(a);if(b)return b.rect;if(W(a,"HTML"))return a=A(a),a=((a?a.parentWindow||a.defaultView:window)||window).document,a="CSS1Compat"==a.compatMode?a.documentElement:a.body,a=new Va(a.clientWidth,a.clientHeight),new X(0,0,a.width,a.height);try{var c=a.getBoundingClientRect()}catch(d){return new X(0,0,0,0)}b=new X(c.left,c.top,c.right-c.left,c.bottom-c.top);v&&a.ownerDocument.body&&(a=A(a),b.a-=a.documentElement.clientLeft+a.body.clientLeft,b.b-=a.documentElement.clientTop+a.body.clientTop);
|
||||
return b}function Lc(a){var b=W(a,"MAP");if(!b&&!W(a,"AREA"))return null;var c=b?a:W(a.parentNode,"MAP")?a.parentNode:null,d=null,e=null;c&&c.name&&(d=Dc('/descendant::*[@usemap = "#'+c.name+'"]',A(c)))&&(e=Jc(d),b||"default"==a.shape.toLowerCase()||(a=Oc(a),b=Math.min(Math.max(a.a,0),e.width),c=Math.min(Math.max(a.b,0),e.height),e=new X(b+e.a,c+e.b,Math.min(a.width,e.width-b),Math.min(a.height,e.height-c))));return{image:d,rect:e||new X(0,0,0,0)}}
|
||||
function Oc(a){var b=a.shape.toLowerCase();a=a.coords.split(",");if("rect"==b&&4==a.length){b=a[0];var c=a[1];return new X(b,c,a[2]-b,a[3]-c)}if("circle"==b&&3==a.length)return b=a[2],new X(a[0]-b,a[1]-b,2*b,2*b);if("poly"==b&&2<a.length){b=a[0];c=a[1];for(var d=b,e=c,f=2;f+1<a.length;f+=2)b=Math.min(b,a[f]),d=Math.max(d,a[f]),c=Math.min(c,a[f+1]),e=Math.max(e,a[f+1]);return new X(b,c,d-b,e-c)}return new X(0,0,0,0)}function Nc(a){a=Jc(a);return new Ec(a.b,a.a+a.width,a.b+a.height,a.a)}
|
||||
function Mc(a){if(Ac){if("relative"==Y(a,"position"))return 1;a=Y(a,"filter");return(a=a.match(/^alpha\(opacity=(\d*)\)/)||a.match(/^progid:DXImageTransform.Microsoft.Alpha\(Opacity=(\d*)\)/))?Number(a[1])/100:1}return Pc(a)}function Pc(a){var b=1,c=Y(a,"opacity");c&&(b=Number(c));(a=Gc(a))&&(b*=Pc(a));return b};ba("_",function(a,b){function c(d){if(W(d)&&"none"==Y(d,"display"))return!1;var e;if((e=d.parentNode)&&e.shadowRoot&&void 0!==d.assignedSlot)e=d.assignedSlot?d.assignedSlot.parentNode:null;else if(d.getDestinationInsertionPoints){var f=d.getDestinationInsertionPoints();0<f.length&&(e=f[f.length-1])}if(Fc&&e instanceof ShadowRoot){if(e.host.shadowRoot!==e)return!1;e=e.host}return!e||9!=e.nodeType&&11!=e.nodeType?e&&W(e,"DETAILS")&&!e.open&&!W(d,"SUMMARY")?!1:!!e&&c(e):!0}return Ic(a,!!b,c)});; return this._.apply(null,arguments);}).apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);};
|
||||
55
node_modules/selenium-webdriver/lib/atoms/mutation-listener.js
generated
vendored
Normal file
55
node_modules/selenium-webdriver/lib/atoms/mutation-listener.js
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
(function () {
|
||||
const observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
switch (mutation.type) {
|
||||
case 'attributes':
|
||||
// Don't report our own attribute has changed.
|
||||
if (mutation.attributeName === "data-__webdriver_id") {
|
||||
break;
|
||||
}
|
||||
const curr = mutation.target.getAttribute(mutation.attributeName);
|
||||
var id = mutation.target.dataset.__webdriver_id
|
||||
if (!id) {
|
||||
id = Math.random().toString(36).substring(2) + Date.now().toString(36);
|
||||
mutation.target.dataset.__webdriver_id = id;
|
||||
}
|
||||
const json = JSON.stringify({
|
||||
'target': id,
|
||||
'name': mutation.attributeName,
|
||||
'value': curr,
|
||||
'oldValue': mutation.oldValue
|
||||
});
|
||||
__webdriver_attribute(json);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document, {
|
||||
'attributes': true,
|
||||
'attributeOldValue': true,
|
||||
'characterData': true,
|
||||
'characterDataOldValue': true,
|
||||
'childList': true,
|
||||
'subtree': true
|
||||
});
|
||||
})();
|
||||
432
node_modules/selenium-webdriver/lib/by.js
generated
vendored
Normal file
432
node_modules/selenium-webdriver/lib/by.js
generated
vendored
Normal file
@@ -0,0 +1,432 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @fileoverview Factory methods for the supported locator strategies.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Short-hand expressions for the primary element locator strategies.
|
||||
* For example the following two statements are equivalent:
|
||||
*
|
||||
* var e1 = driver.findElement(By.id('foo'));
|
||||
* var e2 = driver.findElement({id: 'foo'});
|
||||
*
|
||||
* Care should be taken when using JavaScript minifiers (such as the
|
||||
* Closure compiler), as locator hashes will always be parsed using
|
||||
* the un-obfuscated properties listed.
|
||||
*
|
||||
* @typedef {(
|
||||
* {className: string}|
|
||||
* {css: string}|
|
||||
* {id: string}|
|
||||
* {js: string}|
|
||||
* {linkText: string}|
|
||||
* {name: string}|
|
||||
* {partialLinkText: string}|
|
||||
* {tagName: string}|
|
||||
* {xpath: string})} ByHash
|
||||
*/
|
||||
|
||||
/**
|
||||
* Error thrown if an invalid character is encountered while escaping a CSS
|
||||
* identifier.
|
||||
* @see https://drafts.csswg.org/cssom/#serialize-an-identifier
|
||||
*/
|
||||
class InvalidCharacterError extends Error {
|
||||
constructor() {
|
||||
super()
|
||||
this.name = this.constructor.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a CSS string.
|
||||
* @param {string} css the string to escape.
|
||||
* @return {string} the escaped string.
|
||||
* @throws {TypeError} if the input value is not a string.
|
||||
* @throws {InvalidCharacterError} if the string contains an invalid character.
|
||||
* @see https://drafts.csswg.org/cssom/#serialize-an-identifier
|
||||
*/
|
||||
function escapeCss(css) {
|
||||
if (typeof css !== 'string') {
|
||||
throw new TypeError('input must be a string')
|
||||
}
|
||||
let ret = ''
|
||||
const n = css.length
|
||||
for (let i = 0; i < n; i++) {
|
||||
const c = css.charCodeAt(i)
|
||||
if (c == 0x0) {
|
||||
throw new InvalidCharacterError()
|
||||
}
|
||||
|
||||
if (
|
||||
(c >= 0x0001 && c <= 0x001f) ||
|
||||
c == 0x007f ||
|
||||
(i == 0 && c >= 0x0030 && c <= 0x0039) ||
|
||||
(i == 1 && c >= 0x0030 && c <= 0x0039 && css.charCodeAt(0) == 0x002d)
|
||||
) {
|
||||
ret += '\\' + c.toString(16) + ' '
|
||||
continue
|
||||
}
|
||||
|
||||
if (i == 0 && c == 0x002d && n == 1) {
|
||||
ret += '\\' + css.charAt(i)
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
c >= 0x0080 ||
|
||||
c == 0x002d || // -
|
||||
c == 0x005f || // _
|
||||
(c >= 0x0030 && c <= 0x0039) || // [0-9]
|
||||
(c >= 0x0041 && c <= 0x005a) || // [A-Z]
|
||||
(c >= 0x0061 && c <= 0x007a)
|
||||
) {
|
||||
// [a-z]
|
||||
ret += css.charAt(i)
|
||||
continue
|
||||
}
|
||||
|
||||
ret += '\\' + css.charAt(i)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a mechanism for locating an element on the page.
|
||||
* @final
|
||||
*/
|
||||
class By {
|
||||
/**
|
||||
* @param {string} using the name of the location strategy to use.
|
||||
* @param {string} value the value to search for.
|
||||
*/
|
||||
constructor(using, value) {
|
||||
/** @type {string} */
|
||||
this.using = using
|
||||
|
||||
/** @type {string} */
|
||||
this.value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements that have a specific class name.
|
||||
*
|
||||
* @param {string} name The class name to search for.
|
||||
* @return {!By} The new locator.
|
||||
* @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
|
||||
* @see http://www.w3.org/TR/CSS2/selector.html#class-html
|
||||
*/
|
||||
static className(name) {
|
||||
let names = name
|
||||
.split(/\s+/g)
|
||||
.filter((s) => s.length > 0)
|
||||
.map((s) => escapeCss(s))
|
||||
return By.css('.' + names.join('.'))
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements using a CSS selector.
|
||||
*
|
||||
* @param {string} selector The CSS selector to use.
|
||||
* @return {!By} The new locator.
|
||||
* @see http://www.w3.org/TR/CSS2/selector.html
|
||||
*/
|
||||
static css(selector) {
|
||||
return new By('css selector', selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements by the ID attribute. This locator uses the CSS selector
|
||||
* `*[id="$ID"]`, _not_ `document.getElementById`.
|
||||
*
|
||||
* @param {string} id The ID to search for.
|
||||
* @return {!By} The new locator.
|
||||
*/
|
||||
static id(id) {
|
||||
return By.css('*[id="' + escapeCss(id) + '"]')
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates link elements whose
|
||||
* {@linkplain webdriver.WebElement#getText visible text} matches the given
|
||||
* string.
|
||||
*
|
||||
* @param {string} text The link text to search for.
|
||||
* @return {!By} The new locator.
|
||||
*/
|
||||
static linkText(text) {
|
||||
return new By('link text', text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements by evaluating a `script` that defines the body of
|
||||
* a {@linkplain webdriver.WebDriver#executeScript JavaScript function}.
|
||||
* The return value of this function must be an element or an array-like
|
||||
* list of elements. When this locator returns a list of elements, but only
|
||||
* one is expected, the first element in this list will be used as the
|
||||
* single element value.
|
||||
*
|
||||
* @param {!(string|Function)} script The script to execute.
|
||||
* @param {...*} var_args The arguments to pass to the script.
|
||||
* @return {function(!./webdriver.WebDriver): !Promise}
|
||||
* A new JavaScript-based locator function.
|
||||
*/
|
||||
static js(script, ...var_args) {
|
||||
return function (driver) {
|
||||
return driver.executeScript.call(driver, script, ...var_args)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements whose `name` attribute has the given value.
|
||||
*
|
||||
* @param {string} name The name attribute to search for.
|
||||
* @return {!By} The new locator.
|
||||
*/
|
||||
static name(name) {
|
||||
return By.css('*[name="' + escapeCss(name) + '"]')
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates link elements whose
|
||||
* {@linkplain webdriver.WebElement#getText visible text} contains the given
|
||||
* substring.
|
||||
*
|
||||
* @param {string} text The substring to check for in a link's visible text.
|
||||
* @return {!By} The new locator.
|
||||
*/
|
||||
static partialLinkText(text) {
|
||||
return new By('partial link text', text)
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements with a given tag name.
|
||||
*
|
||||
* @param {string} name The tag name to search for.
|
||||
* @return {!By} The new locator.
|
||||
*/
|
||||
static tagName(name) {
|
||||
return new By('tag name', name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates elements matching a XPath selector. Care should be taken when
|
||||
* using an XPath selector with a {@link webdriver.WebElement} as WebDriver
|
||||
* will respect the context in the specified in the selector. For example,
|
||||
* given the selector `//div`, WebDriver will search from the document root
|
||||
* regardless of whether the locator was used with a WebElement.
|
||||
*
|
||||
* @param {string} xpath The XPath selector to use.
|
||||
* @return {!By} The new locator.
|
||||
* @see http://www.w3.org/TR/xpath/
|
||||
*/
|
||||
static xpath(xpath) {
|
||||
return new By('xpath', xpath)
|
||||
}
|
||||
|
||||
/** @override */
|
||||
toString() {
|
||||
// The static By.name() overrides this.constructor.name. Shame...
|
||||
return `By(${this.using}, ${this.value})`
|
||||
}
|
||||
|
||||
toObject() {
|
||||
const tmp = {}
|
||||
tmp[this.using] = this.value
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start Searching for relative objects using the value returned from
|
||||
* `By.tagName()`.
|
||||
*
|
||||
* Note: this method will likely be removed in the future please use
|
||||
* `locateWith`.
|
||||
* @param {By} The value returned from calling By.tagName()
|
||||
* @returns
|
||||
*/
|
||||
function withTagName(tagName) {
|
||||
return new RelativeBy({ 'css selector': tagName })
|
||||
}
|
||||
|
||||
/**
|
||||
* Start searching for relative objects using search criteria with By.
|
||||
* @param {string} A By map that shows how to find the initial element
|
||||
* @returns {RelativeBy}
|
||||
*/
|
||||
function locateWith(by) {
|
||||
return new RelativeBy(getLocator(by))
|
||||
}
|
||||
|
||||
function getLocator(locatorOrElement) {
|
||||
let toFind
|
||||
if (locatorOrElement instanceof By) {
|
||||
toFind = locatorOrElement.toObject()
|
||||
} else {
|
||||
toFind = locatorOrElement
|
||||
}
|
||||
return toFind
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a mechanism for locating an element relative to others
|
||||
* on the page.
|
||||
* @final
|
||||
*/
|
||||
class RelativeBy {
|
||||
/**
|
||||
* @param {By} findDetails
|
||||
* @param {Array<Object>} filters
|
||||
*/
|
||||
constructor(findDetails, filters = null) {
|
||||
this.root = findDetails
|
||||
this.filters = filters || []
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for elements above the root element passed in
|
||||
* @param {string|WebElement} locatorOrElement
|
||||
* @return {!RelativeBy} Return this object
|
||||
*/
|
||||
above(locatorOrElement) {
|
||||
this.filters.push({
|
||||
kind: 'above',
|
||||
args: [getLocator(locatorOrElement)],
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for elements below the root element passed in
|
||||
* @param {string|WebElement} locatorOrElement
|
||||
* @return {!RelativeBy} Return this object
|
||||
*/
|
||||
below(locatorOrElement) {
|
||||
this.filters.push({
|
||||
kind: 'below',
|
||||
args: [getLocator(locatorOrElement)],
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for elements left the root element passed in
|
||||
* @param {string|WebElement} locatorOrElement
|
||||
* @return {!RelativeBy} Return this object
|
||||
*/
|
||||
toLeftOf(locatorOrElement) {
|
||||
this.filters.push({
|
||||
kind: 'left',
|
||||
args: [getLocator(locatorOrElement)],
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for elements right the root element passed in
|
||||
* @param {string|WebElement} locatorOrElement
|
||||
* @return {!RelativeBy} Return this object
|
||||
*/
|
||||
toRightOf(locatorOrElement) {
|
||||
this.filters.push({
|
||||
kind: 'right',
|
||||
args: [getLocator(locatorOrElement)],
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for elements near the root element passed in
|
||||
* @param {string|WebElement} locatorOrElement
|
||||
* @return {!RelativeBy} Return this object
|
||||
*/
|
||||
near(locatorOrElement) {
|
||||
this.filters.push({
|
||||
kind: 'near',
|
||||
args: [getLocator(locatorOrElement)],
|
||||
})
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a marshalled version of the {@link RelativeBy}
|
||||
* @return {!Object} Object representation of a {@link WebElement}
|
||||
* that will be used in {@link #findElements}.
|
||||
*/
|
||||
marshall() {
|
||||
return {
|
||||
relative: {
|
||||
root: this.root,
|
||||
filters: this.filters,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
toString() {
|
||||
// The static By.name() overrides this.constructor.name. Shame...
|
||||
return `RelativeBy(${JSON.stringify(this.marshall())})`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a value is a valid locator.
|
||||
* @param {!(By|Function|ByHash)} locator The value to check.
|
||||
* @return {!(By|Function)} The valid locator.
|
||||
* @throws {TypeError} If the given value does not define a valid locator
|
||||
* strategy.
|
||||
*/
|
||||
function check(locator) {
|
||||
if (locator instanceof By || typeof locator === 'function') {
|
||||
return locator
|
||||
}
|
||||
|
||||
if (
|
||||
locator &&
|
||||
typeof locator === 'object' &&
|
||||
typeof locator.using === 'string' &&
|
||||
typeof locator.value === 'string'
|
||||
) {
|
||||
return new By(locator.using, locator.value)
|
||||
}
|
||||
|
||||
for (let key in locator) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(locator, key) &&
|
||||
Object.prototype.hasOwnProperty.call(By, key)
|
||||
) {
|
||||
return By[key](locator[key])
|
||||
}
|
||||
}
|
||||
throw new TypeError('Invalid locator')
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
By,
|
||||
RelativeBy,
|
||||
withTagName,
|
||||
locateWith,
|
||||
escapeCss,
|
||||
checkedLocator: check,
|
||||
}
|
||||
554
node_modules/selenium-webdriver/lib/capabilities.js
generated
vendored
Normal file
554
node_modules/selenium-webdriver/lib/capabilities.js
generated
vendored
Normal file
@@ -0,0 +1,554 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @fileoverview Defines types related to describing the capabilities of a
|
||||
* WebDriver session.
|
||||
*/
|
||||
|
||||
const Symbols = require('./symbols')
|
||||
|
||||
/**
|
||||
* Recognized browser names.
|
||||
* @enum {string}
|
||||
*/
|
||||
const Browser = {
|
||||
CHROME: 'chrome',
|
||||
EDGE: 'MicrosoftEdge',
|
||||
FIREFOX: 'firefox',
|
||||
INTERNET_EXPLORER: 'internet explorer',
|
||||
SAFARI: 'safari',
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategies for waiting for [document readiness] after a navigation
|
||||
* event.
|
||||
*
|
||||
* [document readiness]: https://html.spec.whatwg.org/#current-document-readiness
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
const PageLoadStrategy = {
|
||||
/**
|
||||
* Indicates WebDriver should not wait on the document readiness state after a
|
||||
* navigation event.
|
||||
*/
|
||||
NONE: 'none',
|
||||
|
||||
/**
|
||||
* Indicates WebDriver should wait for the document readiness state to
|
||||
* become "interactive" after navigation.
|
||||
*/
|
||||
EAGER: 'eager',
|
||||
|
||||
/**
|
||||
* Indicates WebDriver should wait for the document readiness state to
|
||||
* be "complete" after navigation. This is the default page loading strategy.
|
||||
*/
|
||||
NORMAL: 'normal',
|
||||
}
|
||||
|
||||
/**
|
||||
* Common platform names. These platforms are not explicitly defined by the
|
||||
* WebDriver spec, however, their use is encouraged for interoperability.
|
||||
*
|
||||
* @enum {string}
|
||||
* @see <https://w3c.github.io/webdriver/webdriver-spec.html>
|
||||
*/
|
||||
const Platform = {
|
||||
LINUX: 'linux',
|
||||
MAC: 'mac',
|
||||
WINDOWS: 'windows',
|
||||
}
|
||||
|
||||
/**
|
||||
* Record object defining the timeouts that apply to certain WebDriver actions.
|
||||
*
|
||||
* @record
|
||||
*/
|
||||
function Timeouts() {}
|
||||
|
||||
/**
|
||||
* Defines when, in milliseconds, to interrupt a script that is being
|
||||
* {@linkplain ./webdriver.IWebDriver#executeScript evaluated}.
|
||||
* @type {number}
|
||||
*/
|
||||
Timeouts.prototype.script
|
||||
|
||||
/**
|
||||
* The timeout, in milliseconds, to apply to navigation events along with the
|
||||
* {@link PageLoadStrategy}.
|
||||
* @type {number}
|
||||
*/
|
||||
Timeouts.prototype.pageLoad
|
||||
|
||||
/**
|
||||
* The maximum amount of time, in milliseconds, to spend attempting to
|
||||
* {@linkplain ./webdriver.IWebDriver#findElement locate} an element on the
|
||||
* current page.
|
||||
* @type {number}
|
||||
*/
|
||||
Timeouts.prototype.implicit
|
||||
|
||||
/**
|
||||
* The possible default actions a WebDriver session can take to respond to
|
||||
* unhandled user prompts (`window.alert()`, `window.confirm()`, and
|
||||
* `window.prompt()`).
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
const UserPromptHandler = {
|
||||
/** All prompts should be silently accepted. */
|
||||
ACCEPT: 'accept',
|
||||
/** All prompts should be silently dismissed. */
|
||||
DISMISS: 'dismiss',
|
||||
/**
|
||||
* All prompts should be automatically accepted, but an error should be
|
||||
* returned to the next (or currently executing) WebDriver command.
|
||||
*/
|
||||
ACCEPT_AND_NOTIFY: 'accept and notify',
|
||||
/**
|
||||
* All prompts should be automatically dismissed, but an error should be
|
||||
* returned to the next (or currently executing) WebDriver command.
|
||||
*/
|
||||
DISMISS_AND_NOTIFY: 'dismiss and notify',
|
||||
/** All prompts should be left unhandled. */
|
||||
IGNORE: 'ignore',
|
||||
}
|
||||
|
||||
/**
|
||||
* The standard WebDriver capability keys.
|
||||
*
|
||||
* @enum {string}
|
||||
* @see <https://w3c.github.io/webdriver/webdriver-spec.html#capabilities>
|
||||
*/
|
||||
const Capability = {
|
||||
/**
|
||||
* Indicates whether a WebDriver session implicitly trusts otherwise untrusted
|
||||
* and self-signed TLS certificates during navigation.
|
||||
*/
|
||||
ACCEPT_INSECURE_TLS_CERTS: 'acceptInsecureCerts',
|
||||
|
||||
/**
|
||||
* The browser name. Common browser names are defined in the
|
||||
* {@link ./capabilities.Browser Browser} enum.
|
||||
*/
|
||||
BROWSER_NAME: 'browserName',
|
||||
|
||||
/** Identifies the browser version. */
|
||||
BROWSER_VERSION: 'browserVersion',
|
||||
|
||||
/**
|
||||
* Key for the logging driver logging preferences.
|
||||
* The browser name. Common browser names are defined in the
|
||||
* {@link ./capabilities.Browser Browser} enum.
|
||||
*/
|
||||
LOGGING_PREFS: 'goog:loggingPrefs',
|
||||
|
||||
/**
|
||||
* Defines the session's
|
||||
* {@linkplain ./capabilities.PageLoadStrategy page loading strategy}.
|
||||
*/
|
||||
PAGE_LOAD_STRATEGY: 'pageLoadStrategy',
|
||||
|
||||
/**
|
||||
* Identifies the operating system of the endpoint node. Common values
|
||||
* recognized by the most WebDriver server implementations are predefined in
|
||||
* the {@link ./capabilities.Platform Platform} enum.
|
||||
*/
|
||||
PLATFORM_NAME: 'platformName',
|
||||
|
||||
/**
|
||||
* Describes the proxy configuration to use for a new WebDriver session.
|
||||
*/
|
||||
PROXY: 'proxy',
|
||||
|
||||
/**
|
||||
* Indicates whether the remote end supports all of the window resizing and
|
||||
* positioning commands:
|
||||
*
|
||||
* - {@linkplain ./webdriver.Window#getRect Window.getRect()}
|
||||
* - {@linkplain ./webdriver.Window#setRect Window.setRect()}
|
||||
* - {@linkplain ./webdriver.Window#maximize Window.maximize()}
|
||||
* - {@linkplain ./webdriver.Window#minimize Window.minimize()}
|
||||
* - {@linkplain ./webdriver.Window#fullscreen Window.fullscreen()}
|
||||
*/
|
||||
SET_WINDOW_RECT: 'setWindowRect',
|
||||
|
||||
/**
|
||||
* Describes the {@linkplain ./capabilities.Timeouts timeouts} imposed on
|
||||
* certain session operations.
|
||||
*/
|
||||
TIMEOUTS: 'timeouts',
|
||||
|
||||
/**
|
||||
* Defines how a WebDriver session should
|
||||
* {@linkplain ./capabilities.UserPromptHandler respond} to unhandled user
|
||||
* prompts.
|
||||
*/
|
||||
UNHANDLED_PROMPT_BEHAVIOR: 'unhandledPromptBehavior',
|
||||
|
||||
/**
|
||||
* Defines the current session’s strict file interactability.
|
||||
* Used to upload a file when strict file interactability is on
|
||||
*/
|
||||
STRICT_FILE_INTERACTABILITY: 'strictFileInteractability',
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a generic hash object to a map.
|
||||
* @param {!Object<string, ?>} hash The hash object.
|
||||
* @return {!Map<string, ?>} The converted map.
|
||||
*/
|
||||
function toMap(hash) {
|
||||
let m = new Map()
|
||||
for (let key in hash) {
|
||||
if (Object.prototype.hasOwnProperty.call(hash, key)) {
|
||||
m.set(key, hash[key])
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a set of capabilities for a WebDriver session.
|
||||
*/
|
||||
class Capabilities {
|
||||
/**
|
||||
* @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
|
||||
* capabilities to initialize this instance from.
|
||||
*/
|
||||
constructor(other = undefined) {
|
||||
if (other instanceof Capabilities) {
|
||||
other = other.map_
|
||||
} else if (other && !(other instanceof Map)) {
|
||||
other = toMap(other)
|
||||
}
|
||||
/** @private @const {!Map<string, ?>} */
|
||||
this.map_ = new Map(other)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Capabilities} A basic set of capabilities for Chrome.
|
||||
*/
|
||||
static chrome() {
|
||||
return new Capabilities().setBrowserName(Browser.CHROME)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Capabilities} A basic set of capabilities for Microsoft Edge.
|
||||
*/
|
||||
static edge() {
|
||||
return new Capabilities().setBrowserName(Browser.EDGE)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Capabilities} A basic set of capabilities for Firefox.
|
||||
*/
|
||||
static firefox() {
|
||||
return new Capabilities()
|
||||
.setBrowserName(Browser.FIREFOX)
|
||||
.set('moz:debuggerAddress', true)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Capabilities} A basic set of capabilities for Internet Explorer.
|
||||
*/
|
||||
static ie() {
|
||||
return new Capabilities().setBrowserName(Browser.INTERNET_EXPLORER)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Capabilities} A basic set of capabilities for Safari.
|
||||
*/
|
||||
static safari() {
|
||||
return new Capabilities().setBrowserName(Browser.SAFARI)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object<string, ?>} The JSON representation of this instance.
|
||||
* Note, the returned object may contain nested promised values.
|
||||
* @suppress {checkTypes} Suppress [] access on a struct (state inherited from
|
||||
* Map).
|
||||
*/
|
||||
[Symbols.serialize]() {
|
||||
return serialize(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key the parameter key to get.
|
||||
* @return {T} the stored parameter value.
|
||||
* @template T
|
||||
*/
|
||||
get(key) {
|
||||
return this.map_.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key the key to test.
|
||||
* @return {boolean} whether this capability set has the specified key.
|
||||
*/
|
||||
has(key) {
|
||||
return this.map_.has(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Iterator<string>} an iterator of the keys set.
|
||||
*/
|
||||
keys() {
|
||||
return this.map_.keys()
|
||||
}
|
||||
|
||||
/** @return {number} The number of capabilities set. */
|
||||
get size() {
|
||||
return this.map_.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges another set of capabilities into this instance.
|
||||
* @param {!(Capabilities|Map<String, ?>|Object<string, ?>)} other The other
|
||||
* set of capabilities to merge.
|
||||
* @return {!Capabilities} A self reference.
|
||||
*/
|
||||
merge(other) {
|
||||
if (other) {
|
||||
let otherMap
|
||||
if (other instanceof Capabilities) {
|
||||
otherMap = other.map_
|
||||
} else if (other instanceof Map) {
|
||||
otherMap = other
|
||||
} else {
|
||||
otherMap = toMap(other)
|
||||
}
|
||||
otherMap.forEach((value, key) => {
|
||||
this.set(key, value)
|
||||
})
|
||||
return this
|
||||
} else {
|
||||
throw new TypeError('no capabilities provided for merge')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entry from this set of capabilities.
|
||||
*
|
||||
* @param {string} key the capability key to delete.
|
||||
*/
|
||||
delete(key) {
|
||||
this.map_.delete(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key The capability key.
|
||||
* @param {*} value The capability value.
|
||||
* @return {!Capabilities} A self reference.
|
||||
* @throws {TypeError} If the `key` is not a string.
|
||||
*/
|
||||
set(key, value) {
|
||||
if (typeof key !== 'string') {
|
||||
throw new TypeError('Capability keys must be strings: ' + typeof key)
|
||||
}
|
||||
this.map_.set(key, value)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether a WebDriver session should implicitly accept self-signed, or
|
||||
* other untrusted TLS certificates on navigation.
|
||||
*
|
||||
* @param {boolean} accept whether to accept insecure certs.
|
||||
* @return {!Capabilities} a self reference.
|
||||
*/
|
||||
setAcceptInsecureCerts(accept) {
|
||||
return this.set(Capability.ACCEPT_INSECURE_TLS_CERTS, accept)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {boolean} whether the session is configured to accept insecure
|
||||
* TLS certificates.
|
||||
*/
|
||||
getAcceptInsecureCerts() {
|
||||
return this.get(Capability.ACCEPT_INSECURE_TLS_CERTS)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the target browser.
|
||||
*
|
||||
* @param {(Browser|string)} name the browser name.
|
||||
* @return {!Capabilities} a self reference.
|
||||
*/
|
||||
setBrowserName(name) {
|
||||
return this.set(Capability.BROWSER_NAME, name)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(string|undefined)} the configured browser name, or undefined if
|
||||
* not set.
|
||||
*/
|
||||
getBrowserName() {
|
||||
return this.get(Capability.BROWSER_NAME)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired version of the target browser.
|
||||
*
|
||||
* @param {string} version the desired version.
|
||||
* @return {!Capabilities} a self reference.
|
||||
*/
|
||||
setBrowserVersion(version) {
|
||||
return this.set(Capability.BROWSER_VERSION, version)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(string|undefined)} the configured browser version, or undefined
|
||||
* if not set.
|
||||
*/
|
||||
getBrowserVersion() {
|
||||
return this.get(Capability.BROWSER_VERSION)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired page loading strategy for a new WebDriver session.
|
||||
*
|
||||
* @param {PageLoadStrategy} strategy the desired strategy.
|
||||
* @return {!Capabilities} a self reference.
|
||||
*/
|
||||
setPageLoadStrategy(strategy) {
|
||||
return this.set(Capability.PAGE_LOAD_STRATEGY, strategy)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured page load strategy.
|
||||
*
|
||||
* @return {(string|undefined)} the page load strategy.
|
||||
*/
|
||||
getPageLoadStrategy() {
|
||||
return this.get(Capability.PAGE_LOAD_STRATEGY)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the target platform.
|
||||
*
|
||||
* @param {(Platform|string)} platform the target platform.
|
||||
* @return {!Capabilities} a self reference.
|
||||
*/
|
||||
setPlatform(platform) {
|
||||
return this.set(Capability.PLATFORM_NAME, platform)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(string|undefined)} the configured platform or undefined if not
|
||||
* set.
|
||||
*/
|
||||
getPlatform() {
|
||||
return this.get(Capability.PLATFORM_NAME)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logging preferences. Preferences may be specified as a
|
||||
* {@link ./logging.Preferences} instance, or as a map of log-type to
|
||||
* log-level.
|
||||
* @param {!(./logging.Preferences|Object<string>)} prefs The logging
|
||||
* preferences.
|
||||
* @return {!Capabilities} A self reference.
|
||||
*/
|
||||
setLoggingPrefs(prefs) {
|
||||
return this.set(Capability.LOGGING_PREFS, prefs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the proxy configuration for this instance.
|
||||
* @param {proxy.Config} proxy The desired proxy configuration.
|
||||
* @return {!Capabilities} A self reference.
|
||||
*/
|
||||
setProxy(proxy) {
|
||||
return this.set(Capability.PROXY, proxy)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(proxy.Config|undefined)} the configured proxy settings, or
|
||||
* undefined if not set.
|
||||
*/
|
||||
getProxy() {
|
||||
return this.get(Capability.PROXY)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default action to take with an unexpected alert before returning
|
||||
* an error. If unspecified, WebDriver will default to
|
||||
* {@link UserPromptHandler.DISMISS_AND_NOTIFY}.
|
||||
*
|
||||
* @param {?UserPromptHandler} behavior The way WebDriver should respond to
|
||||
* unhandled user prompts.
|
||||
* @return {!Capabilities} A self reference.
|
||||
*/
|
||||
setAlertBehavior(behavior) {
|
||||
return this.set(Capability.UNHANDLED_PROMPT_BEHAVIOR, behavior)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(UserPromptHandler|undefined)} the behavior pattern for responding
|
||||
* to unhandled user prompts, or undefined if not set.
|
||||
*/
|
||||
getAlertBehavior() {
|
||||
return this.get(Capability.UNHANDLED_PROMPT_BEHAVIOR)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the boolean flag configuration for this instance.
|
||||
*/
|
||||
setStrictFileInteractability(strictFileInteractability) {
|
||||
return this.set(
|
||||
Capability.STRICT_FILE_INTERACTABILITY,
|
||||
strictFileInteractability
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a capabilities object. This is defined as a standalone function
|
||||
* so it may be type checked (where Capabilities[Symbols.serialize] has type
|
||||
* checking disabled since it is defined with [] access on a struct).
|
||||
*
|
||||
* @param {!Capabilities} caps The capabilities to serialize.
|
||||
* @return {!Object<string, ?>} The JSON representation of this instance.
|
||||
* Note, the returned object may contain nested promised values.
|
||||
*/
|
||||
function serialize(caps) {
|
||||
let ret = {}
|
||||
for (let key of caps.keys()) {
|
||||
let cap = caps.get(key)
|
||||
if (cap !== undefined && cap !== null) {
|
||||
ret[key] = cap
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Browser,
|
||||
Capabilities,
|
||||
Capability,
|
||||
PageLoadStrategy,
|
||||
Platform,
|
||||
Timeouts,
|
||||
UserPromptHandler,
|
||||
}
|
||||
210
node_modules/selenium-webdriver/lib/command.js
generated
vendored
Normal file
210
node_modules/selenium-webdriver/lib/command.js
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Contains several classes for handling commands.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Describes a command to execute.
|
||||
* @final
|
||||
*/
|
||||
class Command {
|
||||
/** @param {string} name The name of this command. */
|
||||
constructor(name) {
|
||||
/** @private {string} */
|
||||
this.name_ = name
|
||||
|
||||
/** @private {!Object<*>} */
|
||||
this.parameters_ = {}
|
||||
}
|
||||
|
||||
/** @return {string} This command's name. */
|
||||
getName() {
|
||||
return this.name_
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a parameter to send with this command.
|
||||
* @param {string} name The parameter name.
|
||||
* @param {*} value The parameter value.
|
||||
* @return {!Command} A self reference.
|
||||
*/
|
||||
setParameter(name, value) {
|
||||
this.parameters_[name] = value
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parameters for this command.
|
||||
* @param {!Object<*>} parameters The command parameters.
|
||||
* @return {!Command} A self reference.
|
||||
*/
|
||||
setParameters(parameters) {
|
||||
this.parameters_ = parameters
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a named command parameter.
|
||||
* @param {string} key The parameter key to look up.
|
||||
* @return {*} The parameter value, or undefined if it has not been set.
|
||||
*/
|
||||
getParameter(key) {
|
||||
return this.parameters_[key]
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Object<*>} The parameters to send with this command.
|
||||
*/
|
||||
getParameters() {
|
||||
return this.parameters_
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enumeration of predefined names command names that all command processors
|
||||
* will support.
|
||||
* @enum {string}
|
||||
*/
|
||||
const Name = {
|
||||
GET_SERVER_STATUS: 'getStatus',
|
||||
|
||||
NEW_SESSION: 'newSession',
|
||||
GET_SESSIONS: 'getSessions',
|
||||
|
||||
CLOSE: 'close',
|
||||
QUIT: 'quit',
|
||||
|
||||
GET_CURRENT_URL: 'getCurrentUrl',
|
||||
GET: 'get',
|
||||
GO_BACK: 'goBack',
|
||||
GO_FORWARD: 'goForward',
|
||||
REFRESH: 'refresh',
|
||||
|
||||
ADD_COOKIE: 'addCookie',
|
||||
GET_COOKIE: 'getCookie',
|
||||
GET_ALL_COOKIES: 'getCookies',
|
||||
DELETE_COOKIE: 'deleteCookie',
|
||||
DELETE_ALL_COOKIES: 'deleteAllCookies',
|
||||
|
||||
GET_ACTIVE_ELEMENT: 'getActiveElement',
|
||||
FIND_ELEMENT: 'findElement',
|
||||
FIND_ELEMENTS: 'findElements',
|
||||
FIND_ELEMENTS_RELATIVE: 'findElementsRelative',
|
||||
FIND_CHILD_ELEMENT: 'findChildElement',
|
||||
FIND_CHILD_ELEMENTS: 'findChildElements',
|
||||
|
||||
CLEAR_ELEMENT: 'clearElement',
|
||||
CLICK_ELEMENT: 'clickElement',
|
||||
SEND_KEYS_TO_ELEMENT: 'sendKeysToElement',
|
||||
|
||||
GET_CURRENT_WINDOW_HANDLE: 'getCurrentWindowHandle',
|
||||
GET_WINDOW_HANDLES: 'getWindowHandles',
|
||||
GET_WINDOW_RECT: 'getWindowRect',
|
||||
SET_WINDOW_RECT: 'setWindowRect',
|
||||
MAXIMIZE_WINDOW: 'maximizeWindow',
|
||||
MINIMIZE_WINDOW: 'minimizeWindow',
|
||||
FULLSCREEN_WINDOW: 'fullscreenWindow',
|
||||
|
||||
SWITCH_TO_WINDOW: 'switchToWindow',
|
||||
SWITCH_TO_NEW_WINDOW: 'newWindow',
|
||||
SWITCH_TO_FRAME: 'switchToFrame',
|
||||
SWITCH_TO_FRAME_PARENT: 'switchToFrameParent',
|
||||
GET_PAGE_SOURCE: 'getPageSource',
|
||||
GET_TITLE: 'getTitle',
|
||||
|
||||
EXECUTE_SCRIPT: 'executeScript',
|
||||
EXECUTE_ASYNC_SCRIPT: 'executeAsyncScript',
|
||||
|
||||
GET_ELEMENT_TEXT: 'getElementText',
|
||||
GET_COMPUTED_ROLE: 'getAriaRole',
|
||||
GET_COMPUTED_LABEL: 'getAccessibleName',
|
||||
GET_ELEMENT_TAG_NAME: 'getElementTagName',
|
||||
IS_ELEMENT_SELECTED: 'isElementSelected',
|
||||
IS_ELEMENT_ENABLED: 'isElementEnabled',
|
||||
IS_ELEMENT_DISPLAYED: 'isElementDisplayed',
|
||||
GET_ELEMENT_RECT: 'getElementRect',
|
||||
GET_ELEMENT_ATTRIBUTE: 'getElementAttribute',
|
||||
GET_DOM_ATTRIBUTE: 'getDomAttribute',
|
||||
GET_ELEMENT_VALUE_OF_CSS_PROPERTY: 'getElementValueOfCssProperty',
|
||||
GET_ELEMENT_PROPERTY: 'getElementProperty',
|
||||
|
||||
SCREENSHOT: 'screenshot',
|
||||
TAKE_ELEMENT_SCREENSHOT: 'takeElementScreenshot',
|
||||
|
||||
PRINT_PAGE: 'printPage',
|
||||
|
||||
GET_TIMEOUT: 'getTimeout',
|
||||
SET_TIMEOUT: 'setTimeout',
|
||||
|
||||
ACCEPT_ALERT: 'acceptAlert',
|
||||
DISMISS_ALERT: 'dismissAlert',
|
||||
GET_ALERT_TEXT: 'getAlertText',
|
||||
SET_ALERT_TEXT: 'setAlertValue',
|
||||
|
||||
// Shadow DOM Commands
|
||||
GET_SHADOW_ROOT: 'getShadowRoot',
|
||||
FIND_ELEMENT_FROM_SHADOWROOT: 'findElementFromShadowRoot',
|
||||
FIND_ELEMENTS_FROM_SHADOWROOT: 'findElementsFromShadowRoot',
|
||||
|
||||
// Virtual Authenticator Commands
|
||||
ADD_VIRTUAL_AUTHENTICATOR: 'addVirtualAuthenticator',
|
||||
REMOVE_VIRTUAL_AUTHENTICATOR: 'removeVirtualAuthenticator',
|
||||
ADD_CREDENTIAL: 'addCredential',
|
||||
GET_CREDENTIALS: 'getCredentials',
|
||||
REMOVE_CREDENTIAL: 'removeCredential',
|
||||
REMOVE_ALL_CREDENTIALS: 'removeAllCredentials',
|
||||
SET_USER_VERIFIED: 'setUserVerified',
|
||||
|
||||
GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes',
|
||||
GET_LOG: 'getLog',
|
||||
|
||||
// Non-standard commands used by the standalone Selenium server.
|
||||
UPLOAD_FILE: 'uploadFile',
|
||||
|
||||
ACTIONS: 'actions',
|
||||
CLEAR_ACTIONS: 'clearActions',
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the execution of WebDriver {@link Command commands}.
|
||||
* @record
|
||||
*/
|
||||
class Executor {
|
||||
/**
|
||||
* Executes the given {@code command}. If there is an error executing the
|
||||
* command, the provided callback will be invoked with the offending error.
|
||||
* Otherwise, the callback will be invoked with a null Error and non-null
|
||||
* response object.
|
||||
*
|
||||
* @param {!Command} command The command to execute.
|
||||
* @return {!Promise<?>} A promise that will be fulfilled with the command
|
||||
* result.
|
||||
*/
|
||||
execute(command) {} // eslint-disable-line
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Command,
|
||||
Name,
|
||||
Executor,
|
||||
}
|
||||
610
node_modules/selenium-webdriver/lib/error.js
generated
vendored
Normal file
610
node_modules/selenium-webdriver/lib/error.js
generated
vendored
Normal file
@@ -0,0 +1,610 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const { isObject } = require('./util')
|
||||
|
||||
/**
|
||||
* The base WebDriver error type. This error type is only used directly when a
|
||||
* more appropriate category is not defined for the offending error.
|
||||
*/
|
||||
class WebDriverError extends Error {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
|
||||
/** @override */
|
||||
this.name = this.constructor.name
|
||||
|
||||
/**
|
||||
* A stacktrace reported by the remote webdriver endpoint that initially
|
||||
* reported this error. This property will be an empty string if the remote
|
||||
* end did not provide a stacktrace.
|
||||
* @type {string}
|
||||
*/
|
||||
this.remoteStacktrace = ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the shadow root is no longer attached to the DOM
|
||||
*/
|
||||
class DetachedShadowRootError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a {@linkplain ./webdriver.WebElement#click click command} could not
|
||||
* completed because the click target is obscured by other elements on the
|
||||
* page.
|
||||
*/
|
||||
class ElementClickInterceptedError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An attempt was made to select an element that cannot be selected.
|
||||
*/
|
||||
class ElementNotSelectableError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a command could not be completed because the target element is
|
||||
* not pointer or keyboard interactable. This will often occur if an element
|
||||
* is present in the DOM, but not rendered (i.e. its CSS style has
|
||||
* "display: none").
|
||||
*/
|
||||
class ElementNotInteractableError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a navigation event caused the browser to generate a certificate
|
||||
* warning. This is usually caused by an expired or invalid TLS certificate.
|
||||
*/
|
||||
class InsecureCertificateError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The arguments passed to a command are either invalid or malformed.
|
||||
*/
|
||||
class InvalidArgumentError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An illegal attempt was made to set a cookie under a different domain than
|
||||
* the current page.
|
||||
*/
|
||||
class InvalidCookieDomainError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The coordinates provided to an interactions operation are invalid.
|
||||
*/
|
||||
class InvalidCoordinatesError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An element command could not be completed because the element is in an
|
||||
* invalid state, e.g. attempting to click an element that is no longer attached
|
||||
* to the document.
|
||||
*/
|
||||
class InvalidElementStateError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Argument was an invalid selector.
|
||||
*/
|
||||
class InvalidSelectorError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Occurs when a command is directed to a session that does not exist.
|
||||
*/
|
||||
class NoSuchSessionError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An error occurred while executing JavaScript supplied by the user.
|
||||
*/
|
||||
class JavascriptError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The target for mouse interaction is not in the browser’s viewport and cannot
|
||||
* be brought into that viewport.
|
||||
*/
|
||||
class MoveTargetOutOfBoundsError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An attempt was made to operate on a modal dialog when one was not open.
|
||||
*/
|
||||
class NoSuchAlertError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a named cookie could not be found in the cookie jar for the
|
||||
* currently selected document.
|
||||
*/
|
||||
class NoSuchCookieError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An element could not be located on the page using the given search
|
||||
* parameters.
|
||||
*/
|
||||
class NoSuchElementError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A ShadowRoot could not be located on the element
|
||||
*/
|
||||
class NoSuchShadowRootError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request to switch to a frame could not be satisfied because the frame
|
||||
* could not be found.
|
||||
*/
|
||||
class NoSuchFrameError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request to switch to a window could not be satisfied because the window
|
||||
* could not be found.
|
||||
*/
|
||||
class NoSuchWindowError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A script did not complete before its timeout expired.
|
||||
*/
|
||||
class ScriptTimeoutError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A new session could not be created.
|
||||
*/
|
||||
class SessionNotCreatedError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An element command failed because the referenced element is no longer
|
||||
* attached to the DOM.
|
||||
*/
|
||||
class StaleElementReferenceError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An operation did not complete before its timeout expired.
|
||||
*/
|
||||
class TimeoutError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A request to set a cookie’s value could not be satisfied.
|
||||
*/
|
||||
class UnableToSetCookieError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A screen capture operation was not possible.
|
||||
*/
|
||||
class UnableToCaptureScreenError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A modal dialog was open, blocking this operation.
|
||||
*/
|
||||
class UnexpectedAlertOpenError extends WebDriverError {
|
||||
/**
|
||||
* @param {string=} opt_error the error message, if any.
|
||||
* @param {string=} opt_text the text of the open dialog, if available.
|
||||
*/
|
||||
constructor(opt_error, opt_text) {
|
||||
super(opt_error)
|
||||
|
||||
/** @private {(string|undefined)} */
|
||||
this.text_ = opt_text
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {(string|undefined)} The text displayed with the unhandled alert,
|
||||
* if available.
|
||||
*/
|
||||
getAlertText() {
|
||||
return this.text_
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A command could not be executed because the remote end is not aware of it.
|
||||
*/
|
||||
class UnknownCommandError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The requested command matched a known URL but did not match an method for
|
||||
* that URL.
|
||||
*/
|
||||
class UnknownMethodError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports an unsupported operation.
|
||||
*/
|
||||
class UnsupportedOperationError extends WebDriverError {
|
||||
/** @param {string=} opt_error the error message, if any. */
|
||||
constructor(opt_error) {
|
||||
super(opt_error)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(jleyba): Define UnknownError as an alias of WebDriverError?
|
||||
|
||||
/**
|
||||
* Enum of legacy error codes.
|
||||
* TODO: remove this when all code paths have been switched to the new error
|
||||
* types.
|
||||
* @deprecated
|
||||
* @enum {number}
|
||||
*/
|
||||
const ErrorCode = {
|
||||
SUCCESS: 0,
|
||||
NO_SUCH_SESSION: 6,
|
||||
NO_SUCH_ELEMENT: 7,
|
||||
NO_SUCH_FRAME: 8,
|
||||
UNKNOWN_COMMAND: 9,
|
||||
UNSUPPORTED_OPERATION: 9,
|
||||
STALE_ELEMENT_REFERENCE: 10,
|
||||
ELEMENT_NOT_VISIBLE: 11,
|
||||
INVALID_ELEMENT_STATE: 12,
|
||||
UNKNOWN_ERROR: 13,
|
||||
ELEMENT_NOT_SELECTABLE: 15,
|
||||
JAVASCRIPT_ERROR: 17,
|
||||
XPATH_LOOKUP_ERROR: 19,
|
||||
TIMEOUT: 21,
|
||||
NO_SUCH_WINDOW: 23,
|
||||
INVALID_COOKIE_DOMAIN: 24,
|
||||
UNABLE_TO_SET_COOKIE: 25,
|
||||
UNEXPECTED_ALERT_OPEN: 26,
|
||||
NO_SUCH_ALERT: 27,
|
||||
SCRIPT_TIMEOUT: 28,
|
||||
INVALID_ELEMENT_COORDINATES: 29,
|
||||
IME_NOT_AVAILABLE: 30,
|
||||
IME_ENGINE_ACTIVATION_FAILED: 31,
|
||||
INVALID_SELECTOR_ERROR: 32,
|
||||
SESSION_NOT_CREATED: 33,
|
||||
MOVE_TARGET_OUT_OF_BOUNDS: 34,
|
||||
SQL_DATABASE_ERROR: 35,
|
||||
INVALID_XPATH_SELECTOR: 51,
|
||||
INVALID_XPATH_SELECTOR_RETURN_TYPE: 52,
|
||||
ELEMENT_NOT_INTERACTABLE: 60,
|
||||
INVALID_ARGUMENT: 61,
|
||||
NO_SUCH_COOKIE: 62,
|
||||
UNABLE_TO_CAPTURE_SCREEN: 63,
|
||||
ELEMENT_CLICK_INTERCEPTED: 64,
|
||||
METHOD_NOT_ALLOWED: 405,
|
||||
}
|
||||
|
||||
const LEGACY_ERROR_CODE_TO_TYPE = new Map([
|
||||
[ErrorCode.NO_SUCH_SESSION, NoSuchSessionError],
|
||||
[ErrorCode.NO_SUCH_ELEMENT, NoSuchElementError],
|
||||
[ErrorCode.NO_SUCH_FRAME, NoSuchFrameError],
|
||||
[ErrorCode.UNSUPPORTED_OPERATION, UnsupportedOperationError],
|
||||
[ErrorCode.STALE_ELEMENT_REFERENCE, StaleElementReferenceError],
|
||||
[ErrorCode.INVALID_ELEMENT_STATE, InvalidElementStateError],
|
||||
[ErrorCode.UNKNOWN_ERROR, WebDriverError],
|
||||
[ErrorCode.ELEMENT_NOT_SELECTABLE, ElementNotSelectableError],
|
||||
[ErrorCode.JAVASCRIPT_ERROR, JavascriptError],
|
||||
[ErrorCode.XPATH_LOOKUP_ERROR, InvalidSelectorError],
|
||||
[ErrorCode.TIMEOUT, TimeoutError],
|
||||
[ErrorCode.NO_SUCH_WINDOW, NoSuchWindowError],
|
||||
[ErrorCode.INVALID_COOKIE_DOMAIN, InvalidCookieDomainError],
|
||||
[ErrorCode.UNABLE_TO_SET_COOKIE, UnableToSetCookieError],
|
||||
[ErrorCode.UNEXPECTED_ALERT_OPEN, UnexpectedAlertOpenError],
|
||||
[ErrorCode.NO_SUCH_ALERT, NoSuchAlertError],
|
||||
[ErrorCode.SCRIPT_TIMEOUT, ScriptTimeoutError],
|
||||
[ErrorCode.INVALID_ELEMENT_COORDINATES, InvalidCoordinatesError],
|
||||
[ErrorCode.INVALID_SELECTOR_ERROR, InvalidSelectorError],
|
||||
[ErrorCode.SESSION_NOT_CREATED, SessionNotCreatedError],
|
||||
[ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS, MoveTargetOutOfBoundsError],
|
||||
[ErrorCode.INVALID_XPATH_SELECTOR, InvalidSelectorError],
|
||||
[ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPE, InvalidSelectorError],
|
||||
[ErrorCode.ELEMENT_NOT_INTERACTABLE, ElementNotInteractableError],
|
||||
[ErrorCode.INVALID_ARGUMENT, InvalidArgumentError],
|
||||
[ErrorCode.NO_SUCH_COOKIE, NoSuchCookieError],
|
||||
[ErrorCode.UNABLE_TO_CAPTURE_SCREEN, UnableToCaptureScreenError],
|
||||
[ErrorCode.ELEMENT_CLICK_INTERCEPTED, ElementClickInterceptedError],
|
||||
[ErrorCode.METHOD_NOT_ALLOWED, UnsupportedOperationError],
|
||||
])
|
||||
|
||||
const ERROR_CODE_TO_TYPE = new Map([
|
||||
['unknown error', WebDriverError],
|
||||
['detached shadow root', DetachedShadowRootError],
|
||||
['element click intercepted', ElementClickInterceptedError],
|
||||
['element not interactable', ElementNotInteractableError],
|
||||
['element not selectable', ElementNotSelectableError],
|
||||
['insecure certificate', InsecureCertificateError],
|
||||
['invalid argument', InvalidArgumentError],
|
||||
['invalid cookie domain', InvalidCookieDomainError],
|
||||
['invalid coordinates', InvalidCoordinatesError],
|
||||
['invalid element state', InvalidElementStateError],
|
||||
['invalid selector', InvalidSelectorError],
|
||||
['invalid session id', NoSuchSessionError],
|
||||
['javascript error', JavascriptError],
|
||||
['move target out of bounds', MoveTargetOutOfBoundsError],
|
||||
['no such alert', NoSuchAlertError],
|
||||
['no such cookie', NoSuchCookieError],
|
||||
['no such element', NoSuchElementError],
|
||||
['no such frame', NoSuchFrameError],
|
||||
['no such shadow root', NoSuchShadowRootError],
|
||||
['no such window', NoSuchWindowError],
|
||||
['script timeout', ScriptTimeoutError],
|
||||
['session not created', SessionNotCreatedError],
|
||||
['stale element reference', StaleElementReferenceError],
|
||||
['timeout', TimeoutError],
|
||||
['unable to set cookie', UnableToSetCookieError],
|
||||
['unable to capture screen', UnableToCaptureScreenError],
|
||||
['unexpected alert open', UnexpectedAlertOpenError],
|
||||
['unknown command', UnknownCommandError],
|
||||
['unknown method', UnknownMethodError],
|
||||
['unsupported operation', UnsupportedOperationError],
|
||||
])
|
||||
|
||||
const TYPE_TO_ERROR_CODE = new Map()
|
||||
ERROR_CODE_TO_TYPE.forEach((value, key) => {
|
||||
TYPE_TO_ERROR_CODE.set(value, key)
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {*} err The error to encode.
|
||||
* @return {{error: string, message: string}} the encoded error.
|
||||
*/
|
||||
function encodeError(err) {
|
||||
let type = WebDriverError
|
||||
if (
|
||||
err instanceof WebDriverError &&
|
||||
TYPE_TO_ERROR_CODE.has(err.constructor)
|
||||
) {
|
||||
type = err.constructor
|
||||
}
|
||||
|
||||
let message = err instanceof Error ? err.message : err + ''
|
||||
|
||||
let code = /** @type {string} */ (TYPE_TO_ERROR_CODE.get(type))
|
||||
return { error: code, message: message }
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given value is a valid error response object according to the
|
||||
* W3C WebDriver spec.
|
||||
*
|
||||
* @param {?} data The value to test.
|
||||
* @return {boolean} Whether the given value data object is a valid error
|
||||
* response.
|
||||
* @see https://w3c.github.io/webdriver/webdriver-spec.html#protocol
|
||||
*/
|
||||
function isErrorResponse(data) {
|
||||
return isObject(data) && typeof data.error === 'string'
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an error coded from the W3C protocol. A generic error will be thrown
|
||||
* if the provided `data` is not a valid encoded error.
|
||||
*
|
||||
* @param {{error: string, message: string}} data The error data to decode.
|
||||
* @throws {WebDriverError} the decoded error.
|
||||
* @see https://w3c.github.io/webdriver/webdriver-spec.html#protocol
|
||||
*/
|
||||
function throwDecodedError(data) {
|
||||
if (isErrorResponse(data)) {
|
||||
let ctor = ERROR_CODE_TO_TYPE.get(data.error) || WebDriverError
|
||||
let err = new ctor(data.message)
|
||||
// TODO(jleyba): remove whichever case is excluded from the final W3C spec.
|
||||
if (typeof data.stacktrace === 'string') {
|
||||
err.remoteStacktrace = data.stacktrace
|
||||
} else if (typeof data.stackTrace === 'string') {
|
||||
err.remoteStacktrace = data.stackTrace
|
||||
}
|
||||
throw err
|
||||
}
|
||||
throw new WebDriverError('Unknown error: ' + JSON.stringify(data))
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a legacy response from the Selenium 2.0 wire protocol for an error.
|
||||
* @param {*} responseObj the response object to check.
|
||||
* @return {*} responseObj the original response if it does not define an error.
|
||||
* @throws {WebDriverError} if the response object defines an error.
|
||||
*/
|
||||
function checkLegacyResponse(responseObj) {
|
||||
// Handle the legacy Selenium error response format.
|
||||
if (
|
||||
isObject(responseObj) &&
|
||||
typeof responseObj.status === 'number' &&
|
||||
responseObj.status !== 0
|
||||
) {
|
||||
const { status, value } = responseObj
|
||||
|
||||
let ctor = LEGACY_ERROR_CODE_TO_TYPE.get(status) || WebDriverError
|
||||
|
||||
if (!value || typeof value !== 'object') {
|
||||
throw new ctor(value + '')
|
||||
} else {
|
||||
let message = value['message'] + ''
|
||||
if (ctor !== UnexpectedAlertOpenError) {
|
||||
throw new ctor(message)
|
||||
}
|
||||
|
||||
let text = ''
|
||||
if (value['alert'] && typeof value['alert']['text'] === 'string') {
|
||||
text = value['alert']['text']
|
||||
}
|
||||
throw new UnexpectedAlertOpenError(message, text)
|
||||
}
|
||||
}
|
||||
return responseObj
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
ErrorCode,
|
||||
|
||||
WebDriverError,
|
||||
DetachedShadowRootError,
|
||||
ElementClickInterceptedError,
|
||||
ElementNotInteractableError,
|
||||
ElementNotSelectableError,
|
||||
InsecureCertificateError,
|
||||
InvalidArgumentError,
|
||||
InvalidCookieDomainError,
|
||||
InvalidCoordinatesError,
|
||||
InvalidElementStateError,
|
||||
InvalidSelectorError,
|
||||
JavascriptError,
|
||||
MoveTargetOutOfBoundsError,
|
||||
NoSuchAlertError,
|
||||
NoSuchCookieError,
|
||||
NoSuchElementError,
|
||||
NoSuchFrameError,
|
||||
NoSuchShadowRootError,
|
||||
NoSuchSessionError,
|
||||
NoSuchWindowError,
|
||||
ScriptTimeoutError,
|
||||
SessionNotCreatedError,
|
||||
StaleElementReferenceError,
|
||||
TimeoutError,
|
||||
UnableToSetCookieError,
|
||||
UnableToCaptureScreenError,
|
||||
UnexpectedAlertOpenError,
|
||||
UnknownCommandError,
|
||||
UnknownMethodError,
|
||||
UnsupportedOperationError,
|
||||
checkLegacyResponse,
|
||||
encodeError,
|
||||
isErrorResponse,
|
||||
throwDecodedError,
|
||||
}
|
||||
652
node_modules/selenium-webdriver/lib/http.js
generated
vendored
Normal file
652
node_modules/selenium-webdriver/lib/http.js
generated
vendored
Normal file
@@ -0,0 +1,652 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines an environment agnostic {@linkplain cmd.Executor
|
||||
* command executor} that communicates with a remote end using JSON over HTTP.
|
||||
*
|
||||
* Clients should implement the {@link Client} interface, which is used by
|
||||
* the {@link Executor} to send commands to the remote end.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const cmd = require('./command')
|
||||
const error = require('./error')
|
||||
const logging = require('./logging')
|
||||
const promise = require('./promise')
|
||||
const { Session } = require('./session')
|
||||
const webElement = require('./webelement')
|
||||
const { isObject } = require('./util')
|
||||
|
||||
const getAttribute = requireAtom(
|
||||
'get-attribute.js',
|
||||
'//javascript/node/selenium-webdriver/lib/atoms:get-attribute.js'
|
||||
)
|
||||
const isDisplayed = requireAtom(
|
||||
'is-displayed.js',
|
||||
'//javascript/node/selenium-webdriver/lib/atoms:is-displayed.js'
|
||||
)
|
||||
const findElements = requireAtom(
|
||||
'find-elements.js',
|
||||
'//javascript/node/selenium-webdriver/lib/atoms:find-elements.js'
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {string} module
|
||||
* @param {string} bazelTarget
|
||||
* @return {!Function}
|
||||
*/
|
||||
function requireAtom(module, bazelTarget) {
|
||||
try {
|
||||
return require('./atoms/' + module)
|
||||
} catch (ex) {
|
||||
try {
|
||||
const file = bazelTarget.slice(2).replace(':', '/')
|
||||
console.log(`../../../bazel-bin/${file}`)
|
||||
return require(path.resolve(`../../../bazel-bin/${file}`))
|
||||
} catch (ex2) {
|
||||
console.log(ex2)
|
||||
throw Error(
|
||||
`Failed to import atoms module ${module}. If running in dev mode, you` +
|
||||
` need to run \`bazel build ${bazelTarget}\` from the project` +
|
||||
`root: ${ex}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a headers map to a HTTP header block string.
|
||||
* @param {!Map<string, string>} headers The map to convert.
|
||||
* @return {string} The headers as a string.
|
||||
*/
|
||||
function headersToString(headers) {
|
||||
const ret = []
|
||||
headers.forEach(function (value, name) {
|
||||
ret.push(`${name.toLowerCase()}: ${value}`)
|
||||
})
|
||||
return ret.join('\n')
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a HTTP request message. This class is a "partial" request and only
|
||||
* defines the path on the server to send a request to. It is each client's
|
||||
* responsibility to build the full URL for the final request.
|
||||
* @final
|
||||
*/
|
||||
class Request {
|
||||
/**
|
||||
* @param {string} method The HTTP method to use for the request.
|
||||
* @param {string} path The path on the server to send the request to.
|
||||
* @param {Object=} opt_data This request's non-serialized JSON payload data.
|
||||
*/
|
||||
constructor(method, path, opt_data) {
|
||||
this.method = /** string */ method
|
||||
this.path = /** string */ path
|
||||
this.data = /** Object */ opt_data
|
||||
this.headers = /** !Map<string, string> */ new Map([
|
||||
['Accept', 'application/json; charset=utf-8'],
|
||||
])
|
||||
}
|
||||
|
||||
/** @override */
|
||||
toString() {
|
||||
let ret = `${this.method} ${this.path} HTTP/1.1\n`
|
||||
ret += headersToString(this.headers) + '\n\n'
|
||||
if (this.data) {
|
||||
ret += JSON.stringify(this.data)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a HTTP response message.
|
||||
* @final
|
||||
*/
|
||||
class Response {
|
||||
/**
|
||||
* @param {number} status The response code.
|
||||
* @param {!Object<string>} headers The response headers. All header names
|
||||
* will be converted to lowercase strings for consistent lookups.
|
||||
* @param {string} body The response body.
|
||||
*/
|
||||
constructor(status, headers, body) {
|
||||
this.status = /** number */ status
|
||||
this.body = /** string */ body
|
||||
this.headers = /** !Map<string, string>*/ new Map()
|
||||
for (let header in headers) {
|
||||
this.headers.set(header.toLowerCase(), headers[header])
|
||||
}
|
||||
}
|
||||
|
||||
/** @override */
|
||||
toString() {
|
||||
let ret = `HTTP/1.1 ${this.status}\n${headersToString(this.headers)}\n\n`
|
||||
if (this.body) {
|
||||
ret += this.body
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
/** @enum {!Function} */
|
||||
const Atom = {
|
||||
GET_ATTRIBUTE: getAttribute,
|
||||
IS_DISPLAYED: isDisplayed,
|
||||
FIND_ELEMENTS: findElements,
|
||||
}
|
||||
|
||||
const LOG = logging.getLogger('webdriver.http')
|
||||
|
||||
function post(path) {
|
||||
return resource('POST', path)
|
||||
}
|
||||
function del(path) {
|
||||
return resource('DELETE', path)
|
||||
}
|
||||
function get(path) {
|
||||
return resource('GET', path)
|
||||
}
|
||||
function resource(method, path) {
|
||||
return { method: method, path: path }
|
||||
}
|
||||
|
||||
/** @typedef {{method: string, path: string}} */
|
||||
var CommandSpec // eslint-disable-line
|
||||
|
||||
/** @typedef {function(!cmd.Command): !cmd.Command} */
|
||||
var CommandTransformer // eslint-disable-line
|
||||
|
||||
class InternalTypeError extends TypeError {}
|
||||
|
||||
/**
|
||||
* @param {!cmd.Command} command The initial command.
|
||||
* @param {Atom} atom The name of the atom to execute.
|
||||
* @param params
|
||||
* @return {!Command} The transformed command to execute.
|
||||
*/
|
||||
function toExecuteAtomCommand(command, atom, name, ...params) {
|
||||
if (typeof atom !== 'function') {
|
||||
throw new InternalTypeError('atom is not a function: ' + typeof atom)
|
||||
}
|
||||
|
||||
return new cmd.Command(cmd.Name.EXECUTE_SCRIPT)
|
||||
.setParameter('sessionId', command.getParameter('sessionId'))
|
||||
.setParameter('script', `/* ${name} */return (${atom}).apply(null, arguments)`)
|
||||
.setParameter(
|
||||
'args',
|
||||
params.map((param) => command.getParameter(param))
|
||||
)
|
||||
}
|
||||
|
||||
/** @const {!Map<string, (CommandSpec|CommandTransformer)>} */
|
||||
const W3C_COMMAND_MAP = new Map([
|
||||
// Session management.
|
||||
[cmd.Name.NEW_SESSION, post('/session')],
|
||||
[cmd.Name.QUIT, del('/session/:sessionId')],
|
||||
|
||||
// Server status.
|
||||
[cmd.Name.GET_SERVER_STATUS, get('/status')],
|
||||
|
||||
// timeouts
|
||||
[cmd.Name.GET_TIMEOUT, get('/session/:sessionId/timeouts')],
|
||||
[cmd.Name.SET_TIMEOUT, post('/session/:sessionId/timeouts')],
|
||||
|
||||
// Navigation.
|
||||
[cmd.Name.GET_CURRENT_URL, get('/session/:sessionId/url')],
|
||||
[cmd.Name.GET, post('/session/:sessionId/url')],
|
||||
[cmd.Name.GO_BACK, post('/session/:sessionId/back')],
|
||||
[cmd.Name.GO_FORWARD, post('/session/:sessionId/forward')],
|
||||
[cmd.Name.REFRESH, post('/session/:sessionId/refresh')],
|
||||
|
||||
// Page inspection.
|
||||
[cmd.Name.GET_PAGE_SOURCE, get('/session/:sessionId/source')],
|
||||
[cmd.Name.GET_TITLE, get('/session/:sessionId/title')],
|
||||
|
||||
// Script execution.
|
||||
[cmd.Name.EXECUTE_SCRIPT, post('/session/:sessionId/execute/sync')],
|
||||
[cmd.Name.EXECUTE_ASYNC_SCRIPT, post('/session/:sessionId/execute/async')],
|
||||
|
||||
// Frame selection.
|
||||
[cmd.Name.SWITCH_TO_FRAME, post('/session/:sessionId/frame')],
|
||||
[cmd.Name.SWITCH_TO_FRAME_PARENT, post('/session/:sessionId/frame/parent')],
|
||||
|
||||
// Window management.
|
||||
[cmd.Name.GET_CURRENT_WINDOW_HANDLE, get('/session/:sessionId/window')],
|
||||
[cmd.Name.CLOSE, del('/session/:sessionId/window')],
|
||||
[cmd.Name.SWITCH_TO_WINDOW, post('/session/:sessionId/window')],
|
||||
[cmd.Name.SWITCH_TO_NEW_WINDOW, post('/session/:sessionId/window/new')],
|
||||
[cmd.Name.GET_WINDOW_HANDLES, get('/session/:sessionId/window/handles')],
|
||||
[cmd.Name.GET_WINDOW_RECT, get('/session/:sessionId/window/rect')],
|
||||
[cmd.Name.SET_WINDOW_RECT, post('/session/:sessionId/window/rect')],
|
||||
[cmd.Name.MAXIMIZE_WINDOW, post('/session/:sessionId/window/maximize')],
|
||||
[cmd.Name.MINIMIZE_WINDOW, post('/session/:sessionId/window/minimize')],
|
||||
[cmd.Name.FULLSCREEN_WINDOW, post('/session/:sessionId/window/fullscreen')],
|
||||
|
||||
// Actions.
|
||||
[cmd.Name.ACTIONS, post('/session/:sessionId/actions')],
|
||||
[cmd.Name.CLEAR_ACTIONS, del('/session/:sessionId/actions')],
|
||||
[cmd.Name.PRINT_PAGE, post('/session/:sessionId/print')],
|
||||
|
||||
// Locating elements.
|
||||
[cmd.Name.GET_ACTIVE_ELEMENT, get('/session/:sessionId/element/active')],
|
||||
[cmd.Name.FIND_ELEMENT, post('/session/:sessionId/element')],
|
||||
[cmd.Name.FIND_ELEMENTS, post('/session/:sessionId/elements')],
|
||||
[
|
||||
cmd.Name.FIND_ELEMENTS_RELATIVE,
|
||||
(cmd) => {
|
||||
return toExecuteAtomCommand(cmd, Atom.FIND_ELEMENTS, 'findElements', 'args')
|
||||
},
|
||||
],
|
||||
[
|
||||
cmd.Name.FIND_CHILD_ELEMENT,
|
||||
post('/session/:sessionId/element/:id/element'),
|
||||
],
|
||||
[
|
||||
cmd.Name.FIND_CHILD_ELEMENTS,
|
||||
post('/session/:sessionId/element/:id/elements'),
|
||||
],
|
||||
// Element interaction.
|
||||
[cmd.Name.GET_ELEMENT_TAG_NAME, get('/session/:sessionId/element/:id/name')],
|
||||
[
|
||||
cmd.Name.GET_DOM_ATTRIBUTE,
|
||||
get('/session/:sessionId/element/:id/attribute/:name'),
|
||||
],
|
||||
[
|
||||
cmd.Name.GET_ELEMENT_ATTRIBUTE,
|
||||
(cmd) => {
|
||||
return toExecuteAtomCommand(cmd, Atom.GET_ATTRIBUTE, 'getAttribute', 'id', 'name')
|
||||
},
|
||||
],
|
||||
[
|
||||
cmd.Name.GET_ELEMENT_PROPERTY,
|
||||
get('/session/:sessionId/element/:id/property/:name'),
|
||||
],
|
||||
[
|
||||
cmd.Name.GET_ELEMENT_VALUE_OF_CSS_PROPERTY,
|
||||
get('/session/:sessionId/element/:id/css/:propertyName'),
|
||||
],
|
||||
[cmd.Name.GET_ELEMENT_RECT, get('/session/:sessionId/element/:id/rect')],
|
||||
[cmd.Name.CLEAR_ELEMENT, post('/session/:sessionId/element/:id/clear')],
|
||||
[cmd.Name.CLICK_ELEMENT, post('/session/:sessionId/element/:id/click')],
|
||||
[
|
||||
cmd.Name.SEND_KEYS_TO_ELEMENT,
|
||||
post('/session/:sessionId/element/:id/value'),
|
||||
],
|
||||
[cmd.Name.GET_ELEMENT_TEXT, get('/session/:sessionId/element/:id/text')],
|
||||
[
|
||||
cmd.Name.GET_COMPUTED_ROLE,
|
||||
get('/session/:sessionId/element/:id/computedrole'),
|
||||
],
|
||||
[
|
||||
cmd.Name.GET_COMPUTED_LABEL,
|
||||
get('/session/:sessionId/element/:id/computedlabel'),
|
||||
],
|
||||
[cmd.Name.IS_ELEMENT_ENABLED, get('/session/:sessionId/element/:id/enabled')],
|
||||
[
|
||||
cmd.Name.IS_ELEMENT_SELECTED,
|
||||
get('/session/:sessionId/element/:id/selected'),
|
||||
],
|
||||
|
||||
[
|
||||
cmd.Name.IS_ELEMENT_DISPLAYED,
|
||||
(cmd) => {
|
||||
return toExecuteAtomCommand(cmd, Atom.IS_DISPLAYED, 'isDisplayed', 'id')
|
||||
},
|
||||
],
|
||||
|
||||
// Cookie management.
|
||||
[cmd.Name.GET_ALL_COOKIES, get('/session/:sessionId/cookie')],
|
||||
[cmd.Name.ADD_COOKIE, post('/session/:sessionId/cookie')],
|
||||
[cmd.Name.DELETE_ALL_COOKIES, del('/session/:sessionId/cookie')],
|
||||
[cmd.Name.GET_COOKIE, get('/session/:sessionId/cookie/:name')],
|
||||
[cmd.Name.DELETE_COOKIE, del('/session/:sessionId/cookie/:name')],
|
||||
|
||||
// Alert management.
|
||||
[cmd.Name.ACCEPT_ALERT, post('/session/:sessionId/alert/accept')],
|
||||
[cmd.Name.DISMISS_ALERT, post('/session/:sessionId/alert/dismiss')],
|
||||
[cmd.Name.GET_ALERT_TEXT, get('/session/:sessionId/alert/text')],
|
||||
[cmd.Name.SET_ALERT_TEXT, post('/session/:sessionId/alert/text')],
|
||||
|
||||
// Screenshots.
|
||||
[cmd.Name.SCREENSHOT, get('/session/:sessionId/screenshot')],
|
||||
[
|
||||
cmd.Name.TAKE_ELEMENT_SCREENSHOT,
|
||||
get('/session/:sessionId/element/:id/screenshot'),
|
||||
],
|
||||
|
||||
// Shadow Root
|
||||
[cmd.Name.GET_SHADOW_ROOT, get('/session/:sessionId/element/:id/shadow')],
|
||||
[
|
||||
cmd.Name.FIND_ELEMENT_FROM_SHADOWROOT,
|
||||
post('/session/:sessionId/shadow/:id/element'),
|
||||
],
|
||||
[
|
||||
cmd.Name.FIND_ELEMENTS_FROM_SHADOWROOT,
|
||||
post('/session/:sessionId/shadow/:id/elements'),
|
||||
],
|
||||
// Log extensions.
|
||||
[cmd.Name.GET_LOG, post('/session/:sessionId/se/log')],
|
||||
[cmd.Name.GET_AVAILABLE_LOG_TYPES, get('/session/:sessionId/se/log/types')],
|
||||
|
||||
// Server Extensions
|
||||
[cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
|
||||
|
||||
// Virtual Authenticator
|
||||
[
|
||||
cmd.Name.ADD_VIRTUAL_AUTHENTICATOR,
|
||||
post('/session/:sessionId/webauthn/authenticator'),
|
||||
],
|
||||
[
|
||||
cmd.Name.REMOVE_VIRTUAL_AUTHENTICATOR,
|
||||
del('/session/:sessionId/webauthn/authenticator/:authenticatorId'),
|
||||
],
|
||||
[
|
||||
cmd.Name.ADD_CREDENTIAL,
|
||||
post(
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credential'
|
||||
),
|
||||
],
|
||||
[
|
||||
cmd.Name.GET_CREDENTIALS,
|
||||
get(
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials'
|
||||
),
|
||||
],
|
||||
[
|
||||
cmd.Name.REMOVE_CREDENTIAL,
|
||||
del(
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/:credentialId'
|
||||
),
|
||||
],
|
||||
[
|
||||
cmd.Name.REMOVE_ALL_CREDENTIALS,
|
||||
del(
|
||||
'/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials'
|
||||
),
|
||||
],
|
||||
[
|
||||
cmd.Name.SET_USER_VERIFIED,
|
||||
post('/session/:sessionId/webauthn/authenticator/:authenticatorId/uv'),
|
||||
],
|
||||
])
|
||||
|
||||
/**
|
||||
* Handles sending HTTP messages to a remote end.
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
class Client {
|
||||
/**
|
||||
* Sends a request to the server. The client will automatically follow any
|
||||
* redirects returned by the server, fulfilling the returned promise with the
|
||||
* final response.
|
||||
*
|
||||
* @param {!Request} httpRequest The request to send.
|
||||
* @return {!Promise<Response>} A promise that will be fulfilled with the
|
||||
* server's response.
|
||||
*/
|
||||
send(httpRequest) {} // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map<string, CommandSpec>} customCommands
|
||||
* A map of custom command definitions.
|
||||
* @param {!cmd.Command} command The command to resolve.
|
||||
* @return {!Request} A promise that will resolve with the
|
||||
* command to execute.
|
||||
*/
|
||||
function buildRequest(customCommands, command) {
|
||||
LOG.finest(() => `Translating command: ${command.getName()}`)
|
||||
let spec = customCommands && customCommands.get(command.getName())
|
||||
if (spec) {
|
||||
return toHttpRequest(spec)
|
||||
}
|
||||
|
||||
spec = W3C_COMMAND_MAP.get(command.getName())
|
||||
if (typeof spec === 'function') {
|
||||
LOG.finest(() => `Transforming command for W3C: ${command.getName()}`)
|
||||
let newCommand = spec(command)
|
||||
return buildRequest(customCommands, newCommand)
|
||||
} else if (spec) {
|
||||
return toHttpRequest(spec)
|
||||
}
|
||||
throw new error.UnknownCommandError(
|
||||
'Unrecognized command: ' + command.getName()
|
||||
)
|
||||
|
||||
/**
|
||||
* @param {CommandSpec} resource
|
||||
* @return {!Request}
|
||||
*/
|
||||
function toHttpRequest(resource) {
|
||||
LOG.finest(() => `Building HTTP request: ${JSON.stringify(resource)}`)
|
||||
let parameters = command.getParameters()
|
||||
let path = buildPath(resource.path, parameters)
|
||||
return new Request(resource.method, path, parameters)
|
||||
}
|
||||
}
|
||||
|
||||
const CLIENTS =
|
||||
/** !WeakMap<!Executor, !(Client|IThenable<!Client>)> */ new WeakMap()
|
||||
|
||||
/**
|
||||
* A command executor that communicates with the server using JSON over HTTP.
|
||||
*
|
||||
* By default, each instance of this class will use the legacy wire protocol
|
||||
* from [Selenium project][json]. The executor will automatically switch to the
|
||||
* [W3C wire protocol][w3c] if the remote end returns a compliant response to
|
||||
* a new session command.
|
||||
*
|
||||
* [json]: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol
|
||||
* [w3c]: https://w3c.github.io/webdriver/webdriver-spec.html
|
||||
*
|
||||
* @implements {cmd.Executor}
|
||||
*/
|
||||
class Executor {
|
||||
/**
|
||||
* @param {!(Client|IThenable<!Client>)} client The client to use for sending
|
||||
* requests to the server, or a promise-like object that will resolve
|
||||
* to the client.
|
||||
*/
|
||||
constructor(client) {
|
||||
CLIENTS.set(this, client)
|
||||
|
||||
/** @private {Map<string, CommandSpec>} */
|
||||
this.customCommands_ = null
|
||||
|
||||
/** @private {!logging.Logger} */
|
||||
this.log_ = logging.getLogger('webdriver.http.Executor')
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a new command for use with this executor. When a command is sent,
|
||||
* the {@code path} will be preprocessed using the command's parameters; any
|
||||
* path segments prefixed with ":" will be replaced by the parameter of the
|
||||
* same name. For example, given "/person/:name" and the parameters
|
||||
* "{name: 'Bob'}", the final command path will be "/person/Bob".
|
||||
*
|
||||
* @param {string} name The command name.
|
||||
* @param {string} method The HTTP method to use when sending this command.
|
||||
* @param {string} path The path to send the command to, relative to
|
||||
* the WebDriver server's command root and of the form
|
||||
* "/path/:variable/segment".
|
||||
*/
|
||||
defineCommand(name, method, path) {
|
||||
if (!this.customCommands_) {
|
||||
this.customCommands_ = new Map()
|
||||
}
|
||||
this.customCommands_.set(name, { method, path })
|
||||
}
|
||||
|
||||
/** @override */
|
||||
async execute(command) {
|
||||
let request = buildRequest(this.customCommands_, command)
|
||||
this.log_.finer(() => `>>> ${request.method} ${request.path}`)
|
||||
|
||||
let client = CLIENTS.get(this)
|
||||
if (promise.isPromise(client)) {
|
||||
client = await client
|
||||
CLIENTS.set(this, client)
|
||||
}
|
||||
|
||||
let response = await client.send(request)
|
||||
this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`)
|
||||
|
||||
let httpResponse = /** @type {!Response} */ (response)
|
||||
|
||||
let { isW3C, value } = parseHttpResponse(command, httpResponse)
|
||||
|
||||
if (command.getName() === cmd.Name.NEW_SESSION) {
|
||||
if (!value || !value.sessionId) {
|
||||
throw new error.WebDriverError(
|
||||
`Unable to parse new session response: ${response.body}`
|
||||
)
|
||||
}
|
||||
|
||||
// The remote end is a W3C compliant server if there is no `status`
|
||||
// field in the response.
|
||||
if (command.getName() === cmd.Name.NEW_SESSION) {
|
||||
this.w3c = this.w3c || isW3C
|
||||
}
|
||||
|
||||
// No implementations use the `capabilities` key yet...
|
||||
let capabilities = value.capabilities || value.value
|
||||
return new Session(
|
||||
/** @type {{sessionId: string}} */ (value).sessionId,
|
||||
capabilities
|
||||
)
|
||||
}
|
||||
|
||||
return typeof value === 'undefined' ? null : value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str .
|
||||
* @return {?} .
|
||||
*/
|
||||
function tryParse(str) {
|
||||
try {
|
||||
return JSON.parse(str)
|
||||
} catch (ignored) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback used to parse {@link Response} objects from a
|
||||
* {@link HttpClient}.
|
||||
*
|
||||
* @param {!cmd.Command} command The command the response is for.
|
||||
* @param {!Response} httpResponse The HTTP response to parse.
|
||||
* @return {{isW3C: boolean, value: ?}} An object describing the parsed
|
||||
* response. This object will have two fields: `isW3C` indicates whether
|
||||
* the response looks like it came from a remote end that conforms with the
|
||||
* W3C WebDriver spec, and `value`, the actual response value.
|
||||
* @throws {WebDriverError} If the HTTP response is an error.
|
||||
*/
|
||||
function parseHttpResponse(command, httpResponse) {
|
||||
if (httpResponse.status < 200) {
|
||||
// This should never happen, but throw the raw response so users report it.
|
||||
throw new error.WebDriverError(`Unexpected HTTP response:\n${httpResponse}`)
|
||||
}
|
||||
|
||||
let parsed = tryParse(httpResponse.body)
|
||||
|
||||
if (parsed && typeof parsed === 'object') {
|
||||
let value = parsed.value
|
||||
let isW3C = isObject(value) && typeof parsed.status === 'undefined'
|
||||
|
||||
if (!isW3C) {
|
||||
error.checkLegacyResponse(parsed)
|
||||
|
||||
// Adjust legacy new session responses to look like W3C to simplify
|
||||
// later processing.
|
||||
if (command.getName() === cmd.Name.NEW_SESSION) {
|
||||
value = parsed
|
||||
}
|
||||
} else if (httpResponse.status > 399) {
|
||||
error.throwDecodedError(value)
|
||||
}
|
||||
|
||||
return { isW3C, value }
|
||||
}
|
||||
|
||||
if (parsed !== undefined) {
|
||||
return { isW3C: false, value: parsed }
|
||||
}
|
||||
|
||||
let value = httpResponse.body.replace(/\r\n/g, '\n')
|
||||
|
||||
// 404 represents an unknown command; anything else > 399 is a generic unknown
|
||||
// error.
|
||||
if (httpResponse.status === 404) {
|
||||
throw new error.UnsupportedOperationError(command.getName() + ': ' + value)
|
||||
} else if (httpResponse.status >= 400) {
|
||||
throw new error.WebDriverError(value)
|
||||
}
|
||||
|
||||
return { isW3C: false, value: value || null }
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a fully qualified path using the given set of command parameters. Each
|
||||
* path segment prefixed with ':' will be replaced by the value of the
|
||||
* corresponding parameter. All parameters spliced into the path will be
|
||||
* removed from the parameter map.
|
||||
* @param {string} path The original resource path.
|
||||
* @param {!Object<*>} parameters The parameters object to splice into the path.
|
||||
* @return {string} The modified path.
|
||||
*/
|
||||
function buildPath(path, parameters) {
|
||||
let pathParameters = path.match(/\/:(\w+)\b/g)
|
||||
if (pathParameters) {
|
||||
for (let i = 0; i < pathParameters.length; ++i) {
|
||||
let key = pathParameters[i].substring(2) // Trim the /:
|
||||
if (key in parameters) {
|
||||
let value = parameters[key]
|
||||
if (webElement.isId(value)) {
|
||||
// When inserting a WebElement into the URL, only use its ID value,
|
||||
// not the full JSON.
|
||||
value = webElement.extractId(value)
|
||||
}
|
||||
path = path.replace(pathParameters[i], '/' + value)
|
||||
delete parameters[key]
|
||||
} else {
|
||||
throw new error.InvalidArgumentError(
|
||||
'Missing required parameter: ' + key
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Executor,
|
||||
Client,
|
||||
Request,
|
||||
Response,
|
||||
// Exported for testing.
|
||||
buildPath,
|
||||
}
|
||||
1063
node_modules/selenium-webdriver/lib/input.js
generated
vendored
Normal file
1063
node_modules/selenium-webdriver/lib/input.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
661
node_modules/selenium-webdriver/lib/logging.js
generated
vendored
Normal file
661
node_modules/selenium-webdriver/lib/logging.js
generated
vendored
Normal file
@@ -0,0 +1,661 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @fileoverview Defines WebDriver's logging system. The logging system is
|
||||
* broken into major components: local and remote logging.
|
||||
*
|
||||
* The local logging API, which is anchored by the {@linkplain Logger} class is
|
||||
* similar to Java's logging API. Loggers, retrieved by
|
||||
* {@linkplain #getLogger getLogger(name)}, use hierarchical, dot-delimited
|
||||
* namespaces (e.g. "" > "webdriver" > "webdriver.logging"). Recorded log
|
||||
* messages are represented by the {@linkplain Entry} class. You can capture log
|
||||
* records by {@linkplain Logger#addHandler attaching} a handler function to the
|
||||
* desired logger. For convenience, you can quickly enable logging to the
|
||||
* console by simply calling {@linkplain #installConsoleHandler
|
||||
* installConsoleHandler}.
|
||||
*
|
||||
* The [remote logging API](https://github.com/SeleniumHQ/selenium/wiki/Logging)
|
||||
* allows you to retrieve logs from a remote WebDriver server. This API uses the
|
||||
* {@link Preferences} class to define desired log levels prior to creating
|
||||
* a WebDriver session:
|
||||
*
|
||||
* var prefs = new logging.Preferences();
|
||||
* prefs.setLevel(logging.Type.BROWSER, logging.Level.DEBUG);
|
||||
*
|
||||
* var caps = Capabilities.chrome();
|
||||
* caps.setLoggingPrefs(prefs);
|
||||
* // ...
|
||||
*
|
||||
* Remote log entries, also represented by the {@link Entry} class, may be
|
||||
* retrieved via {@link webdriver.WebDriver.Logs}:
|
||||
*
|
||||
* driver.manage().logs().get(logging.Type.BROWSER)
|
||||
* .then(function(entries) {
|
||||
* entries.forEach(function(entry) {
|
||||
* console.log('[%s] %s', entry.level.name, entry.message);
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* **NOTE:** Only a few browsers support the remote logging API (notably
|
||||
* Firefox and Chrome). Firefox supports basic logging functionality, while
|
||||
* Chrome exposes robust
|
||||
* [performance logging](https://chromedriver.chromium.org/logging)
|
||||
* options. Remote logging is still considered a non-standard feature, and the
|
||||
* APIs exposed by this module for it are non-frozen. This module will be
|
||||
* updated, possibly breaking backwards-compatibility, once logging is
|
||||
* officially defined by the
|
||||
* [W3C WebDriver spec](http://www.w3.org/TR/webdriver/).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines a message level that may be used to control logging output.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Level {
|
||||
/**
|
||||
* @param {string} name the level's name.
|
||||
* @param {number} level the level's numeric value.
|
||||
*/
|
||||
constructor(name, level) {
|
||||
if (level < 0) {
|
||||
throw new TypeError('Level must be >= 0')
|
||||
}
|
||||
|
||||
/** @private {string} */
|
||||
this.name_ = name
|
||||
|
||||
/** @private {number} */
|
||||
this.value_ = level
|
||||
}
|
||||
|
||||
/** This logger's name. */
|
||||
get name() {
|
||||
return this.name_
|
||||
}
|
||||
|
||||
/** The numeric log level. */
|
||||
get value() {
|
||||
return this.value_
|
||||
}
|
||||
|
||||
/** @override */
|
||||
toString() {
|
||||
return this.name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates no log messages should be recorded.
|
||||
* @const
|
||||
*/
|
||||
Level.OFF = new Level('OFF', Infinity)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `1000` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.SEVERE = new Level('SEVERE', 1000)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `900` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.WARNING = new Level('WARNING', 900)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `800` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.INFO = new Level('INFO', 800)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `700` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.DEBUG = new Level('DEBUG', 700)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `500` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.FINE = new Level('FINE', 500)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `400` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.FINER = new Level('FINER', 400)
|
||||
|
||||
/**
|
||||
* Log messages with a level of `300` or higher.
|
||||
* @const
|
||||
*/
|
||||
Level.FINEST = new Level('FINEST', 300)
|
||||
|
||||
/**
|
||||
* Indicates all log messages should be recorded.
|
||||
* @const
|
||||
*/
|
||||
Level.ALL = new Level('ALL', 0)
|
||||
|
||||
const ALL_LEVELS = /** !Set<Level> */ new Set([
|
||||
Level.OFF,
|
||||
Level.SEVERE,
|
||||
Level.WARNING,
|
||||
Level.INFO,
|
||||
Level.DEBUG,
|
||||
Level.FINE,
|
||||
Level.FINER,
|
||||
Level.FINEST,
|
||||
Level.ALL,
|
||||
])
|
||||
|
||||
const LEVELS_BY_NAME = /** !Map<string, !Level> */ new Map([
|
||||
[Level.OFF.name, Level.OFF],
|
||||
[Level.SEVERE.name, Level.SEVERE],
|
||||
[Level.WARNING.name, Level.WARNING],
|
||||
[Level.INFO.name, Level.INFO],
|
||||
[Level.DEBUG.name, Level.DEBUG],
|
||||
[Level.FINE.name, Level.FINE],
|
||||
[Level.FINER.name, Level.FINER],
|
||||
[Level.FINEST.name, Level.FINEST],
|
||||
[Level.ALL.name, Level.ALL],
|
||||
])
|
||||
|
||||
/**
|
||||
* Converts a level name or value to a {@link Level} value. If the name/value
|
||||
* is not recognized, {@link Level.ALL} will be returned.
|
||||
*
|
||||
* @param {(number|string)} nameOrValue The log level name, or value, to
|
||||
* convert.
|
||||
* @return {!Level} The converted level.
|
||||
*/
|
||||
function getLevel(nameOrValue) {
|
||||
if (typeof nameOrValue === 'string') {
|
||||
return LEVELS_BY_NAME.get(nameOrValue) || Level.ALL
|
||||
}
|
||||
if (typeof nameOrValue !== 'number') {
|
||||
throw new TypeError('not a string or number')
|
||||
}
|
||||
for (let level of ALL_LEVELS) {
|
||||
if (nameOrValue >= level.value) {
|
||||
return level
|
||||
}
|
||||
}
|
||||
return Level.ALL
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes a single log entry.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Entry {
|
||||
/**
|
||||
* @param {(!Level|string|number)} level The entry level.
|
||||
* @param {string} message The log message.
|
||||
* @param {number=} opt_timestamp The time this entry was generated, in
|
||||
* milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the
|
||||
* current time will be used.
|
||||
* @param {string=} opt_type The log type, if known.
|
||||
*/
|
||||
constructor(level, message, opt_timestamp, opt_type) {
|
||||
this.level = level instanceof Level ? level : getLevel(level)
|
||||
this.message = message
|
||||
this.timestamp =
|
||||
typeof opt_timestamp === 'number' ? opt_timestamp : Date.now()
|
||||
this.type = opt_type || ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {{level: string, message: string, timestamp: number,
|
||||
* type: string}} The JSON representation of this entry.
|
||||
*/
|
||||
toJSON() {
|
||||
return {
|
||||
level: this.level.name,
|
||||
message: this.message,
|
||||
timestamp: this.timestamp,
|
||||
type: this.type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object used to log debugging messages. Loggers use a hierarchical,
|
||||
* dot-separated naming scheme. For instance, "foo" is considered the parent of
|
||||
* the "foo.bar" and an ancestor of "foo.bar.baz".
|
||||
*
|
||||
* Each logger may be assigned a {@linkplain #setLevel log level}, which
|
||||
* controls which level of messages will be reported to the
|
||||
* {@linkplain #addHandler handlers} attached to this instance. If a log level
|
||||
* is not explicitly set on a logger, it will inherit its parent.
|
||||
*
|
||||
* This class should never be directly instantiated. Instead, users should
|
||||
* obtain logger references using the {@linkplain ./logging.getLogger()
|
||||
* getLogger()} function.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Logger {
|
||||
/**
|
||||
* @param {string} name the name of this logger.
|
||||
* @param {Level=} opt_level the initial level for this logger.
|
||||
*/
|
||||
constructor(name, opt_level) {
|
||||
/** @private {string} */
|
||||
this.name_ = name
|
||||
|
||||
/** @private {Level} */
|
||||
this.level_ = opt_level || null
|
||||
|
||||
/** @private {Logger} */
|
||||
this.parent_ = null
|
||||
|
||||
/** @private {Set<function(!Entry)>} */
|
||||
this.handlers_ = null
|
||||
}
|
||||
|
||||
/** @return {string} the name of this logger. */
|
||||
getName() {
|
||||
return this.name_
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Level} level the new level for this logger, or `null` if the logger
|
||||
* should inherit its level from its parent logger.
|
||||
*/
|
||||
setLevel(level) {
|
||||
this.level_ = level
|
||||
}
|
||||
|
||||
/** @return {Level} the log level for this logger. */
|
||||
getLevel() {
|
||||
return this.level_
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Level} the effective level for this logger.
|
||||
*/
|
||||
getEffectiveLevel() {
|
||||
let logger = this
|
||||
let level
|
||||
do {
|
||||
level = logger.level_
|
||||
logger = logger.parent_
|
||||
} while (logger && !level)
|
||||
return level || Level.OFF
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Level} level the level to check.
|
||||
* @return {boolean} whether messages recorded at the given level are loggable
|
||||
* by this instance.
|
||||
*/
|
||||
isLoggable(level) {
|
||||
return (
|
||||
level.value !== Level.OFF.value &&
|
||||
level.value >= this.getEffectiveLevel().value
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a handler to this logger. The handler will be invoked for each message
|
||||
* logged with this instance, or any of its descendants.
|
||||
*
|
||||
* @param {function(!Entry)} handler the handler to add.
|
||||
*/
|
||||
addHandler(handler) {
|
||||
if (!this.handlers_) {
|
||||
this.handlers_ = new Set()
|
||||
}
|
||||
this.handlers_.add(handler)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a handler from this logger.
|
||||
*
|
||||
* @param {function(!Entry)} handler the handler to remove.
|
||||
* @return {boolean} whether a handler was successfully removed.
|
||||
*/
|
||||
removeHandler(handler) {
|
||||
if (!this.handlers_) {
|
||||
return false
|
||||
}
|
||||
return this.handlers_.delete(handler)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the given level. The message may be defined as a string
|
||||
* or as a function that will return the message. If a function is provided,
|
||||
* it will only be invoked if this logger's
|
||||
* {@linkplain #getEffectiveLevel() effective log level} includes the given
|
||||
* `level`.
|
||||
*
|
||||
* @param {!Level} level the level at which to log the message.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
log(level, loggable) {
|
||||
if (!this.isLoggable(level)) {
|
||||
return
|
||||
}
|
||||
let message =
|
||||
'[' +
|
||||
this.name_ +
|
||||
'] ' +
|
||||
(typeof loggable === 'function' ? loggable() : loggable)
|
||||
let entry = new Entry(level, message, Date.now())
|
||||
for (let logger = this; logger; logger = logger.parent_) {
|
||||
if (logger.handlers_) {
|
||||
for (let handler of logger.handlers_) {
|
||||
handler(entry)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.SEVERE} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
severe(loggable) {
|
||||
this.log(Level.SEVERE, loggable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.WARNING} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
warning(loggable) {
|
||||
this.log(Level.WARNING, loggable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.INFO} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
info(loggable) {
|
||||
this.log(Level.INFO, loggable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.DEBUG} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
debug(loggable) {
|
||||
this.log(Level.DEBUG, loggable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.FINE} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
fine(loggable) {
|
||||
this.log(Level.FINE, loggable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.FINER} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
finer(loggable) {
|
||||
this.log(Level.FINER, loggable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message at the {@link Level.FINEST} log level.
|
||||
* @param {(string|function(): string)} loggable the message to log, or a
|
||||
* function that will return the message.
|
||||
*/
|
||||
finest(loggable) {
|
||||
this.log(Level.FINEST, loggable)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintains a collection of loggers.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class LogManager {
|
||||
constructor() {
|
||||
/** @private {!Map<string, !Logger>} */
|
||||
this.loggers_ = new Map()
|
||||
this.root_ = new Logger('', Level.OFF)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a named logger, creating it in the process. This function will
|
||||
* implicitly create the requested logger, and any of its parents, if they
|
||||
* do not yet exist.
|
||||
*
|
||||
* @param {string} name the logger's name.
|
||||
* @return {!Logger} the requested logger.
|
||||
*/
|
||||
getLogger(name) {
|
||||
if (!name) {
|
||||
return this.root_
|
||||
}
|
||||
let parent = this.root_
|
||||
for (let i = name.indexOf('.'); i != -1; i = name.indexOf('.', i + 1)) {
|
||||
let parentName = name.substr(0, i)
|
||||
parent = this.createLogger_(parentName, parent)
|
||||
}
|
||||
return this.createLogger_(name, parent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new logger.
|
||||
*
|
||||
* @param {string} name the logger's name.
|
||||
* @param {!Logger} parent the logger's parent.
|
||||
* @return {!Logger} the new logger.
|
||||
* @private
|
||||
*/
|
||||
createLogger_(name, parent) {
|
||||
if (this.loggers_.has(name)) {
|
||||
return /** @type {!Logger} */ (this.loggers_.get(name))
|
||||
}
|
||||
let logger = new Logger(name, null)
|
||||
logger.parent_ = parent
|
||||
this.loggers_.set(name, logger)
|
||||
return logger
|
||||
}
|
||||
}
|
||||
|
||||
const logManager = new LogManager()
|
||||
|
||||
/**
|
||||
* Retrieves a named logger, creating it in the process. This function will
|
||||
* implicitly create the requested logger, and any of its parents, if they
|
||||
* do not yet exist.
|
||||
*
|
||||
* The log level will be unspecified for newly created loggers. Use
|
||||
* {@link Logger#setLevel(level)} to explicitly set a level.
|
||||
*
|
||||
* @param {string} name the logger's name.
|
||||
* @return {!Logger} the requested logger.
|
||||
*/
|
||||
function getLogger(name) {
|
||||
return logManager.getLogger(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads a number to ensure it has a minimum of two digits.
|
||||
*
|
||||
* @param {number} n the number to be padded.
|
||||
* @return {string} the padded number.
|
||||
*/
|
||||
function pad(n) {
|
||||
if (n >= 10) {
|
||||
return '' + n
|
||||
} else {
|
||||
return '0' + n
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs all messages to the Console API.
|
||||
* @param {!Entry} entry the entry to log.
|
||||
*/
|
||||
function consoleHandler(entry) {
|
||||
if (typeof console === 'undefined' || !console) {
|
||||
return
|
||||
}
|
||||
|
||||
var timestamp = new Date(entry.timestamp)
|
||||
var msg =
|
||||
'[' +
|
||||
timestamp.getUTCFullYear() +
|
||||
'-' +
|
||||
pad(timestamp.getUTCMonth() + 1) +
|
||||
'-' +
|
||||
pad(timestamp.getUTCDate()) +
|
||||
'T' +
|
||||
pad(timestamp.getUTCHours()) +
|
||||
':' +
|
||||
pad(timestamp.getUTCMinutes()) +
|
||||
':' +
|
||||
pad(timestamp.getUTCSeconds()) +
|
||||
'Z] ' +
|
||||
'[' +
|
||||
entry.level.name +
|
||||
'] ' +
|
||||
entry.message
|
||||
|
||||
var level = entry.level.value
|
||||
if (level >= Level.SEVERE.value) {
|
||||
console.error(msg)
|
||||
} else if (level >= Level.WARNING.value) {
|
||||
console.warn(msg)
|
||||
} else {
|
||||
console.log(msg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the console handler to the given logger. The console handler will log
|
||||
* all messages using the JavaScript Console API.
|
||||
*
|
||||
* @param {Logger=} opt_logger The logger to add the handler to; defaults
|
||||
* to the root logger.
|
||||
*/
|
||||
function addConsoleHandler(opt_logger) {
|
||||
let logger = opt_logger || logManager.root_
|
||||
logger.addHandler(consoleHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the console log handler from the given logger.
|
||||
*
|
||||
* @param {Logger=} opt_logger The logger to remove the handler from; defaults
|
||||
* to the root logger.
|
||||
* @see exports.addConsoleHandler
|
||||
*/
|
||||
function removeConsoleHandler(opt_logger) {
|
||||
let logger = opt_logger || logManager.root_
|
||||
logger.removeHandler(consoleHandler)
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the console log handler on the root logger.
|
||||
*/
|
||||
function installConsoleHandler() {
|
||||
addConsoleHandler(logManager.root_)
|
||||
}
|
||||
|
||||
/**
|
||||
* Common log types.
|
||||
* @enum {string}
|
||||
*/
|
||||
const Type = {
|
||||
/** Logs originating from the browser. */
|
||||
BROWSER: 'browser',
|
||||
/** Logs from a WebDriver client. */
|
||||
CLIENT: 'client',
|
||||
/** Logs from a WebDriver implementation. */
|
||||
DRIVER: 'driver',
|
||||
/** Logs related to performance. */
|
||||
PERFORMANCE: 'performance',
|
||||
/** Logs from the remote server. */
|
||||
SERVER: 'server',
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the log preferences for a WebDriver session.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class Preferences {
|
||||
constructor() {
|
||||
/** @private {!Map<string, !Level>} */
|
||||
this.prefs_ = new Map()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the desired logging level for a particular log type.
|
||||
* @param {(string|Type)} type The log type.
|
||||
* @param {(!Level|string|number)} level The desired log level.
|
||||
* @throws {TypeError} if `type` is not a `string`.
|
||||
*/
|
||||
setLevel(type, level) {
|
||||
if (typeof type !== 'string') {
|
||||
throw TypeError('specified log type is not a string: ' + typeof type)
|
||||
}
|
||||
this.prefs_.set(type, level instanceof Level ? level : getLevel(level))
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this instance to its JSON representation.
|
||||
* @return {!Object<string, string>} The JSON representation of this set of
|
||||
* preferences.
|
||||
*/
|
||||
toJSON() {
|
||||
let json = {}
|
||||
for (let key of this.prefs_.keys()) {
|
||||
json[key] = this.prefs_.get(key).name
|
||||
}
|
||||
return json
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Entry: Entry,
|
||||
Level: Level,
|
||||
LogManager: LogManager,
|
||||
Logger: Logger,
|
||||
Preferences: Preferences,
|
||||
Type: Type,
|
||||
addConsoleHandler: addConsoleHandler,
|
||||
getLevel: getLevel,
|
||||
getLogger: getLogger,
|
||||
installConsoleHandler: installConsoleHandler,
|
||||
removeConsoleHandler: removeConsoleHandler,
|
||||
}
|
||||
271
node_modules/selenium-webdriver/lib/promise.js
generated
vendored
Normal file
271
node_modules/selenium-webdriver/lib/promise.js
generated
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a handful of utility functions to simplify working
|
||||
* with promises.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { isObject, isPromise } = require('./util')
|
||||
|
||||
/**
|
||||
* Creates a promise that will be resolved at a set time in the future.
|
||||
* @param {number} ms The amount of time, in milliseconds, to wait before
|
||||
* resolving the promise.
|
||||
* @return {!Promise<void>} The promise.
|
||||
*/
|
||||
function delayed(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a function that expects a node-style callback as its final
|
||||
* argument. This callback expects two arguments: an error value (which will be
|
||||
* null if the call succeeded), and the success value as the second argument.
|
||||
* The callback will the resolve or reject the returned promise, based on its
|
||||
* arguments.
|
||||
* @param {!Function} fn The function to wrap.
|
||||
* @param {...?} args The arguments to apply to the function, excluding the
|
||||
* final callback.
|
||||
* @return {!Thenable} A promise that will be resolved with the
|
||||
* result of the provided function's callback.
|
||||
*/
|
||||
function checkedNodeCall(fn, ...args) {
|
||||
return new Promise(function (fulfill, reject) {
|
||||
try {
|
||||
fn(...args, function (error, value) {
|
||||
error ? reject(error) : fulfill(value)
|
||||
})
|
||||
} catch (ex) {
|
||||
reject(ex)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a listener to invoke when a promise is resolved, regardless
|
||||
* of whether the promise's value was successfully computed. This function
|
||||
* is synonymous with the {@code finally} clause in a synchronous API:
|
||||
*
|
||||
* // Synchronous API:
|
||||
* try {
|
||||
* doSynchronousWork();
|
||||
* } finally {
|
||||
* cleanUp();
|
||||
* }
|
||||
*
|
||||
* // Asynchronous promise API:
|
||||
* doAsynchronousWork().finally(cleanUp);
|
||||
*
|
||||
* __Note:__ similar to the {@code finally} clause, if the registered
|
||||
* callback returns a rejected promise or throws an error, it will silently
|
||||
* replace the rejection error (if any) from this promise:
|
||||
*
|
||||
* try {
|
||||
* throw Error('one');
|
||||
* } finally {
|
||||
* throw Error('two'); // Hides Error: one
|
||||
* }
|
||||
*
|
||||
* let p = Promise.reject(Error('one'));
|
||||
* promise.finally(p, function() {
|
||||
* throw Error('two'); // Hides Error: one
|
||||
* });
|
||||
*
|
||||
* @param {!IThenable<?>} promise The promise to add the listener to.
|
||||
* @param {function(): (R|IThenable<R>)} callback The function to call when
|
||||
* the promise is resolved.
|
||||
* @return {!Promise<R>} A promise that will be resolved with the callback
|
||||
* result.
|
||||
* @template R
|
||||
*/
|
||||
async function thenFinally(promise, callback) {
|
||||
try {
|
||||
await Promise.resolve(promise)
|
||||
return callback()
|
||||
} catch (e) {
|
||||
await callback()
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a function for each element in an array and inserts the result into a
|
||||
* new array, which is used as the fulfillment value of the promise returned
|
||||
* by this function.
|
||||
*
|
||||
* If the return value of the mapping function is a promise, this function
|
||||
* will wait for it to be fulfilled before inserting it into the new array.
|
||||
*
|
||||
* If the mapping function throws or returns a rejected promise, the
|
||||
* promise returned by this function will be rejected with the same reason.
|
||||
* Only the first failure will be reported; all subsequent errors will be
|
||||
* silently ignored.
|
||||
*
|
||||
* @param {!(Array<TYPE>|IThenable<!Array<TYPE>>)} array The array to iterate
|
||||
* over, or a promise that will resolve to said array.
|
||||
* @param {function(this: SELF, TYPE, number, !Array<TYPE>): ?} fn The
|
||||
* function to call for each element in the array. This function should
|
||||
* expect three arguments (the element, the index, and the array itself.
|
||||
* @param {SELF=} self The object to be used as the value of 'this' within `fn`.
|
||||
* @template TYPE, SELF
|
||||
*/
|
||||
async function map(array, fn, self = undefined) {
|
||||
const v = await Promise.resolve(array)
|
||||
if (!Array.isArray(v)) {
|
||||
throw TypeError('not an array')
|
||||
}
|
||||
|
||||
const arr = /** @type {!Array} */ (v)
|
||||
const values = []
|
||||
|
||||
for (const [index, item] of arr.entries()) {
|
||||
values.push(await Promise.resolve(fn.call(self, item, index, arr)))
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a function for each element in an array, and if the function returns
|
||||
* true adds the element to a new array.
|
||||
*
|
||||
* If the return value of the filter function is a promise, this function
|
||||
* will wait for it to be fulfilled before determining whether to insert the
|
||||
* element into the new array.
|
||||
*
|
||||
* If the filter function throws or returns a rejected promise, the promise
|
||||
* returned by this function will be rejected with the same reason. Only the
|
||||
* first failure will be reported; all subsequent errors will be silently
|
||||
* ignored.
|
||||
*
|
||||
* @param {!(Array<TYPE>|IThenable<!Array<TYPE>>)} array The array to iterate
|
||||
* over, or a promise that will resolve to said array.
|
||||
* @param {function(this: SELF, TYPE, number, !Array<TYPE>): (
|
||||
* boolean|IThenable<boolean>)} fn The function
|
||||
* to call for each element in the array.
|
||||
* @param {SELF=} self The object to be used as the value of 'this' within `fn`.
|
||||
* @template TYPE, SELF
|
||||
*/
|
||||
async function filter(array, fn, self = undefined) {
|
||||
const v = await Promise.resolve(array)
|
||||
if (!Array.isArray(v)) {
|
||||
throw TypeError('not an array')
|
||||
}
|
||||
|
||||
const arr = /** @type {!Array} */ (v)
|
||||
const values = []
|
||||
|
||||
for (const [index, item] of arr.entries()) {
|
||||
const isConditionTrue = await Promise.resolve(
|
||||
fn.call(self, item, index, arr)
|
||||
)
|
||||
if (isConditionTrue) {
|
||||
values.push(item)
|
||||
}
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a promise that will be resolved with the input value in a
|
||||
* fully-resolved state. If the value is an array, each element will be fully
|
||||
* resolved. Likewise, if the value is an object, all keys will be fully
|
||||
* resolved. In both cases, all nested arrays and objects will also be
|
||||
* fully resolved. All fields are resolved in place; the returned promise will
|
||||
* resolve on {@code value} and not a copy.
|
||||
*
|
||||
* Warning: This function makes no checks against objects that contain
|
||||
* cyclical references:
|
||||
*
|
||||
* var value = {};
|
||||
* value['self'] = value;
|
||||
* promise.fullyResolved(value); // Stack overflow.
|
||||
*
|
||||
* @param {*} value The value to fully resolve.
|
||||
* @return {!Thenable} A promise for a fully resolved version
|
||||
* of the input value.
|
||||
*/
|
||||
async function fullyResolved(value) {
|
||||
value = await Promise.resolve(value)
|
||||
if (Array.isArray(value)) {
|
||||
return fullyResolveKeys(/** @type {!Array} */ (value))
|
||||
}
|
||||
|
||||
if (isObject(value)) {
|
||||
return fullyResolveKeys(/** @type {!Object} */ (value))
|
||||
}
|
||||
|
||||
if (typeof value === 'function') {
|
||||
return fullyResolveKeys(/** @type {!Object} */ (value))
|
||||
}
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!(Array|Object)} obj the object to resolve.
|
||||
* @return {!Thenable} A promise that will be resolved with the
|
||||
* input object once all of its values have been fully resolved.
|
||||
*/
|
||||
async function fullyResolveKeys(obj) {
|
||||
const isArray = Array.isArray(obj)
|
||||
const numKeys = isArray ? obj.length : Object.keys(obj).length
|
||||
|
||||
if (!numKeys) {
|
||||
return obj
|
||||
}
|
||||
|
||||
async function forEachProperty(obj, fn) {
|
||||
for (let key in obj) {
|
||||
await fn(obj[key], key)
|
||||
}
|
||||
}
|
||||
|
||||
async function forEachElement(arr, fn) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
await fn(arr[i], i)
|
||||
}
|
||||
}
|
||||
|
||||
const forEachKey = isArray ? forEachElement : forEachProperty
|
||||
await forEachKey(obj, async function (partialValue, key) {
|
||||
if (
|
||||
!Array.isArray(partialValue) &&
|
||||
(!partialValue || typeof partialValue !== 'object')
|
||||
) {
|
||||
return
|
||||
}
|
||||
obj[key] = await fullyResolved(partialValue)
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
checkedNodeCall,
|
||||
delayed,
|
||||
filter,
|
||||
finally: thenFinally,
|
||||
fullyResolved,
|
||||
isPromise,
|
||||
map,
|
||||
}
|
||||
222
node_modules/selenium-webdriver/lib/proxy.js
generated
vendored
Normal file
222
node_modules/selenium-webdriver/lib/proxy.js
generated
vendored
Normal file
@@ -0,0 +1,222 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines functions for configuring a webdriver proxy:
|
||||
*
|
||||
* const proxy = require('selenium-webdriver/proxy');
|
||||
* const {Capabilities} = require('selenium-webdriver');
|
||||
*
|
||||
* let capabilities = new Capabilities();
|
||||
* capabilities.setProxy(proxy.manual({http: 'host:1234'});
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Supported {@linkplain Config proxy configuration} types.
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
const Type = {
|
||||
AUTODETECT: 'autodetect',
|
||||
DIRECT: 'direct',
|
||||
MANUAL: 'manual',
|
||||
PAC: 'pac',
|
||||
SYSTEM: 'system',
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes how a proxy should be configured for a WebDriver session.
|
||||
* @record
|
||||
*/
|
||||
function Config() {}
|
||||
|
||||
/**
|
||||
* The proxy type.
|
||||
* @type {Type}
|
||||
*/
|
||||
Config.prototype.proxyType
|
||||
|
||||
/**
|
||||
* Describes how to configure a PAC proxy.
|
||||
* @record
|
||||
* @extends {Config}
|
||||
*/
|
||||
function PacConfig() {}
|
||||
|
||||
/**
|
||||
* URL for the PAC file to use.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
PacConfig.prototype.proxyAutoconfigUrl
|
||||
|
||||
/**
|
||||
* Record object that defines a manual proxy configuration. Manual
|
||||
* configurations can be easily created using either the
|
||||
* {@link ./proxy.manual proxy.manual()} or {@link ./proxy.socks proxy.socks()}
|
||||
* factory method.
|
||||
*
|
||||
* @record
|
||||
* @extends {Config}
|
||||
*/
|
||||
function ManualConfig() {}
|
||||
|
||||
/**
|
||||
* The proxy host for FTP requests.
|
||||
*
|
||||
* @type {(string|undefined)}
|
||||
*/
|
||||
ManualConfig.prototype.ftpProxy
|
||||
|
||||
/**
|
||||
* The proxy host for HTTP requests.
|
||||
*
|
||||
* @type {(string|undefined)}
|
||||
*/
|
||||
ManualConfig.prototype.httpProxy
|
||||
|
||||
/**
|
||||
* An array of hosts which should bypass all proxies.
|
||||
*
|
||||
* @type {(Array<string>|undefined)}
|
||||
*/
|
||||
ManualConfig.prototype.noProxy
|
||||
|
||||
/**
|
||||
* The proxy host for HTTPS requests.
|
||||
*
|
||||
* @type {(string|undefined)}
|
||||
*/
|
||||
ManualConfig.prototype.sslProxy
|
||||
|
||||
/**
|
||||
* Defines the host and port for the SOCKS proxy to use.
|
||||
*
|
||||
* @type {(number|undefined)}
|
||||
*/
|
||||
ManualConfig.prototype.socksProxy
|
||||
|
||||
/**
|
||||
* Defines the SOCKS proxy version. Must be a number in the range [0, 255].
|
||||
*
|
||||
* @type {(number|undefined)}
|
||||
*/
|
||||
ManualConfig.prototype.socksVersion
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
/** @const */ exports.Config = Config
|
||||
/** @const */ exports.ManualConfig = ManualConfig
|
||||
/** @const */ exports.PacConfig = PacConfig
|
||||
/** @const */ exports.Type = Type
|
||||
|
||||
/**
|
||||
* Configures WebDriver to bypass all browser proxies.
|
||||
* @return {!Config} A new proxy configuration object.
|
||||
*/
|
||||
function direct() {
|
||||
return { proxyType: Type.DIRECT }
|
||||
}
|
||||
|
||||
/**
|
||||
* Manually configures the browser proxy. The following options are
|
||||
* supported:
|
||||
*
|
||||
* - `ftp`: Proxy host to use for FTP requests
|
||||
* - `http`: Proxy host to use for HTTP requests
|
||||
* - `https`: Proxy host to use for HTTPS requests
|
||||
* - `bypass`: A list of hosts requests should directly connect to,
|
||||
* bypassing any other proxies for that request. May be specified as a
|
||||
* comma separated string, or a list of strings.
|
||||
*
|
||||
* Behavior is undefined for FTP, HTTP, and HTTPS requests if the
|
||||
* corresponding key is omitted from the configuration options.
|
||||
*
|
||||
* @param {{ftp: (string|undefined),
|
||||
* http: (string|undefined),
|
||||
* https: (string|undefined),
|
||||
* bypass: (Array<string>|undefined)}} options Proxy
|
||||
* configuration options.
|
||||
* @return {!ManualConfig} A new proxy configuration object.
|
||||
*/
|
||||
function manual({ ftp, http, https, bypass }) {
|
||||
return {
|
||||
proxyType: Type.MANUAL,
|
||||
ftpProxy: ftp,
|
||||
httpProxy: http,
|
||||
sslProxy: https,
|
||||
noProxy: bypass,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a proxy configuration for a socks proxy.
|
||||
*
|
||||
* __Example:__
|
||||
*
|
||||
* const {Capabilities} = require('selenium-webdriver');
|
||||
* const proxy = require('selenium-webdriver/lib/proxy');
|
||||
*
|
||||
* let capabilities = new Capabilities();
|
||||
* capabilities.setProxy(proxy.socks('localhost:1234'));
|
||||
*
|
||||
* // Or, to include authentication.
|
||||
* capabilities.setProxy(proxy.socks('bob:password@localhost:1234'));
|
||||
*
|
||||
*
|
||||
* @param {string} socksProxy The proxy host, in the form `hostname:port`.
|
||||
* @param {number=} socksVersion The SOCKS proxy version.
|
||||
* @return {!ManualConfig} A new proxy configuration object.
|
||||
* @see https://en.wikipedia.org/wiki/SOCKS
|
||||
*/
|
||||
function socks(socksProxy, socksVersion = undefined) {
|
||||
return /** @type {!Config} */ ({
|
||||
proxyType: Type.MANUAL,
|
||||
socksProxy,
|
||||
socksVersion,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures WebDriver to configure the browser proxy using the PAC file at
|
||||
* the given URL.
|
||||
* @param {string} proxyAutoconfigUrl URL for the PAC proxy to use.
|
||||
* @return {!PacConfig} A new proxy configuration object.
|
||||
*/
|
||||
function pac(proxyAutoconfigUrl) {
|
||||
return { proxyType: Type.PAC, proxyAutoconfigUrl }
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures WebDriver to use the current system's proxy.
|
||||
* @return {!Config} A new proxy configuration object.
|
||||
*/
|
||||
function system() {
|
||||
return { proxyType: Type.SYSTEM }
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
system,
|
||||
pac,
|
||||
socks,
|
||||
manual,
|
||||
direct,
|
||||
}
|
||||
466
node_modules/selenium-webdriver/lib/select.js
generated
vendored
Normal file
466
node_modules/selenium-webdriver/lib/select.js
generated
vendored
Normal file
@@ -0,0 +1,466 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/*
|
||||
* Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The SFC licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { By, escapeCss } = require('./by')
|
||||
const error = require('./error')
|
||||
|
||||
/**
|
||||
* ISelect interface makes a protocol for all kind of select elements (standard html and custom
|
||||
* model)
|
||||
*
|
||||
* @interface
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
class ISelect {
|
||||
/**
|
||||
* @return {!Promise<boolean>} Whether this select element supports selecting multiple options at the same time? This
|
||||
* is done by checking the value of the "multiple" attribute.
|
||||
*/
|
||||
isMultiple() {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Array<!WebElement>>} All options belonging to this select tag
|
||||
*/
|
||||
getOptions() {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* @return {!Promise<!Array<!WebElement>>} All selected options belonging to this select tag
|
||||
*/
|
||||
getAllSelectedOptions() {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* @return {!Promise<!WebElement>} The first selected option in this select tag (or the currently selected option in a
|
||||
* normal select)
|
||||
*/
|
||||
getFirstSelectedOption() {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Select all options that display text matching the argument. That is, when given "Bar" this
|
||||
* would select an option like:
|
||||
*
|
||||
* <option value="foo">Bar</option>
|
||||
*
|
||||
* @param {string} text The visible text to match against
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
selectByVisibleText(text) {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Select all options that have a value matching the argument. That is, when given "foo" this
|
||||
* would select an option like:
|
||||
*
|
||||
* <option value="foo">Bar</option>
|
||||
*
|
||||
* @param {string} value The value to match against
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
selectByValue(value) {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Select the option at the given index. This is done by examining the "index" attribute of an
|
||||
* element, and not merely by counting.
|
||||
*
|
||||
* @param {Number} index The option at this index will be selected
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
selectByIndex(index) {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Clear all selected entries. This is only valid when the SELECT supports multiple selections.
|
||||
*
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
deselectAll() {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Deselect all options that display text matching the argument. That is, when given "Bar" this
|
||||
* would deselect an option like:
|
||||
*
|
||||
* <option value="foo">Bar</option>
|
||||
*
|
||||
* @param {string} text The visible text to match against
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
deselectByVisibleText(text) {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Deselect all options that have a value matching the argument. That is, when given "foo" this
|
||||
* would deselect an option like:
|
||||
*
|
||||
* @param {string} value The value to match against
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
deselectByValue(value) {} // eslint-disable-line
|
||||
|
||||
/**
|
||||
* Deselect the option at the given index. This is done by examining the "index" attribute of an
|
||||
* element, and not merely by counting.
|
||||
*
|
||||
* @param {Number} index The option at this index will be deselected
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
deselectByIndex(index) {} // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
* @implements ISelect
|
||||
*/
|
||||
class Select {
|
||||
/**
|
||||
* Create an Select Element
|
||||
* @param {WebElement} element Select WebElement.
|
||||
*/
|
||||
constructor(element) {
|
||||
this.element = element
|
||||
|
||||
this.element.getAttribute('tagName').then(function (tagName) {
|
||||
if (tagName.toLowerCase() !== 'select') {
|
||||
throw new Error(`Select only works on <select> elements`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Select option with specified index.
|
||||
*
|
||||
* <example>
|
||||
<select id="selectbox">
|
||||
<option value="1">Option 1</option>
|
||||
<option value="2">Option 2</option>
|
||||
<option value="3">Option 3</option>
|
||||
</select>
|
||||
const selectBox = await driver.findElement(By.id("selectbox"));
|
||||
await selectObject.selectByIndex(1);
|
||||
* </example>
|
||||
*
|
||||
* @param index
|
||||
*/
|
||||
async selectByIndex(index) {
|
||||
if (index < 0) {
|
||||
throw new Error('Index needs to be 0 or any other positive number')
|
||||
}
|
||||
|
||||
let options = await this.element.findElements(By.tagName('option'))
|
||||
|
||||
if (options.length === 0) {
|
||||
throw new Error("Select element doesn't contain any option element")
|
||||
}
|
||||
|
||||
if (options.length - 1 < index) {
|
||||
throw new Error(
|
||||
`Option with index "${index}" not found. Select element only contains ${
|
||||
options.length - 1
|
||||
} option elements`
|
||||
)
|
||||
}
|
||||
|
||||
for (let option of options) {
|
||||
if ((await option.getAttribute('index')) === index.toString()) {
|
||||
await this.setSelected(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Select option by specific value.
|
||||
*
|
||||
* <example>
|
||||
<select id="selectbox">
|
||||
<option value="1">Option 1</option>
|
||||
<option value="2">Option 2</option>
|
||||
<option value="3">Option 3</option>
|
||||
</select>
|
||||
const selectBox = await driver.findElement(By.id("selectbox"));
|
||||
await selectObject.selectByVisibleText("Option 2");
|
||||
* </example>
|
||||
*
|
||||
*
|
||||
* @param {string} value value of option element to be selected
|
||||
*/
|
||||
async selectByValue(value) {
|
||||
let matched = false
|
||||
let isMulti = await this.isMultiple()
|
||||
|
||||
let options = await this.element.findElements({
|
||||
css: 'option[value =' + escapeCss(value) + ']',
|
||||
})
|
||||
|
||||
for (let option of options) {
|
||||
await this.setSelected(option)
|
||||
|
||||
if (!isMulti) {
|
||||
return
|
||||
}
|
||||
matched = true
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
throw new Error(`Cannot locate option with value: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Select option with displayed text matching the argument.
|
||||
*
|
||||
* <example>
|
||||
<select id="selectbox">
|
||||
<option value="1">Option 1</option>
|
||||
<option value="2">Option 2</option>
|
||||
<option value="3">Option 3</option>
|
||||
</select>
|
||||
const selectBox = await driver.findElement(By.id("selectbox"));
|
||||
await selectObject.selectByVisibleText("Option 2");
|
||||
* </example>
|
||||
*
|
||||
* @param {String|Number} text text of option element to get selected
|
||||
*
|
||||
*/
|
||||
async selectByVisibleText(text) {
|
||||
text = typeof text === 'number' ? text.toString() : text
|
||||
|
||||
const normalized = text
|
||||
.trim() // strip leading and trailing white-space characters
|
||||
.replace(/\s+/, ' ') // replace sequences of whitespace characters by a single space
|
||||
|
||||
/**
|
||||
* find option element using xpath
|
||||
*/
|
||||
const formatted = /"/.test(normalized)
|
||||
? 'concat("' + normalized.split('"').join('", \'"\', "') + '")'
|
||||
: `"${normalized}"`
|
||||
const dotFormat = `[. = ${formatted}]`
|
||||
const spaceFormat = `[normalize-space(text()) = ${formatted}]`
|
||||
|
||||
const selections = [
|
||||
`./option${dotFormat}`,
|
||||
`./option${spaceFormat}`,
|
||||
`./optgroup/option${dotFormat}`,
|
||||
`./optgroup/option${spaceFormat}`,
|
||||
]
|
||||
|
||||
const optionElement = await this.element.findElement({
|
||||
xpath: selections.join('|'),
|
||||
})
|
||||
await this.setSelected(optionElement)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all options belonging to this select tag
|
||||
* @returns {!Promise<!Array<!WebElement>>}
|
||||
*/
|
||||
async getOptions() {
|
||||
return await this.element.findElements({ tagName: 'option' })
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean value if the select tag is multiple
|
||||
* @returns {Promise<boolean>}
|
||||
*/
|
||||
async isMultiple() {
|
||||
return (await this.element.getAttribute('multiple')) !== null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all selected options belonging to this select tag
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async getAllSelectedOptions() {
|
||||
const opts = await this.getOptions()
|
||||
const results = []
|
||||
for (let options of opts) {
|
||||
if (await options.isSelected()) {
|
||||
results.push(options)
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first Selected Option
|
||||
* @returns {Promise<Element>}
|
||||
*/
|
||||
async getFirstSelectedOption() {
|
||||
return (await this.getAllSelectedOptions())[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Deselects all selected options
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deselectAll() {
|
||||
if (!this.isMultiple()) {
|
||||
throw new Error('You may only deselect all options of a multi-select')
|
||||
}
|
||||
|
||||
const options = await this.getOptions()
|
||||
|
||||
for (let option of options) {
|
||||
if (await option.isSelected()) {
|
||||
await option.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string|Number}text text of option to deselect
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deselectByVisibleText(text) {
|
||||
if (!(await this.isMultiple())) {
|
||||
throw new Error('You may only deselect options of a multi-select')
|
||||
}
|
||||
|
||||
/**
|
||||
* convert value into string
|
||||
*/
|
||||
text = typeof text === 'number' ? text.toString() : text
|
||||
|
||||
const normalized = text
|
||||
.trim() // strip leading and trailing white-space characters
|
||||
.replace(/\s+/, ' ') // replace sequences of whitespace characters by a single space
|
||||
|
||||
/**
|
||||
* find option element using xpath
|
||||
*/
|
||||
const formatted = /"/.test(normalized)
|
||||
? 'concat("' + normalized.split('"').join('", \'"\', "') + '")'
|
||||
: `"${normalized}"`
|
||||
const dotFormat = `[. = ${formatted}]`
|
||||
const spaceFormat = `[normalize-space(text()) = ${formatted}]`
|
||||
|
||||
const selections = [
|
||||
`./option${dotFormat}`,
|
||||
`./option${spaceFormat}`,
|
||||
`./optgroup/option${dotFormat}`,
|
||||
`./optgroup/option${spaceFormat}`,
|
||||
]
|
||||
|
||||
const optionElement = await this.element.findElement({
|
||||
xpath: selections.join('|'),
|
||||
})
|
||||
if (await optionElement.isSelected()) {
|
||||
await optionElement.click()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Number} index index of option element to deselect
|
||||
* Deselect the option at the given index.
|
||||
* This is done by examining the "index"
|
||||
* attribute of an element, and not merely by counting.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deselectByIndex(index) {
|
||||
if (!(await this.isMultiple())) {
|
||||
throw new Error('You may only deselect options of a multi-select')
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
throw new Error('Index needs to be 0 or any other positive number')
|
||||
}
|
||||
|
||||
let options = await this.element.findElements(By.tagName('option'))
|
||||
|
||||
if (options.length === 0) {
|
||||
throw new Error("Select element doesn't contain any option element")
|
||||
}
|
||||
|
||||
if (options.length - 1 < index) {
|
||||
throw new Error(
|
||||
`Option with index "${index}" not found. Select element only contains ${
|
||||
options.length - 1
|
||||
} option elements`
|
||||
)
|
||||
}
|
||||
|
||||
for (let option of options) {
|
||||
if ((await option.getAttribute('index')) === index.toString()) {
|
||||
if (await option.isSelected()) {
|
||||
await option.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} value value of an option to deselect
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async deselectByValue(value) {
|
||||
if (!(await this.isMultiple())) {
|
||||
throw new Error('You may only deselect options of a multi-select')
|
||||
}
|
||||
|
||||
let matched = false
|
||||
|
||||
let options = await this.element.findElements({
|
||||
css: 'option[value =' + escapeCss(value) + ']',
|
||||
})
|
||||
|
||||
for (let option of options) {
|
||||
if (await option.isSelected()) {
|
||||
await option.click()
|
||||
}
|
||||
matched = true
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
throw new Error(`Cannot locate option with value: ${value}`)
|
||||
}
|
||||
}
|
||||
|
||||
async setSelected(option) {
|
||||
if (!(await option.isSelected())) {
|
||||
if (!(await option.isEnabled())) {
|
||||
throw new error.UnsupportedOperationError(
|
||||
`You may not select a disabled option`
|
||||
)
|
||||
}
|
||||
await option.click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Select }
|
||||
77
node_modules/selenium-webdriver/lib/session.js
generated
vendored
Normal file
77
node_modules/selenium-webdriver/lib/session.js
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const { Capabilities } = require('./capabilities')
|
||||
|
||||
/**
|
||||
* Contains information about a single WebDriver session.
|
||||
*/
|
||||
class Session {
|
||||
/**
|
||||
* @param {string} id The session ID.
|
||||
* @param {!./capabilities.Capabilities} capabilities
|
||||
* The session capabilities.
|
||||
*/
|
||||
constructor(id, capabilities) {
|
||||
/** @private {string} */
|
||||
this.id_ = id
|
||||
|
||||
/** @private {!Capabilities} */
|
||||
this.caps_ =
|
||||
capabilities instanceof Capabilities
|
||||
? /** @type {!Capabilities} */ (capabilities)
|
||||
: new Capabilities(capabilities)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string} This session's ID.
|
||||
*/
|
||||
getId() {
|
||||
return this.id_
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Capabilities} This session's capabilities.
|
||||
*/
|
||||
getCapabilities() {
|
||||
return this.caps_
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value of a specific capability.
|
||||
* @param {string} key The capability to retrieve.
|
||||
* @return {*} The capability value.
|
||||
*/
|
||||
getCapability(key) {
|
||||
return this.caps_.get(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JSON representation of this object, which is just the string
|
||||
* session ID.
|
||||
* @return {string} The JSON representation of this Session.
|
||||
*/
|
||||
toJSON() {
|
||||
return this.getId()
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = { Session }
|
||||
37
node_modules/selenium-webdriver/lib/symbols.js
generated
vendored
Normal file
37
node_modules/selenium-webdriver/lib/symbols.js
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* @fileoverview Defines well-known symbols used within the selenium-webdriver
|
||||
* library.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
/**
|
||||
* The serialize symbol specifies a method that returns an object's serialized
|
||||
* representation. If an object's serialized form is not immediately
|
||||
* available, the serialize method will return a promise that will be resolved
|
||||
* with the serialized form.
|
||||
*
|
||||
* Note that the described method is analogous to objects that define a
|
||||
* `toJSON()` method, except the serialized result may be a promise, or
|
||||
* another object with a promised property.
|
||||
*/
|
||||
serialize: Symbol('serialize'),
|
||||
}
|
||||
452
node_modules/selenium-webdriver/lib/until.js
generated
vendored
Normal file
452
node_modules/selenium-webdriver/lib/until.js
generated
vendored
Normal file
@@ -0,0 +1,452 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines common conditions for use with
|
||||
* {@link webdriver.WebDriver#wait WebDriver wait}.
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* driver.get('http://www.google.com/ncr');
|
||||
*
|
||||
* var query = driver.wait(until.elementLocated(By.name('q')));
|
||||
* query.sendKeys('webdriver\n');
|
||||
*
|
||||
* driver.wait(until.titleIs('webdriver - Google Search'));
|
||||
*
|
||||
* To define a custom condition, simply call WebDriver.wait with a function
|
||||
* that will eventually return a truthy-value (neither null, undefined, false,
|
||||
* 0, or the empty string):
|
||||
*
|
||||
* driver.wait(function() {
|
||||
* return driver.getTitle().then(function(title) {
|
||||
* return title === 'webdriver - Google Search';
|
||||
* });
|
||||
* }, 1000);
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const by = require('./by')
|
||||
const error = require('./error')
|
||||
const webdriver = require('./webdriver'),
|
||||
Condition = webdriver.Condition,
|
||||
WebElementCondition = webdriver.WebElementCondition
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait until the input driver is able to switch
|
||||
* to the designated frame. The target frame may be specified as
|
||||
*
|
||||
* 1. a numeric index into
|
||||
* [window.frames](https://developer.mozilla.org/en-US/docs/Web/API/Window.frames)
|
||||
* for the currently selected frame.
|
||||
* 2. a {@link ./webdriver.WebElement}, which must reference a FRAME or IFRAME
|
||||
* element on the current page.
|
||||
* 3. a locator which may be used to first locate a FRAME or IFRAME on the
|
||||
* current page before attempting to switch to it.
|
||||
*
|
||||
* Upon successful resolution of this condition, the driver will be left
|
||||
* focused on the new frame.
|
||||
*
|
||||
* @param {!(number|./webdriver.WebElement|By|
|
||||
* function(!./webdriver.WebDriver): !./webdriver.WebElement)} frame
|
||||
* The frame identifier.
|
||||
* @return {!Condition<boolean>} A new condition.
|
||||
*/
|
||||
function ableToSwitchToFrame(frame) {
|
||||
let condition
|
||||
if (typeof frame === 'number' || frame instanceof webdriver.WebElement) {
|
||||
condition = (driver) => attemptToSwitchFrames(driver, frame)
|
||||
} else {
|
||||
condition = function (driver) {
|
||||
let locator = /** @type {!(By|Function)} */ (frame)
|
||||
return driver.findElements(locator).then(function (els) {
|
||||
if (els.length) {
|
||||
return attemptToSwitchFrames(driver, els[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return new Condition('to be able to switch to frame', condition)
|
||||
|
||||
function attemptToSwitchFrames(driver, frame) {
|
||||
return driver
|
||||
.switchTo()
|
||||
.frame(frame)
|
||||
.then(
|
||||
function () {
|
||||
return true
|
||||
},
|
||||
function (e) {
|
||||
if (!(e instanceof error.NoSuchFrameError)) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that waits for an alert to be opened. Upon success, the
|
||||
* returned promise will be fulfilled with the handle for the opened alert.
|
||||
*
|
||||
* @return {!Condition<!./webdriver.Alert>} The new condition.
|
||||
*/
|
||||
function alertIsPresent() {
|
||||
return new Condition('for alert to be present', function (driver) {
|
||||
return driver
|
||||
.switchTo()
|
||||
.alert()
|
||||
.catch(function (e) {
|
||||
if (
|
||||
!(
|
||||
e instanceof error.NoSuchAlertError ||
|
||||
// XXX: Workaround for GeckoDriver error `TypeError: can't convert null
|
||||
// to object`. For more details, see
|
||||
// https://github.com/SeleniumHQ/selenium/pull/2137
|
||||
(e instanceof error.WebDriverError &&
|
||||
e.message === `can't convert null to object`)
|
||||
)
|
||||
) {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the current page's title to match the
|
||||
* given value.
|
||||
*
|
||||
* @param {string} title The expected page title.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function titleIs(title) {
|
||||
return new Condition('for title to be ' + JSON.stringify(title), function (
|
||||
driver
|
||||
) {
|
||||
return driver.getTitle().then(function (t) {
|
||||
return t === title
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the current page's title to contain
|
||||
* the given substring.
|
||||
*
|
||||
* @param {string} substr The substring that should be present in the page
|
||||
* title.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function titleContains(substr) {
|
||||
return new Condition(
|
||||
'for title to contain ' + JSON.stringify(substr),
|
||||
function (driver) {
|
||||
return driver.getTitle().then(function (title) {
|
||||
return title.indexOf(substr) !== -1
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the current page's title to match the
|
||||
* given regular expression.
|
||||
*
|
||||
* @param {!RegExp} regex The regular expression to test against.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function titleMatches(regex) {
|
||||
return new Condition('for title to match ' + regex, function (driver) {
|
||||
return driver.getTitle().then(function (title) {
|
||||
return regex.test(title)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the current page's url to match the
|
||||
* given value.
|
||||
*
|
||||
* @param {string} url The expected page url.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function urlIs(url) {
|
||||
return new Condition('for URL to be ' + JSON.stringify(url), function (
|
||||
driver
|
||||
) {
|
||||
return driver.getCurrentUrl().then(function (u) {
|
||||
return u === url
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the current page's url to contain
|
||||
* the given substring.
|
||||
*
|
||||
* @param {string} substrUrl The substring that should be present in the current
|
||||
* URL.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function urlContains(substrUrl) {
|
||||
return new Condition(
|
||||
'for URL to contain ' + JSON.stringify(substrUrl),
|
||||
function (driver) {
|
||||
return driver.getCurrentUrl().then(function (url) {
|
||||
return url && url.includes(substrUrl)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the current page's url to match the
|
||||
* given regular expression.
|
||||
*
|
||||
* @param {!RegExp} regex The regular expression to test against.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function urlMatches(regex) {
|
||||
return new Condition('for URL to match ' + regex, function (driver) {
|
||||
return driver.getCurrentUrl().then(function (url) {
|
||||
return regex.test(url)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will loop until an element is
|
||||
* {@link ./webdriver.WebDriver#findElement found} with the given locator.
|
||||
*
|
||||
* @param {!(By|Function)} locator The locator to use.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
*/
|
||||
function elementLocated(locator) {
|
||||
locator = by.checkedLocator(locator)
|
||||
let locatorStr =
|
||||
typeof locator === 'function' ? 'by function()' : locator + ''
|
||||
return new WebElementCondition(
|
||||
'for element to be located ' + locatorStr,
|
||||
function (driver) {
|
||||
return driver.findElements(locator).then(function (elements) {
|
||||
return elements[0]
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will loop until at least one element is
|
||||
* {@link ./webdriver.WebDriver#findElement found} with the given locator.
|
||||
*
|
||||
* @param {!(By|Function)} locator The locator to use.
|
||||
* @return {!Condition<!Array<!./webdriver.WebElement>>} The new
|
||||
* condition.
|
||||
*/
|
||||
function elementsLocated(locator) {
|
||||
locator = by.checkedLocator(locator)
|
||||
let locatorStr =
|
||||
typeof locator === 'function' ? 'by function()' : locator + ''
|
||||
return new Condition(
|
||||
'for at least one element to be located ' + locatorStr,
|
||||
function (driver) {
|
||||
return driver.findElements(locator).then(function (elements) {
|
||||
return elements.length > 0 ? elements : null
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to become stale. An
|
||||
* element is considered stale once it is removed from the DOM, or a new page
|
||||
* has loaded.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element that should become stale.
|
||||
* @return {!Condition<boolean>} The new condition.
|
||||
*/
|
||||
function stalenessOf(element) {
|
||||
return new Condition('element to become stale', function () {
|
||||
return element.getTagName().then(
|
||||
function () {
|
||||
return false
|
||||
},
|
||||
function (e) {
|
||||
if (e instanceof error.StaleElementReferenceError) {
|
||||
return true
|
||||
}
|
||||
throw e
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to become visible.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see ./webdriver.WebDriver#isDisplayed
|
||||
*/
|
||||
function elementIsVisible(element) {
|
||||
return new WebElementCondition('until element is visible', function () {
|
||||
return element.isDisplayed().then((v) => (v ? element : null))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to be in the DOM,
|
||||
* yet not visible to the user.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see ./webdriver.WebDriver#isDisplayed
|
||||
*/
|
||||
function elementIsNotVisible(element) {
|
||||
return new WebElementCondition('until element is not visible', function () {
|
||||
return element.isDisplayed().then((v) => (v ? null : element))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to be enabled.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#isEnabled
|
||||
*/
|
||||
function elementIsEnabled(element) {
|
||||
return new WebElementCondition('until element is enabled', function () {
|
||||
return element.isEnabled().then((v) => (v ? element : null))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to be disabled.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#isEnabled
|
||||
*/
|
||||
function elementIsDisabled(element) {
|
||||
return new WebElementCondition('until element is disabled', function () {
|
||||
return element.isEnabled().then((v) => (v ? null : element))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to be selected.
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#isSelected
|
||||
*/
|
||||
function elementIsSelected(element) {
|
||||
return new WebElementCondition('until element is selected', function () {
|
||||
return element.isSelected().then((v) => (v ? element : null))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element to be deselected.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#isSelected
|
||||
*/
|
||||
function elementIsNotSelected(element) {
|
||||
return new WebElementCondition('until element is not selected', function () {
|
||||
return element.isSelected().then((v) => (v ? null : element))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element's
|
||||
* {@link webdriver.WebDriver#getText visible text} to match the given
|
||||
* {@code text} exactly.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @param {string} text The expected text.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#getText
|
||||
*/
|
||||
function elementTextIs(element, text) {
|
||||
return new WebElementCondition('until element text is', function () {
|
||||
return element.getText().then((t) => (t === text ? element : null))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element's
|
||||
* {@link webdriver.WebDriver#getText visible text} to contain the given
|
||||
* substring.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @param {string} substr The substring to search for.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#getText
|
||||
*/
|
||||
function elementTextContains(element, substr) {
|
||||
return new WebElementCondition('until element text contains', function () {
|
||||
return element
|
||||
.getText()
|
||||
.then((t) => (t.indexOf(substr) != -1 ? element : null))
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition that will wait for the given element's
|
||||
* {@link webdriver.WebDriver#getText visible text} to match a regular
|
||||
* expression.
|
||||
*
|
||||
* @param {!./webdriver.WebElement} element The element to test.
|
||||
* @param {!RegExp} regex The regular expression to test against.
|
||||
* @return {!WebElementCondition} The new condition.
|
||||
* @see webdriver.WebDriver#getText
|
||||
*/
|
||||
function elementTextMatches(element, regex) {
|
||||
return new WebElementCondition('until element text matches', function () {
|
||||
return element.getText().then((t) => (regex.test(t) ? element : null))
|
||||
})
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
elementTextMatches,
|
||||
elementTextContains,
|
||||
elementTextIs,
|
||||
elementIsNotSelected,
|
||||
elementIsSelected,
|
||||
elementIsDisabled,
|
||||
ableToSwitchToFrame,
|
||||
elementIsEnabled,
|
||||
elementIsNotVisible,
|
||||
elementIsVisible,
|
||||
stalenessOf,
|
||||
elementsLocated,
|
||||
elementLocated,
|
||||
urlMatches,
|
||||
urlContains,
|
||||
urlIs,
|
||||
titleMatches,
|
||||
titleContains,
|
||||
alertIsPresent,
|
||||
titleIs,
|
||||
}
|
||||
52
node_modules/selenium-webdriver/lib/util.js
generated
vendored
Normal file
52
node_modules/selenium-webdriver/lib/util.js
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Determines whether a {@code value} should be treated as an object.
|
||||
* @param {?} value The value to test.
|
||||
* @returns {boolean} Whether the value is an object.
|
||||
*/
|
||||
function isObject(value) {
|
||||
return Object.prototype.toString.call(value) === '[object Object]'
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether a {@code value} should be treated as a promise.
|
||||
* Any object whose "then" property is a function will be considered a promise.
|
||||
*
|
||||
* @param {?} value The value to test.
|
||||
* @return {boolean} Whether the value is a promise.
|
||||
*/
|
||||
function isPromise(value) {
|
||||
try {
|
||||
// Use array notation so the Closure compiler does not obfuscate away our
|
||||
// contract.
|
||||
return (
|
||||
(typeof value === 'object' || typeof value === 'function') &&
|
||||
typeof value['then'] === 'function'
|
||||
)
|
||||
} catch (ex) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isObject,
|
||||
isPromise,
|
||||
}
|
||||
254
node_modules/selenium-webdriver/lib/virtual_authenticator.js
generated
vendored
Normal file
254
node_modules/selenium-webdriver/lib/virtual_authenticator.js
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Protocol for virtual authenticators
|
||||
* @enum {string}
|
||||
*/
|
||||
const Protocol = {
|
||||
CTAP2: 'ctap2',
|
||||
U2F: 'ctap1/u2f',
|
||||
}
|
||||
|
||||
/**
|
||||
* AuthenticatorTransport values
|
||||
* @enum {string}
|
||||
*/
|
||||
const Transport = {
|
||||
BLE: 'ble',
|
||||
USB: 'usb',
|
||||
NFC: 'nfc',
|
||||
INTERNAL: 'internal',
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for the creation of virtual authenticators.
|
||||
* @see http://w3c.github.io/webauthn/#sctn-automation
|
||||
*/
|
||||
class VirtualAuthenticatorOptions {
|
||||
/**
|
||||
* Constructor to initialise VirtualAuthenticatorOptions object.
|
||||
*/
|
||||
constructor() {
|
||||
this._protocol = Protocol['CTAP2']
|
||||
this._transport = Transport['USB']
|
||||
this._hasResidentKey = false
|
||||
this._hasUserVerification = false
|
||||
this._isUserConsenting = true
|
||||
this._isUserVerified = false
|
||||
}
|
||||
|
||||
getProtocol() {
|
||||
return this._protocol
|
||||
}
|
||||
|
||||
setProtocol(protocol) {
|
||||
this._protocol = protocol
|
||||
}
|
||||
|
||||
getTransport() {
|
||||
return this._transport
|
||||
}
|
||||
|
||||
setTransport(transport) {
|
||||
this._transport = transport
|
||||
}
|
||||
|
||||
getHasResidentKey() {
|
||||
return this._hasResidentKey
|
||||
}
|
||||
|
||||
setHasResidentKey(value) {
|
||||
this._hasResidentKey = value
|
||||
}
|
||||
|
||||
getHasUserVerification() {
|
||||
return this._hasUserVerification
|
||||
}
|
||||
|
||||
setHasUserVerification(value) {
|
||||
this._hasUserVerification = value
|
||||
}
|
||||
|
||||
getIsUserConsenting() {
|
||||
return this._isUserConsenting
|
||||
}
|
||||
|
||||
setIsUserConsenting(value) {
|
||||
this._isUserConsenting = value
|
||||
}
|
||||
|
||||
getIsUserVerified() {
|
||||
return this._isUserVerified
|
||||
}
|
||||
|
||||
setIsUserVerified(value) {
|
||||
this._isUserVerified = value
|
||||
}
|
||||
|
||||
toDict() {
|
||||
return {
|
||||
protocol: this.getProtocol(),
|
||||
transport: this.getTransport(),
|
||||
hasResidentKey: this.getHasResidentKey(),
|
||||
hasUserVerification: this.getHasUserVerification(),
|
||||
isUserConsenting: this.getIsUserConsenting(),
|
||||
isUserVerified: this.getIsUserVerified(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A credential stored in a virtual authenticator.
|
||||
* @see https://w3c.github.io/webauthn/#credential-parameters
|
||||
*/
|
||||
class Credential {
|
||||
constructor(
|
||||
credentialId,
|
||||
isResidentCredential,
|
||||
rpId,
|
||||
userHandle,
|
||||
privateKey,
|
||||
signCount
|
||||
) {
|
||||
this._id = credentialId
|
||||
this._isResidentCredential = isResidentCredential
|
||||
this._rpId = rpId
|
||||
this._userHandle = userHandle
|
||||
this._privateKey = privateKey
|
||||
this._signCount = signCount
|
||||
}
|
||||
|
||||
id() {
|
||||
return this._id
|
||||
}
|
||||
|
||||
isResidentCredential() {
|
||||
return this._isResidentCredential
|
||||
}
|
||||
|
||||
rpId() {
|
||||
return this._rpId
|
||||
}
|
||||
|
||||
userHandle() {
|
||||
if (this._userHandle != null) {
|
||||
return this._userHandle
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
privateKey() {
|
||||
return this._privateKey
|
||||
}
|
||||
|
||||
signCount() {
|
||||
return this._signCount
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a resident (i.e. stateless) credential.
|
||||
* @param id Unique base64 encoded string.
|
||||
* @param rpId Relying party identifier.
|
||||
* @param userHandle userHandle associated to the credential. Must be Base64 encoded string.
|
||||
* @param privateKey Base64 encoded PKCS
|
||||
* @param signCount initial value for a signature counter.
|
||||
* @deprecated This method has been made static. Call it with class name. Example, Credential.createResidentCredential()
|
||||
* @returns A resident credential
|
||||
*/
|
||||
createResidentCredential(id, rpId, userHandle, privateKey, signCount) {
|
||||
return new Credential(id, true, rpId, userHandle, privateKey, signCount)
|
||||
}
|
||||
|
||||
static createResidentCredential(id, rpId, userHandle, privateKey, signCount) {
|
||||
return new Credential(id, true, rpId, userHandle, privateKey, signCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a non-resident (i.e. stateless) credential.
|
||||
* @param id Unique base64 encoded string.
|
||||
* @param rpId Relying party identifier.
|
||||
* @param privateKey Base64 encoded PKCS
|
||||
* @param signCount initial value for a signature counter.
|
||||
* @deprecated This method has been made static. Call it with class name. Example, Credential.createNonResidentCredential()
|
||||
* @returns A non-resident credential
|
||||
*/
|
||||
createNonResidentCredential(id, rpId, privateKey, signCount) {
|
||||
return new Credential(id, false, rpId, null, privateKey, signCount)
|
||||
}
|
||||
|
||||
static createNonResidentCredential(id, rpId, privateKey, signCount) {
|
||||
return new Credential(id, false, rpId, null, privateKey, signCount)
|
||||
}
|
||||
|
||||
toDict() {
|
||||
let credentialData = {
|
||||
credentialId: Buffer.from(this._id).toString('base64url'),
|
||||
isResidentCredential: this._isResidentCredential,
|
||||
rpId: this._rpId,
|
||||
privateKey: Buffer.from(this._privateKey, 'binary').toString('base64url'),
|
||||
signCount: this._signCount,
|
||||
}
|
||||
|
||||
if (this.userHandle() != null) {
|
||||
credentialData['userHandle'] = Buffer.from(this._userHandle).toString(
|
||||
'base64url'
|
||||
)
|
||||
}
|
||||
|
||||
return credentialData
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a credential from a map.
|
||||
*/
|
||||
fromDict(data) {
|
||||
let id = new Uint8Array(Buffer.from(data['credentialId'], 'base64url'))
|
||||
let isResidentCredential = data['isResidentCredential']
|
||||
let rpId = data['rpId']
|
||||
let privateKey = Buffer.from(data['privateKey'], 'base64url').toString(
|
||||
'binary'
|
||||
)
|
||||
let signCount = data['signCount']
|
||||
let userHandle
|
||||
|
||||
if ('userHandle' in data) {
|
||||
userHandle = new Uint8Array(Buffer.from(data['userHandle'], 'base64url'))
|
||||
} else {
|
||||
userHandle = null
|
||||
}
|
||||
return new Credential(
|
||||
id,
|
||||
isResidentCredential,
|
||||
rpId,
|
||||
userHandle,
|
||||
privateKey,
|
||||
signCount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Credential,
|
||||
VirtualAuthenticatorOptions,
|
||||
Transport,
|
||||
Protocol,
|
||||
}
|
||||
3346
node_modules/selenium-webdriver/lib/webdriver.js
generated
vendored
Normal file
3346
node_modules/selenium-webdriver/lib/webdriver.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
66
node_modules/selenium-webdriver/lib/webelement.js
generated
vendored
Normal file
66
node_modules/selenium-webdriver/lib/webelement.js
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
const { isObject } = require('./util')
|
||||
|
||||
/**
|
||||
* @fileoverview Defines some common methods used for WebElements.
|
||||
*/
|
||||
|
||||
const LEGACY_ELEMENT_ID_KEY = 'ELEMENT'
|
||||
const ELEMENT_ID_KEY = 'element-6066-11e4-a52e-4f735466cecf'
|
||||
|
||||
/**
|
||||
* Contains logic about WebElements.
|
||||
*/
|
||||
/**
|
||||
* @param {?} obj the object to test.
|
||||
* @return {boolean} whether the object is a valid encoded WebElement ID.
|
||||
*/
|
||||
function isId(obj) {
|
||||
return (
|
||||
isObject(obj) &&
|
||||
(typeof obj[ELEMENT_ID_KEY] === 'string' ||
|
||||
typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string')
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the encoded WebElement ID from the object.
|
||||
*
|
||||
* @param {?} obj The object to extract the ID from.
|
||||
* @return {string} the extracted ID.
|
||||
* @throws {TypeError} if the object is not a valid encoded ID.
|
||||
*/
|
||||
function extractId(obj) {
|
||||
if (isObject(obj)) {
|
||||
if (typeof obj[ELEMENT_ID_KEY] === 'string') {
|
||||
return obj[ELEMENT_ID_KEY]
|
||||
} else if (typeof obj[LEGACY_ELEMENT_ID_KEY] === 'string') {
|
||||
return obj[LEGACY_ELEMENT_ID_KEY]
|
||||
}
|
||||
}
|
||||
throw new TypeError('object is not a WebElement ID')
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
isId,
|
||||
extractId,
|
||||
}
|
||||
117
node_modules/selenium-webdriver/net/index.js
generated
vendored
Normal file
117
node_modules/selenium-webdriver/net/index.js
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const os = require('os')
|
||||
|
||||
function getLoInterface() {
|
||||
let name
|
||||
if (process.platform === 'darwin') {
|
||||
name = 'lo0'
|
||||
} else if (process.platform === 'linux') {
|
||||
name = 'lo'
|
||||
}
|
||||
return name ? os.networkInterfaces()[name] : null
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the system network interfaces for an IP address.
|
||||
* @param {boolean} loopback Whether to find a loopback address.
|
||||
* @param {string} family The IP family (IPv4 or IPv6). Defaults to IPv4.
|
||||
* @return {(string|undefined)} The located IP address or undefined.
|
||||
*/
|
||||
function getIPAddress(loopback, family) {
|
||||
let interfaces
|
||||
if (loopback) {
|
||||
const lo = getLoInterface()
|
||||
interfaces = lo ? [lo] : null
|
||||
}
|
||||
interfaces = interfaces || os.networkInterfaces()
|
||||
for (let key in interfaces) {
|
||||
if (!Object.prototype.hasOwnProperty.call(interfaces, key)) {
|
||||
continue
|
||||
}
|
||||
|
||||
for (let ipAddress of interfaces[key]) {
|
||||
if (
|
||||
(ipAddress.family === family || `IPv${ipAddress.family}` === family) &&
|
||||
ipAddress.internal === loopback
|
||||
) {
|
||||
return ipAddress.address
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
/**
|
||||
* Retrieves the external IP address for this host.
|
||||
* @param {string=} family The IP family to retrieve. Defaults to "IPv4".
|
||||
* @return {(string|undefined)} The IP address or undefined if not available.
|
||||
*/
|
||||
function getAddress(family = 'IPv4') {
|
||||
return getIPAddress(false, family)
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a loopback address for this machine.
|
||||
* @param {string=} family The IP family to retrieve. Defaults to "IPv4".
|
||||
* @return {(string|undefined)} The IP address or undefined if not available.
|
||||
*/
|
||||
function getLoopbackAddress(family = 'IPv4') {
|
||||
return getIPAddress(true, family)
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a hostport string, e.g. "www.example.com:80", into its component
|
||||
* parts.
|
||||
*
|
||||
* @param {string} hostport The string to split.
|
||||
* @return {{host: string, port: ?number}} A host and port. If no port is
|
||||
* present in the argument `hostport`, port is null.
|
||||
*/
|
||||
function splitHostAndPort(hostport) {
|
||||
let lastIndex = hostport.lastIndexOf(':')
|
||||
if (lastIndex < 0) {
|
||||
return { host: hostport, port: null }
|
||||
}
|
||||
|
||||
let firstIndex = hostport.indexOf(':')
|
||||
if (firstIndex != lastIndex && !hostport.includes('[')) {
|
||||
// Multiple colons but no brackets, so assume the string is an IPv6 address
|
||||
// with no port (e.g. "1234:5678:9:0:1234:5678:9:0").
|
||||
return { host: hostport, port: null }
|
||||
}
|
||||
|
||||
let host = hostport.slice(0, lastIndex)
|
||||
if (host.startsWith('[') && host.endsWith(']')) {
|
||||
host = host.slice(1, -1)
|
||||
}
|
||||
|
||||
let port = parseInt(hostport.slice(lastIndex + 1), 10)
|
||||
return { host, port }
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = {
|
||||
splitHostAndPort,
|
||||
getLoopbackAddress,
|
||||
getAddress,
|
||||
}
|
||||
76
node_modules/selenium-webdriver/net/portprober.js
generated
vendored
Normal file
76
node_modules/selenium-webdriver/net/portprober.js
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const net = require('net')
|
||||
|
||||
/**
|
||||
* Tests if a port is free.
|
||||
* @param {number} port The port to test.
|
||||
* @param {string=} opt_host The bound host to test the {@code port} against.
|
||||
* Defaults to {@code INADDR_ANY}.
|
||||
* @return {!Promise<boolean>} A promise that will resolve with whether the port
|
||||
* is free.
|
||||
*/
|
||||
function isFree(port, opt_host) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = net.createServer().on('error', function (e) {
|
||||
if (e.code === 'EADDRINUSE' || e.code === 'EACCES') {
|
||||
resolve(false)
|
||||
} else {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
|
||||
server.listen(port, opt_host, function () {
|
||||
server.close(() => resolve(true))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string=} opt_host The bound host to test the {@code port} against.
|
||||
* Defaults to {@code INADDR_ANY}.
|
||||
* @return {!Promise<number>} A promise that will resolve to a free port. If a
|
||||
* port cannot be found, the promise will be rejected.
|
||||
*/
|
||||
|
||||
function findFreePort(opt_host) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = net.createServer()
|
||||
server.on('listening', function () {
|
||||
resolve(server.address().port)
|
||||
server.close()
|
||||
})
|
||||
server.on('error', (e) => {
|
||||
if (e.code === 'EADDRINUSE' || e.code === 'EACCES') {
|
||||
resolve('Unable to find a free port')
|
||||
} else {
|
||||
reject(e)
|
||||
}
|
||||
})
|
||||
// By providing 0 we let the operative system find an arbitrary port
|
||||
server.listen(0, opt_host)
|
||||
})
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = {
|
||||
findFreePort,
|
||||
isFree,
|
||||
}
|
||||
52
node_modules/selenium-webdriver/package.json
generated
vendored
Normal file
52
node_modules/selenium-webdriver/package.json
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "selenium-webdriver",
|
||||
"version": "4.8.0",
|
||||
"description": "The official WebDriver JavaScript bindings from the Selenium project",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"automation",
|
||||
"selenium",
|
||||
"testing",
|
||||
"webdriver",
|
||||
"webdriverjs"
|
||||
],
|
||||
"homepage": "https://github.com/SeleniumHQ/selenium/tree/trunk/javascript/node/selenium-webdriver#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/SeleniumHQ/selenium/issues"
|
||||
},
|
||||
"main": "./index",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SeleniumHQ/selenium.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.20.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jszip": "^3.10.0",
|
||||
"tmp": "^0.2.1",
|
||||
"ws": ">=8.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^8.31.0",
|
||||
"eslint-config-prettier": "^8.6.0",
|
||||
"eslint-plugin-no-only-tests": "^3.1.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"express": "^4.18.2",
|
||||
"mocha": "^10.2.0",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"prettier": "^2.7.1",
|
||||
"serve-index": "^1.9.1",
|
||||
"sinon": "^14.0.2"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --ignore-pattern node_modules --ignore-pattern generator --fix --ext js lib/http.js \"**/*.js\"",
|
||||
"test": "npm run lint && mocha -t 600000 --recursive test",
|
||||
"test-jasmine": "bazel test //javascript/node/selenium-webdriver:tests"
|
||||
},
|
||||
"mocha": {
|
||||
"recursive": true,
|
||||
"timeout": 600000
|
||||
}
|
||||
}
|
||||
32
node_modules/selenium-webdriver/proxy.js
generated
vendored
Normal file
32
node_modules/selenium-webdriver/proxy.js
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Proxy module alias.
|
||||
*
|
||||
* var webdriver = require('selenium-webdriver'),
|
||||
* proxy = require('selenium-webdriver/proxy');
|
||||
*
|
||||
* var driver = new webdriver.Builder()
|
||||
* .withCapabilities(webdriver.Capabilities.chrome())
|
||||
* .setProxy(proxy.manual({http: 'host:1234'}))
|
||||
* .build();
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
module.exports = require('./lib/proxy')
|
||||
633
node_modules/selenium-webdriver/remote/index.js
generated
vendored
Normal file
633
node_modules/selenium-webdriver/remote/index.js
generated
vendored
Normal file
@@ -0,0 +1,633 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const url = require('url')
|
||||
|
||||
const httpUtil = require('../http/util')
|
||||
const io = require('../io')
|
||||
const { exec } = require('../io/exec')
|
||||
const { Zip } = require('../io/zip')
|
||||
const cmd = require('../lib/command')
|
||||
const input = require('../lib/input')
|
||||
const net = require('../net')
|
||||
const portprober = require('../net/portprober')
|
||||
const logging = require('../lib/logging')
|
||||
|
||||
const { getJavaPath, formatSpawnArgs } = require('./util')
|
||||
|
||||
/**
|
||||
* @typedef {(string|!Array<string|number|!stream.Stream|null|undefined>)}
|
||||
*/
|
||||
let StdIoOptions // eslint-disable-line
|
||||
|
||||
/**
|
||||
* @typedef {(string|!IThenable<string>)}
|
||||
*/
|
||||
let CommandLineFlag // eslint-disable-line
|
||||
|
||||
/**
|
||||
* A record object that defines the configuration options for a DriverService
|
||||
* instance.
|
||||
*
|
||||
* @record
|
||||
*/
|
||||
function ServiceOptions() {}
|
||||
|
||||
/**
|
||||
* Whether the service should only be accessed on this host's loopback address.
|
||||
*
|
||||
* @type {(boolean|undefined)}
|
||||
*/
|
||||
ServiceOptions.prototype.loopback
|
||||
|
||||
/**
|
||||
* The host name to access the server on. If this option is specified, the
|
||||
* {@link #loopback} option will be ignored.
|
||||
*
|
||||
* @type {(string|undefined)}
|
||||
*/
|
||||
ServiceOptions.prototype.hostname
|
||||
|
||||
/**
|
||||
* The port to start the server on (must be > 0). If the port is provided as a
|
||||
* promise, the service will wait for the promise to resolve before starting.
|
||||
*
|
||||
* @type {(number|!IThenable<number>)}
|
||||
*/
|
||||
ServiceOptions.prototype.port
|
||||
|
||||
/**
|
||||
* The arguments to pass to the service. If a promise is provided, the service
|
||||
* will wait for it to resolve before starting.
|
||||
*
|
||||
* @type {!(Array<CommandLineFlag>|IThenable<!Array<CommandLineFlag>>)}
|
||||
*/
|
||||
ServiceOptions.prototype.args
|
||||
|
||||
/**
|
||||
* The base path on the server for the WebDriver wire protocol (e.g. '/wd/hub').
|
||||
* Defaults to '/'.
|
||||
*
|
||||
* @type {(string|undefined|null)}
|
||||
*/
|
||||
ServiceOptions.prototype.path
|
||||
|
||||
/**
|
||||
* The environment variables that should be visible to the server process.
|
||||
* Defaults to inheriting the current process's environment.
|
||||
*
|
||||
* @type {(Object<string, string>|undefined)}
|
||||
*/
|
||||
ServiceOptions.prototype.env
|
||||
|
||||
/**
|
||||
* IO configuration for the spawned server process. For more information, refer
|
||||
* to the documentation of `child_process.spawn`.
|
||||
*
|
||||
* @type {(StdIoOptions|undefined)}
|
||||
* @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio
|
||||
*/
|
||||
ServiceOptions.prototype.stdio
|
||||
|
||||
/**
|
||||
* Manages the life and death of a native executable WebDriver server.
|
||||
*
|
||||
* It is expected that the driver server implements the
|
||||
* https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol.
|
||||
* Furthermore, the managed server should support multiple concurrent sessions,
|
||||
* so that this class may be reused for multiple clients.
|
||||
*/
|
||||
class DriverService {
|
||||
/**
|
||||
* @param {string} executable Path to the executable to run.
|
||||
* @param {!ServiceOptions} options Configuration options for the service.
|
||||
*/
|
||||
constructor(executable, options) {
|
||||
/** @private @const */
|
||||
this.log_ = logging.getLogger('webdriver.DriverService')
|
||||
/** @private {string} */
|
||||
this.executable_ = executable
|
||||
|
||||
/** @private {boolean} */
|
||||
this.loopbackOnly_ = !!options.loopback
|
||||
|
||||
/** @private {(string|undefined)} */
|
||||
this.hostname_ = options.hostname
|
||||
|
||||
/** @private {(number|!IThenable<number>)} */
|
||||
this.port_ = options.port
|
||||
|
||||
/**
|
||||
* @private {!(Array<CommandLineFlag>|
|
||||
* IThenable<!Array<CommandLineFlag>>)}
|
||||
*/
|
||||
this.args_ = options.args
|
||||
|
||||
/** @private {string} */
|
||||
this.path_ = options.path || '/'
|
||||
|
||||
/** @private {!Object<string, string>} */
|
||||
this.env_ = options.env || process.env
|
||||
|
||||
/**
|
||||
* @private {(string|!Array<string|number|!stream.Stream|null|undefined>)}
|
||||
*/
|
||||
this.stdio_ = options.stdio || 'ignore'
|
||||
|
||||
/**
|
||||
* A promise for the managed subprocess, or null if the server has not been
|
||||
* started yet. This promise will never be rejected.
|
||||
* @private {Promise<!exec.Command>}
|
||||
*/
|
||||
this.command_ = null
|
||||
|
||||
/**
|
||||
* Promise that resolves to the server's address or null if the server has
|
||||
* not been started. This promise will be rejected if the server terminates
|
||||
* before it starts accepting WebDriver requests.
|
||||
* @private {Promise<string>}
|
||||
*/
|
||||
this.address_ = null
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Promise<string>} A promise that resolves to the server's address.
|
||||
* @throws {Error} If the server has not been started.
|
||||
*/
|
||||
address() {
|
||||
if (this.address_) {
|
||||
return this.address_
|
||||
}
|
||||
throw Error('Server has not been started.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the underlying process is still running. This does not take
|
||||
* into account whether the process is in the process of shutting down.
|
||||
* @return {boolean} Whether the underlying service process is running.
|
||||
*/
|
||||
isRunning() {
|
||||
return !!this.address_
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server if it is not already running.
|
||||
* @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the
|
||||
* server to start accepting requests. Defaults to 30 seconds.
|
||||
* @return {!Promise<string>} A promise that will resolve to the server's base
|
||||
* URL when it has started accepting requests. If the timeout expires
|
||||
* before the server has started, the promise will be rejected.
|
||||
*/
|
||||
start(opt_timeoutMs) {
|
||||
if (this.address_) {
|
||||
return this.address_
|
||||
}
|
||||
|
||||
const timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS
|
||||
const self = this
|
||||
|
||||
let resolveCommand
|
||||
this.command_ = new Promise((resolve) => (resolveCommand = resolve))
|
||||
|
||||
this.address_ = new Promise((resolveAddress, rejectAddress) => {
|
||||
resolveAddress(
|
||||
Promise.resolve(this.port_).then((port) => {
|
||||
if (port <= 0) {
|
||||
throw Error('Port must be > 0: ' + port)
|
||||
}
|
||||
|
||||
return resolveCommandLineFlags(this.args_).then((args) => {
|
||||
const command = exec(self.executable_, {
|
||||
args: args,
|
||||
env: self.env_,
|
||||
stdio: self.stdio_,
|
||||
})
|
||||
|
||||
resolveCommand(command)
|
||||
|
||||
const earlyTermination = command.result().then(function (result) {
|
||||
const error =
|
||||
result.code == null
|
||||
? Error('Server was killed with ' + result.signal)
|
||||
: Error('Server terminated early with status ' + result.code)
|
||||
rejectAddress(error)
|
||||
self.address_ = null
|
||||
self.command_ = null
|
||||
throw error
|
||||
})
|
||||
|
||||
let hostname = self.hostname_
|
||||
if (!hostname) {
|
||||
hostname =
|
||||
(!self.loopbackOnly_ && net.getAddress()) ||
|
||||
net.getLoopbackAddress()
|
||||
}
|
||||
|
||||
const serverUrl = url.format({
|
||||
protocol: 'http',
|
||||
hostname: hostname,
|
||||
port: port + '',
|
||||
pathname: self.path_,
|
||||
})
|
||||
|
||||
return new Promise((fulfill, reject) => {
|
||||
let cancelToken = earlyTermination.catch((e) =>
|
||||
reject(Error(e.message))
|
||||
)
|
||||
|
||||
httpUtil.waitForServer(serverUrl, timeout, cancelToken).then(
|
||||
(_) => fulfill(serverUrl),
|
||||
(err) => {
|
||||
if (err instanceof httpUtil.CancellationError) {
|
||||
fulfill(serverUrl)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
return this.address_
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the service if it is not currently running. This function will kill
|
||||
* the server immediately. To synchronize with the active control flow, use
|
||||
* {@link #stop()}.
|
||||
* @return {!Promise} A promise that will be resolved when the server has been
|
||||
* stopped.
|
||||
*/
|
||||
kill() {
|
||||
if (!this.address_ || !this.command_) {
|
||||
return Promise.resolve() // Not currently running.
|
||||
}
|
||||
let cmd = this.command_
|
||||
this.address_ = null
|
||||
this.command_ = null
|
||||
return cmd.then((c) => c.kill('SIGTERM'))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!(Array<CommandLineFlag>|IThenable<!Array<CommandLineFlag>>)} args
|
||||
* @return {!Promise<!Array<string>>}
|
||||
*/
|
||||
function resolveCommandLineFlags(args) {
|
||||
// Resolve the outer array, then the individual flags.
|
||||
return Promise.resolve(args).then(
|
||||
/** !Array<CommandLineFlag> */ (args) => Promise.all(args)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* The default amount of time, in milliseconds, to wait for the server to
|
||||
* start.
|
||||
* @const {number}
|
||||
*/
|
||||
DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000
|
||||
|
||||
/**
|
||||
* Creates {@link DriverService} objects that manage a WebDriver server in a
|
||||
* child process.
|
||||
*/
|
||||
DriverService.Builder = class {
|
||||
/**
|
||||
* @param {string} exe Path to the executable to use. This executable must
|
||||
* accept the `--port` flag for defining the port to start the server on.
|
||||
* @throws {Error} If the provided executable path does not exist.
|
||||
*/
|
||||
constructor(exe) {
|
||||
if (!fs.existsSync(exe)) {
|
||||
throw Error(`The specified executable path does not exist: ${exe}`)
|
||||
}
|
||||
|
||||
/** @private @const {string} */
|
||||
this.exe_ = exe
|
||||
|
||||
/** @private {!ServiceOptions} */
|
||||
this.options_ = {
|
||||
args: [],
|
||||
port: 0,
|
||||
env: null,
|
||||
stdio: 'ignore',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Define additional command line arguments to use when starting the server.
|
||||
*
|
||||
* @param {...CommandLineFlag} var_args The arguments to include.
|
||||
* @return {!THIS} A self reference.
|
||||
* @this {THIS}
|
||||
* @template THIS
|
||||
*/
|
||||
addArguments(...arguments_) {
|
||||
this.options_.args = this.options_.args.concat(arguments_)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the host name to access the server on. If specified, the
|
||||
* {@linkplain #setLoopback() loopback} setting will be ignored.
|
||||
*
|
||||
* @param {string} hostname
|
||||
* @return {!DriverService.Builder} A self reference.
|
||||
*/
|
||||
setHostname(hostname) {
|
||||
this.options_.hostname = hostname
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the service should be accessed at this host's loopback
|
||||
* address.
|
||||
*
|
||||
* @param {boolean} loopback
|
||||
* @return {!DriverService.Builder} A self reference.
|
||||
*/
|
||||
setLoopback(loopback) {
|
||||
this.options_.loopback = loopback
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base path for WebDriver REST commands (e.g. "/wd/hub").
|
||||
* By default, the driver will accept commands relative to "/".
|
||||
*
|
||||
* @param {?string} basePath The base path to use, or `null` to use the
|
||||
* default.
|
||||
* @return {!DriverService.Builder} A self reference.
|
||||
*/
|
||||
setPath(basePath) {
|
||||
this.options_.path = basePath
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the port to start the server on.
|
||||
*
|
||||
* @param {number} port The port to use, or 0 for any free port.
|
||||
* @return {!DriverService.Builder} A self reference.
|
||||
* @throws {Error} If an invalid port is specified.
|
||||
*/
|
||||
setPort(port) {
|
||||
if (port < 0) {
|
||||
throw Error(`port must be >= 0: ${port}`)
|
||||
}
|
||||
this.options_.port = port
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the environment to start the server under. This setting will be
|
||||
* inherited by every browser session started by the server. By default, the
|
||||
* server will inherit the enviroment of the current process.
|
||||
*
|
||||
* @param {(Map<string, string>|Object<string, string>|null)} env The desired
|
||||
* environment to use, or `null` if the server should inherit the
|
||||
* current environment.
|
||||
* @return {!DriverService.Builder} A self reference.
|
||||
*/
|
||||
setEnvironment(env) {
|
||||
if (env instanceof Map) {
|
||||
let tmp = {}
|
||||
env.forEach((value, key) => (tmp[key] = value))
|
||||
env = tmp
|
||||
}
|
||||
this.options_.env = env
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* IO configuration for the spawned server process. For more information,
|
||||
* refer to the documentation of `child_process.spawn`.
|
||||
*
|
||||
* @param {StdIoOptions} config The desired IO configuration.
|
||||
* @return {!DriverService.Builder} A self reference.
|
||||
* @see https://nodejs.org/dist/latest-v4.x/docs/api/child_process.html#child_process_options_stdio
|
||||
*/
|
||||
setStdio(config) {
|
||||
this.options_.stdio = config
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new DriverService using this instance's current configuration.
|
||||
*
|
||||
* @return {!DriverService} A new driver service.
|
||||
*/
|
||||
build() {
|
||||
let port = this.options_.port || portprober.findFreePort()
|
||||
let args = Promise.resolve(port).then((port) => {
|
||||
return this.options_.args.concat('--port=' + port)
|
||||
})
|
||||
|
||||
let options =
|
||||
/** @type {!ServiceOptions} */
|
||||
(Object.assign({}, this.options_, { args, port }))
|
||||
return new DriverService(this.exe_, options)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages the life and death of the
|
||||
* <a href="https://www.selenium.dev/downloads/">
|
||||
* standalone Selenium server</a>.
|
||||
*/
|
||||
class SeleniumServer extends DriverService {
|
||||
/**
|
||||
* @param {string} jar Path to the Selenium server jar.
|
||||
* @param {SeleniumServer.Options=} opt_options Configuration options for the
|
||||
* server.
|
||||
* @throws {Error} If the path to the Selenium jar is not specified or if an
|
||||
* invalid port is specified.
|
||||
*/
|
||||
constructor(jar, opt_options) {
|
||||
if (!jar) {
|
||||
throw Error('Path to the Selenium jar not specified')
|
||||
}
|
||||
|
||||
const options = opt_options || {}
|
||||
|
||||
if (options.port < 0) {
|
||||
throw Error('Port must be >= 0: ' + options.port)
|
||||
}
|
||||
|
||||
let port = options.port || portprober.findFreePort()
|
||||
let args = Promise.all([
|
||||
port,
|
||||
options.jvmArgs || [],
|
||||
options.args || [],
|
||||
]).then((resolved) => {
|
||||
let port = resolved[0]
|
||||
let jvmArgs = resolved[1]
|
||||
let args = resolved[2]
|
||||
|
||||
const fullArgsList = jvmArgs
|
||||
.concat('-jar', jar, '-port', port)
|
||||
.concat(args)
|
||||
|
||||
return formatSpawnArgs(jar, fullArgsList)
|
||||
})
|
||||
|
||||
const java = getJavaPath()
|
||||
|
||||
super(java, {
|
||||
loopback: options.loopback,
|
||||
port: port,
|
||||
args: args,
|
||||
path: '/wd/hub',
|
||||
env: options.env,
|
||||
stdio: options.stdio,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A record object describing configuration options for a {@link SeleniumServer}
|
||||
* instance.
|
||||
*
|
||||
* @record
|
||||
*/
|
||||
SeleniumServer.Options = class {
|
||||
constructor() {
|
||||
/**
|
||||
* Whether the server should only be accessible on this host's loopback
|
||||
* address.
|
||||
*
|
||||
* @type {(boolean|undefined)}
|
||||
*/
|
||||
this.loopback
|
||||
|
||||
/**
|
||||
* The port to start the server on (must be > 0). If the port is provided as
|
||||
* a promise, the service will wait for the promise to resolve before
|
||||
* starting.
|
||||
*
|
||||
* @type {(number|!IThenable<number>)}
|
||||
*/
|
||||
this.port
|
||||
|
||||
/**
|
||||
* The arguments to pass to the service. If a promise is provided,
|
||||
* the service will wait for it to resolve before starting.
|
||||
*
|
||||
* @type {!(Array<string>|IThenable<!Array<string>>)}
|
||||
*/
|
||||
this.args
|
||||
|
||||
/**
|
||||
* The arguments to pass to the JVM. If a promise is provided,
|
||||
* the service will wait for it to resolve before starting.
|
||||
*
|
||||
* @type {(!Array<string>|!IThenable<!Array<string>>|undefined)}
|
||||
*/
|
||||
this.jvmArgs
|
||||
|
||||
/**
|
||||
* The environment variables that should be visible to the server
|
||||
* process. Defaults to inheriting the current process's environment.
|
||||
*
|
||||
* @type {(!Object<string, string>|undefined)}
|
||||
*/
|
||||
this.env
|
||||
|
||||
/**
|
||||
* IO configuration for the spawned server process. If unspecified, IO will
|
||||
* be ignored.
|
||||
*
|
||||
* @type {(string|!Array<string|number|!stream.Stream|null|undefined>|
|
||||
* undefined)}
|
||||
* @see <https://nodejs.org/dist/latest-v8.x/docs/api/child_process.html#child_process_options_stdio>
|
||||
*/
|
||||
this.stdio
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link webdriver.FileDetector} that may be used when running
|
||||
* against a remote
|
||||
* [Selenium server](https://www.selenium.dev/downloads/).
|
||||
*
|
||||
* When a file path on the local machine running this script is entered with
|
||||
* {@link webdriver.WebElement#sendKeys WebElement#sendKeys}, this file detector
|
||||
* will transfer the specified file to the Selenium server's host; the sendKeys
|
||||
* command will be updated to use the transferred file's path.
|
||||
*
|
||||
* __Note:__ This class depends on a non-standard command supported on the
|
||||
* Java Selenium server. The file detector will fail if used with a server that
|
||||
* only supports standard WebDriver commands (such as the ChromeDriver).
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class FileDetector extends input.FileDetector {
|
||||
/**
|
||||
* Prepares a `file` for use with the remote browser. If the provided path
|
||||
* does not reference a normal file (i.e. it does not exist or is a
|
||||
* directory), then the promise returned by this method will be resolved with
|
||||
* the original file path. Otherwise, this method will upload the file to the
|
||||
* remote server, which will return the file's path on the remote system so
|
||||
* it may be referenced in subsequent commands.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
handleFile(driver, file) {
|
||||
return io.stat(file).then(
|
||||
function (stats) {
|
||||
if (stats.isDirectory()) {
|
||||
return file // Not a valid file, return original input.
|
||||
}
|
||||
|
||||
let zip = new Zip()
|
||||
return zip
|
||||
.addFile(file)
|
||||
.then(() => zip.toBuffer())
|
||||
.then((buf) => buf.toString('base64'))
|
||||
.then((encodedZip) => {
|
||||
let command = new cmd.Command(cmd.Name.UPLOAD_FILE).setParameter(
|
||||
'file',
|
||||
encodedZip
|
||||
)
|
||||
return driver.execute(command)
|
||||
})
|
||||
},
|
||||
function (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
return file // Not a file; return original input.
|
||||
}
|
||||
throw err
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
DriverService,
|
||||
FileDetector,
|
||||
SeleniumServer,
|
||||
// Exported for API docs.
|
||||
ServiceOptions,
|
||||
}
|
||||
93
node_modules/selenium-webdriver/remote/util.js
generated
vendored
Normal file
93
node_modules/selenium-webdriver/remote/util.js
generated
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const cp = require('child_process')
|
||||
|
||||
/**
|
||||
* returns path to java or 'java' string if JAVA_HOME does not exist in env obj
|
||||
* @returns {string}
|
||||
*/
|
||||
function getJavaPath() {
|
||||
return process.env['JAVA_HOME']
|
||||
? path.join(process.env['JAVA_HOME'], 'bin/java')
|
||||
: 'java'
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} seleniumStandalonePath path to standalone server
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isSelenium3x(seleniumStandalonePath) {
|
||||
const javaPath = getJavaPath()
|
||||
|
||||
const execRes = cp.execSync(
|
||||
`${javaPath} -jar ${seleniumStandalonePath} --version`
|
||||
)
|
||||
|
||||
return execRes.toString().trim().startsWith('Selenium server version: 3')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} seleniumStandalonePath path to standalone server
|
||||
* @param {Array.<string>} args spawn arguments array
|
||||
* returns formatted args based on selenium standalone server version
|
||||
* @returns {Array.<string>}
|
||||
*/
|
||||
function formatSpawnArgs(seleniumStandalonePath, args) {
|
||||
if (isSelenium3x(seleniumStandalonePath)) {
|
||||
console.warn('Deprecation: Support for Standalone Server 3.x will be removed soon. Please update to version 4.x')
|
||||
return args
|
||||
}
|
||||
|
||||
const standaloneArg = 'standalone'
|
||||
const port3xArgFormat = '-port'
|
||||
const port4xArgFormat = '--port'
|
||||
|
||||
let formattedArgs = Array.from(args)
|
||||
|
||||
const standaloneArgIndex = formattedArgs.findIndex(
|
||||
(arg) => arg === seleniumStandalonePath
|
||||
)
|
||||
const v3portArgFormat = formattedArgs.findIndex(
|
||||
(arg) => arg === port3xArgFormat
|
||||
)
|
||||
|
||||
// old v3x port arg format was -port, new v4x port arg format is --port
|
||||
if (v3portArgFormat !== -1) {
|
||||
formattedArgs[v3portArgFormat] = port4xArgFormat
|
||||
}
|
||||
|
||||
// 'standalone' arg should be right after jar file path
|
||||
// in case if it is already in place - returns args
|
||||
if (formattedArgs[standaloneArgIndex + 1] === standaloneArg)
|
||||
return formattedArgs
|
||||
|
||||
// insert 'standalone' right after jar file path
|
||||
formattedArgs.splice(standaloneArgIndex + 1, 0, standaloneArg)
|
||||
|
||||
return formattedArgs
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
module.exports = {
|
||||
getJavaPath,
|
||||
isSelenium3x,
|
||||
formatSpawnArgs,
|
||||
}
|
||||
168
node_modules/selenium-webdriver/safari.js
generated
vendored
Normal file
168
node_modules/selenium-webdriver/safari.js
generated
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Defines a WebDriver client for Safari.
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const http = require('./http')
|
||||
const io = require('./io')
|
||||
const remote = require('./remote')
|
||||
const webdriver = require('./lib/webdriver')
|
||||
const { Browser, Capabilities } = require('./lib/capabilities')
|
||||
|
||||
/**
|
||||
* _Synchronously_ attempts to locate the IE driver executable on the current
|
||||
* system.
|
||||
*
|
||||
* @return {?string} the located executable, or `null`.
|
||||
*/
|
||||
function locateSynchronously() {
|
||||
return process.platform === 'darwin'
|
||||
? io.findInPath('safaridriver', true)
|
||||
: null
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string} .
|
||||
* @throws {Error}
|
||||
*/
|
||||
function findSafariDriver() {
|
||||
let exe = locateSynchronously()
|
||||
if (!exe) {
|
||||
throw Error(
|
||||
`The safaridriver executable could not be found on the current PATH.
|
||||
Please ensure you are using Safari 10.0 or above.`
|
||||
)
|
||||
}
|
||||
return exe
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
||||
* a [safaridriver] server in a child process.
|
||||
*
|
||||
* [safaridriver]: https://developer.apple.com/library/prerelease/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28
|
||||
*/
|
||||
class ServiceBuilder extends remote.DriverService.Builder {
|
||||
/**
|
||||
* @param {string=} opt_exe Path to the server executable to use. If omitted,
|
||||
* the builder will attempt to locate the safaridriver on the system PATH.
|
||||
*/
|
||||
constructor(opt_exe) {
|
||||
super(opt_exe || findSafariDriver())
|
||||
this.setLoopback(true) // Required.
|
||||
}
|
||||
}
|
||||
|
||||
const OPTIONS_CAPABILITY_KEY = 'safari.options'
|
||||
const TECHNOLOGY_PREVIEW_OPTIONS_KEY = 'technologyPreview'
|
||||
|
||||
/**
|
||||
* Configuration options specific to the {@link Driver SafariDriver}.
|
||||
*/
|
||||
class Options extends Capabilities {
|
||||
/**
|
||||
* @param {(Capabilities|Map<string, ?>|Object)=} other Another set of
|
||||
* capabilities to initialize this instance from.
|
||||
*/
|
||||
constructor(other = undefined) {
|
||||
super(other)
|
||||
|
||||
/** @private {!Object} */
|
||||
this.options_ = this.get(OPTIONS_CAPABILITY_KEY) || {}
|
||||
|
||||
this.set(OPTIONS_CAPABILITY_KEY, this.options_)
|
||||
this.setBrowserName(Browser.SAFARI)
|
||||
}
|
||||
|
||||
/**
|
||||
* Instruct the SafariDriver to use the Safari Technology Preview if true.
|
||||
* Otherwise, use the release version of Safari. Defaults to using the release version of Safari.
|
||||
*
|
||||
* @param {boolean} useTechnologyPreview
|
||||
* @return {!Options} A self reference.
|
||||
*/
|
||||
setTechnologyPreview(useTechnologyPreview) {
|
||||
this.options_[TECHNOLOGY_PREVIEW_OPTIONS_KEY] = !!useTechnologyPreview
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(Capabilities|Object<string, *>)=} o The options object
|
||||
* @return {boolean}
|
||||
*/
|
||||
function useTechnologyPreview(o) {
|
||||
if (o instanceof Capabilities) {
|
||||
let options = o.get(OPTIONS_CAPABILITY_KEY)
|
||||
return !!(options && options[TECHNOLOGY_PREVIEW_OPTIONS_KEY])
|
||||
}
|
||||
|
||||
if (o && typeof o === 'object') {
|
||||
return !!o[TECHNOLOGY_PREVIEW_OPTIONS_KEY]
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const SAFARIDRIVER_TECHNOLOGY_PREVIEW_EXE =
|
||||
'/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver'
|
||||
|
||||
/**
|
||||
* A WebDriver client for Safari. This class should never be instantiated
|
||||
* directly; instead, use the {@linkplain ./builder.Builder Builder}:
|
||||
*
|
||||
* var driver = new Builder()
|
||||
* .forBrowser('safari')
|
||||
* .build();
|
||||
*
|
||||
*/
|
||||
class Driver extends webdriver.WebDriver {
|
||||
/**
|
||||
* Creates a new Safari session.
|
||||
*
|
||||
* @param {(Options|Capabilities)=} options The configuration options.
|
||||
* @return {!Driver} A new driver instance.
|
||||
*/
|
||||
static createSession(options) {
|
||||
let caps = options || new Options()
|
||||
|
||||
let exe
|
||||
if (useTechnologyPreview(caps.get(OPTIONS_CAPABILITY_KEY))) {
|
||||
exe = SAFARIDRIVER_TECHNOLOGY_PREVIEW_EXE
|
||||
}
|
||||
|
||||
let service = new ServiceBuilder(exe).build()
|
||||
let executor = new http.Executor(
|
||||
service.start().then((url) => new http.HttpClient(url))
|
||||
)
|
||||
|
||||
return /** @type {!Driver} */ (
|
||||
super.createSession(executor, caps, () => service.kill())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Public API
|
||||
|
||||
exports.Driver = Driver
|
||||
exports.Options = Options
|
||||
exports.ServiceBuilder = ServiceBuilder
|
||||
exports.locateSynchronously = locateSynchronously
|
||||
506
node_modules/selenium-webdriver/testing/index.js
generated
vendored
Normal file
506
node_modules/selenium-webdriver/testing/index.js
generated
vendored
Normal file
@@ -0,0 +1,506 @@
|
||||
// Licensed to the Software Freedom Conservancy (SFC) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The SFC licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview Provides extensions for
|
||||
* [Jasmine](https://jasmine.github.io) and [Mocha](https://mochajs.org).
|
||||
*
|
||||
* You may conditionally suppress a test function using the exported
|
||||
* "ignore" function. If the provided predicate returns true, the attached
|
||||
* test case will be skipped:
|
||||
*
|
||||
* test.ignore(maybe()).it('is flaky', function() {
|
||||
* if (Math.random() < 0.5) throw Error();
|
||||
* });
|
||||
*
|
||||
* function maybe() { return Math.random() < 0.5; }
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { isatty } = require('tty')
|
||||
const chrome = require('../chrome')
|
||||
const edge = require('../edge')
|
||||
const firefox = require('../firefox')
|
||||
const ie = require('../ie')
|
||||
const remote = require('../remote')
|
||||
const safari = require('../safari')
|
||||
const { Browser } = require('../lib/capabilities')
|
||||
const { Builder } = require('../index')
|
||||
|
||||
/**
|
||||
* Describes a browser targeted by a {@linkplain suite test suite}.
|
||||
* @record
|
||||
*/
|
||||
function TargetBrowser() {}
|
||||
|
||||
/**
|
||||
* The {@linkplain Browser name} of the targeted browser.
|
||||
* @type {string}
|
||||
*/
|
||||
TargetBrowser.prototype.name
|
||||
|
||||
/**
|
||||
* The specific version of the targeted browser, if any.
|
||||
* @type {(string|undefined)}
|
||||
*/
|
||||
TargetBrowser.prototype.version
|
||||
|
||||
/**
|
||||
* The specific {@linkplain ../lib/capabilities.Platform platform} for the
|
||||
* targeted browser, if any.
|
||||
* @type {(string|undefined)}.
|
||||
*/
|
||||
TargetBrowser.prototype.platform
|
||||
|
||||
/** @suppress {checkTypes} */
|
||||
function color(c, s) {
|
||||
return isatty(process.stdout) ? `\u001b[${c}m${s}\u001b[0m` : s
|
||||
}
|
||||
function green(s) {
|
||||
return color(32, s)
|
||||
}
|
||||
function cyan(s) {
|
||||
return color(36, s)
|
||||
}
|
||||
function info(msg) {
|
||||
console.info(`${green('[INFO]')} ${msg}`)
|
||||
}
|
||||
function warn(msg) {
|
||||
console.warn(`${cyan('[WARNING]')} ${msg}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the browsers for a test suite to target from the `SELENIUM_BROWSER`
|
||||
* environment variable.
|
||||
*
|
||||
* @return {{name: string, version: string, platform: string}}[] the browsers to target.
|
||||
*/
|
||||
function getBrowsersToTestFromEnv() {
|
||||
let browsers = process.env['SELENIUM_BROWSER']
|
||||
if (!browsers) {
|
||||
return []
|
||||
}
|
||||
return browsers.split(',').map((spec) => {
|
||||
const parts = spec.split(/:/, 3)
|
||||
let name = parts[0]
|
||||
if (name === 'ie') {
|
||||
name = Browser.INTERNET_EXPLORER
|
||||
} else if (name === 'edge') {
|
||||
name = Browser.EDGE
|
||||
}
|
||||
let version = parts[1]
|
||||
let platform = parts[2]
|
||||
return { name, version, platform }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Array<!TargetBrowser>} the browsers available for testing on this
|
||||
* system.
|
||||
*/
|
||||
function getAvailableBrowsers() {
|
||||
info(`Searching for WebDriver executables installed on the current system...`)
|
||||
|
||||
let targets = [
|
||||
[chrome.locateSynchronously, Browser.CHROME],
|
||||
[edge.locateSynchronously, Browser.EDGE],
|
||||
[firefox.locateSynchronously, Browser.FIREFOX],
|
||||
[ie.locateSynchronously, Browser.INTERNET_EXPLORER],
|
||||
[safari.locateSynchronously, Browser.SAFARI],
|
||||
]
|
||||
|
||||
let availableBrowsers = []
|
||||
for (let pair of targets) {
|
||||
const fn = pair[0]
|
||||
const name = pair[1]
|
||||
const capabilities = pair[2]
|
||||
if (fn()) {
|
||||
info(`... located ${name}`)
|
||||
availableBrowsers.push({ name, capabilities })
|
||||
}
|
||||
}
|
||||
|
||||
if (availableBrowsers.length === 0) {
|
||||
warn(`Unable to locate any WebDriver executables for testing`)
|
||||
}
|
||||
|
||||
return availableBrowsers
|
||||
}
|
||||
|
||||
let wasInit
|
||||
let targetBrowsers
|
||||
let seleniumJar
|
||||
let seleniumUrl
|
||||
let seleniumServer
|
||||
|
||||
/**
|
||||
* Initializes this module by determining which browsers a
|
||||
* {@linkplain ./index.suite test suite} should run against. The default
|
||||
* behavior is to run tests against every browser with a WebDriver executables
|
||||
* (chromedriver, firefoxdriver, etc.) are installed on the system by `PATH`.
|
||||
*
|
||||
* Specific browsers can be selected at runtime by setting the
|
||||
* `SELENIUM_BROWSER` environment variable. This environment variable has the
|
||||
* same semantics as with the WebDriver {@link ../index.Builder Builder},
|
||||
* except you may use a comma-delimited list to run against multiple browsers:
|
||||
*
|
||||
* SELENIUM_BROWSER=chrome,firefox mocha --recursive tests/
|
||||
*
|
||||
* The `SELENIUM_REMOTE_URL` environment variable may be set to configure tests
|
||||
* to run against an externally managed (usually remote) Selenium server. When
|
||||
* set, the WebDriver builder provided by each
|
||||
* {@linkplain TestEnvironment#builder TestEnvironment} will automatically be
|
||||
* configured to use this server instead of starting a browser driver locally.
|
||||
*
|
||||
* The `SELENIUM_SERVER_JAR` environment variable may be set to the path of a
|
||||
* standalone Selenium server on the local machine that should be used for
|
||||
* WebDriver sessions. When set, the WebDriver builder provided by each
|
||||
* {@linkplain TestEnvironment} will automatically be configured to use the
|
||||
* started server instead of using a browser driver directly. It should only be
|
||||
* necessary to set the `SELENIUM_SERVER_JAR` when testing locally against
|
||||
* browsers not natively supported by the WebDriver
|
||||
* {@link ../index.Builder Builder}.
|
||||
*
|
||||
* When either of the `SELENIUM_REMOTE_URL` or `SELENIUM_SERVER_JAR` environment
|
||||
* variables are set, the `SELENIUM_BROWSER` variable must also be set.
|
||||
*
|
||||
* @param {boolean=} force whether to force this module to re-initialize and
|
||||
* scan `process.env` again to determine which browsers to run tests
|
||||
* against.
|
||||
*/
|
||||
function init(force = false) {
|
||||
if (wasInit && !force) {
|
||||
return
|
||||
}
|
||||
wasInit = true
|
||||
|
||||
// If force re-init, kill the current server if there is one.
|
||||
if (seleniumServer) {
|
||||
seleniumServer.kill()
|
||||
seleniumServer = null
|
||||
}
|
||||
|
||||
seleniumJar = process.env['SELENIUM_SERVER_JAR']
|
||||
seleniumUrl = process.env['SELENIUM_REMOTE_URL']
|
||||
if (seleniumJar) {
|
||||
info(`Using Selenium server jar: ${seleniumJar}`)
|
||||
}
|
||||
|
||||
if (seleniumUrl) {
|
||||
info(`Using Selenium remote end: ${seleniumUrl}`)
|
||||
}
|
||||
|
||||
if (seleniumJar && seleniumUrl) {
|
||||
throw Error(
|
||||
'Ambiguous test configuration: both SELENIUM_REMOTE_URL' +
|
||||
' && SELENIUM_SERVER_JAR environment variables are set'
|
||||
)
|
||||
}
|
||||
|
||||
const envBrowsers = getBrowsersToTestFromEnv()
|
||||
if ((seleniumJar || seleniumUrl) && envBrowsers.length === 0) {
|
||||
throw Error(
|
||||
'Ambiguous test configuration: when either the SELENIUM_REMOTE_URL or' +
|
||||
' SELENIUM_SERVER_JAR environment variable is set, the' +
|
||||
' SELENIUM_BROWSER variable must also be set.'
|
||||
)
|
||||
}
|
||||
|
||||
targetBrowsers = envBrowsers.length > 0 ? envBrowsers : getAvailableBrowsers()
|
||||
info(
|
||||
`Running tests against [${targetBrowsers.map((b) => b.name).join(', ')}]`
|
||||
)
|
||||
|
||||
after(function () {
|
||||
if (seleniumServer) {
|
||||
return seleniumServer.kill()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const TARGET_MAP = /** !WeakMap<!Environment, !TargetBrowser> */ new WeakMap()
|
||||
const URL_MAP =
|
||||
/** !WeakMap<!Environment, ?(string|remote.SeleniumServer)> */ new WeakMap()
|
||||
|
||||
/**
|
||||
* Defines the environment a {@linkplain suite test suite} is running against.
|
||||
* @final
|
||||
*/
|
||||
class Environment {
|
||||
/**
|
||||
* @param {!TargetBrowser} browser the browser targetted in this environment.
|
||||
* @param {?(string|remote.SeleniumServer)=} url remote URL of an existing
|
||||
* Selenium server to test against.
|
||||
*/
|
||||
constructor(browser, url = undefined) {
|
||||
browser = /** @type {!TargetBrowser} */ (
|
||||
Object.seal(Object.assign({}, browser))
|
||||
)
|
||||
|
||||
TARGET_MAP.set(this, browser)
|
||||
URL_MAP.set(this, url || null)
|
||||
}
|
||||
|
||||
/** @return {!TargetBrowser} the target browser for this test environment. */
|
||||
get browser() {
|
||||
return TARGET_MAP.get(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a predicate function that will suppress tests in this environment
|
||||
* if the {@linkplain #browser current browser} is in the list of
|
||||
* `browsersToIgnore`.
|
||||
*
|
||||
* @param {...(string|!Browser)} browsersToIgnore the browsers that should
|
||||
* be ignored.
|
||||
* @return {function(): boolean} a new predicate function.
|
||||
*/
|
||||
browsers(...browsersToIgnore) {
|
||||
return () => browsersToIgnore.indexOf(this.browser.name) !== -1
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {!Builder} a new WebDriver builder configured to target this
|
||||
* environment's {@linkplain #browser browser}.
|
||||
*/
|
||||
builder() {
|
||||
const browser = this.browser
|
||||
const urlOrServer = URL_MAP.get(this)
|
||||
|
||||
const builder = new Builder()
|
||||
builder.disableEnvironmentOverrides()
|
||||
|
||||
const realBuild = builder.build
|
||||
builder.build = function () {
|
||||
builder.forBrowser(browser.name, browser.version, browser.platform)
|
||||
|
||||
if (browser.capabilities) {
|
||||
builder.getCapabilities().merge(browser.capabilities)
|
||||
}
|
||||
|
||||
if (browser.name === 'firefox') {
|
||||
builder.setCapability('moz:debuggerAddress', true)
|
||||
}
|
||||
|
||||
if (typeof urlOrServer === 'string') {
|
||||
builder.usingServer(urlOrServer)
|
||||
} else if (urlOrServer) {
|
||||
builder.usingServer(urlOrServer.address())
|
||||
}
|
||||
return realBuild.call(builder)
|
||||
}
|
||||
|
||||
return builder
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration options for a {@linkplain ./index.suite test suite}.
|
||||
* @record
|
||||
*/
|
||||
function SuiteOptions() {}
|
||||
|
||||
/**
|
||||
* The browsers to run the test suite against.
|
||||
* @type {!Array<!(Browser|TargetBrowser)>}
|
||||
*/
|
||||
SuiteOptions.prototype.browsers
|
||||
|
||||
let inSuite = false
|
||||
|
||||
/**
|
||||
* Defines a test suite by calling the provided function once for each of the
|
||||
* target browsers. If a suite is not limited to a specific set of browsers in
|
||||
* the provided {@linkplain ./index.SuiteOptions suite options}, the suite will
|
||||
* be configured to run against each of the {@linkplain ./index.init runtime
|
||||
* target browsers}.
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* const {By, Key, until} = require('selenium-webdriver');
|
||||
* const {suite} = require('selenium-webdriver/testing');
|
||||
*
|
||||
* suite(function(env) {
|
||||
* describe('Google Search', function() {
|
||||
* let driver;
|
||||
*
|
||||
* before(async function() {
|
||||
* driver = await env.builder().build();
|
||||
* });
|
||||
*
|
||||
* after(() => driver.quit());
|
||||
*
|
||||
* it('demo', async function() {
|
||||
* await driver.get('http://www.google.com/ncr');
|
||||
*
|
||||
* let q = await driver.findElement(By.name('q'));
|
||||
* await q.sendKeys('webdriver', Key.RETURN);
|
||||
* await driver.wait(
|
||||
* until.titleIs('webdriver - Google Search'), 1000);
|
||||
* });
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* By default, this example suite will run against every WebDriver-enabled
|
||||
* browser on the current system. Alternatively, the `SELENIUM_BROWSER`
|
||||
* environment variable may be used to run against a specific browser:
|
||||
*
|
||||
* SELENIUM_BROWSER=firefox mocha -t 120000 example_test.js
|
||||
*
|
||||
* @param {function(!Environment)} fn the function to call to build the test
|
||||
* suite.
|
||||
* @param {SuiteOptions=} options configuration options.
|
||||
*/
|
||||
function suite(fn, options = undefined) {
|
||||
if (inSuite) {
|
||||
throw Error('Calls to suite() may not be nested')
|
||||
}
|
||||
try {
|
||||
init()
|
||||
inSuite = true
|
||||
|
||||
const suiteBrowsers = new Map()
|
||||
if (options && options.browsers) {
|
||||
for (let browser of options.browsers) {
|
||||
if (typeof browser === 'string') {
|
||||
suiteBrowsers.set(browser, { name: browser })
|
||||
} else {
|
||||
suiteBrowsers.set(browser.name, browser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let browser of targetBrowsers) {
|
||||
if (suiteBrowsers.size > 0 && !suiteBrowsers.has(browser.name)) {
|
||||
continue
|
||||
}
|
||||
|
||||
describe(`[${browser.name}]`, function () {
|
||||
if (!seleniumUrl && seleniumJar && !seleniumServer) {
|
||||
seleniumServer = new remote.SeleniumServer(seleniumJar)
|
||||
|
||||
const startTimeout = 65 * 1000
|
||||
// eslint-disable-next-line no-inner-declarations
|
||||
function startSelenium() {
|
||||
if (typeof this.timeout === 'function') {
|
||||
this.timeout(startTimeout) // For mocha.
|
||||
}
|
||||
|
||||
info(`Starting selenium server ${seleniumJar}`)
|
||||
return seleniumServer.start(60 * 1000)
|
||||
}
|
||||
|
||||
const /** !Function */ beforeHook = global.beforeAll || global.before
|
||||
beforeHook(startSelenium, startTimeout)
|
||||
}
|
||||
|
||||
fn(new Environment(browser, seleniumUrl || seleniumServer))
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
inSuite = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with wrappers for the standard mocha/jasmine test
|
||||
* functions: `describe` and `it`, which will redirect to `xdescribe` and `xit`,
|
||||
* respectively, if provided predicate function returns false.
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* const {Browser} = require('selenium-webdriver');
|
||||
* const {suite, ignore} = require('selenium-webdriver/testing');
|
||||
*
|
||||
* suite(function(env) {
|
||||
*
|
||||
* // Skip tests the current environment targets Chrome.
|
||||
* ignore(env.browsers(Browser.CHROME)).
|
||||
* describe('something', async function() {
|
||||
* let driver = await env.builder().build();
|
||||
* // etc.
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {function(): boolean} predicateFn A predicate to call to determine
|
||||
* if the test should be suppressed. This function MUST be synchronous.
|
||||
* @return {{describe: !Function, it: !Function}} an object with wrapped
|
||||
* versions of the `describe` and `it` test functions.
|
||||
*/
|
||||
function ignore(predicateFn) {
|
||||
const isJasmine = global.jasmine && typeof global.jasmine === 'object'
|
||||
|
||||
const hooks = {
|
||||
describe: getTestHook('describe'),
|
||||
xdescribe: getTestHook('xdescribe'),
|
||||
it: getTestHook('it'),
|
||||
xit: getTestHook('xit'),
|
||||
}
|
||||
hooks.fdescribe = isJasmine ? getTestHook('fdescribe') : hooks.describe.only
|
||||
hooks.fit = isJasmine ? getTestHook('fit') : hooks.it.only
|
||||
|
||||
let describe = wrap(hooks.xdescribe, hooks.describe)
|
||||
let fdescribe = wrap(hooks.xdescribe, hooks.fdescribe)
|
||||
//eslint-disable-next-line no-only-tests/no-only-tests
|
||||
describe.only = fdescribe
|
||||
|
||||
let it = wrap(hooks.xit, hooks.it)
|
||||
let fit = wrap(hooks.xit, hooks.fit)
|
||||
//eslint-disable-next-line no-only-tests/no-only-tests
|
||||
it.only = fit
|
||||
|
||||
return { describe, it }
|
||||
|
||||
function wrap(onSkip, onRun) {
|
||||
return function (...args) {
|
||||
if (predicateFn()) {
|
||||
onSkip(...args)
|
||||
} else {
|
||||
onRun(...args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @return {!Function}
|
||||
* @throws {TypeError}
|
||||
*/
|
||||
function getTestHook(name) {
|
||||
let fn = global[name]
|
||||
let type = typeof fn
|
||||
if (type !== 'function') {
|
||||
throw TypeError(
|
||||
`Expected global.${name} to be a function, but is ${type}.` +
|
||||
' This can happen if you try using this module when running with' +
|
||||
' node directly instead of using jasmine or mocha'
|
||||
)
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
// PUBLIC API
|
||||
|
||||
module.exports = {
|
||||
Environment,
|
||||
SuiteOptions,
|
||||
init,
|
||||
ignore,
|
||||
suite,
|
||||
}
|
||||
Reference in New Issue
Block a user