selenium-webdriver 3.1.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGES.md +112 -0
  2. package/NOTICE +1 -1
  3. package/README.md +5 -11
  4. package/chrome.js +122 -33
  5. package/edge.js +2 -2
  6. package/example/async_await_test.js +69 -0
  7. package/example/chrome_android.js +2 -3
  8. package/example/chrome_mobile_emulation.js +3 -4
  9. package/example/firefox_channels.js +3 -3
  10. package/example/google_search.js +3 -3
  11. package/example/google_search_generator.js +2 -5
  12. package/example/google_search_test.js +5 -6
  13. package/example/headless.js +55 -0
  14. package/example/logging.js +3 -3
  15. package/example/parallel_flows.js +4 -7
  16. package/firefox/binary.js +3 -0
  17. package/firefox/extension.js +118 -81
  18. package/firefox/index.js +117 -263
  19. package/firefox/profile.js +18 -137
  20. package/ie.js +2 -2
  21. package/index.js +4 -1
  22. package/io/index.js +59 -0
  23. package/io/zip.js +214 -0
  24. package/lib/atoms/get-attribute.js +9 -0
  25. package/lib/atoms/is-displayed.js +73 -78
  26. package/lib/by.js +8 -0
  27. package/lib/capabilities.js +61 -22
  28. package/lib/command.js +2 -0
  29. package/lib/error.js +32 -2
  30. package/lib/http.js +53 -44
  31. package/lib/promise.js +72 -25
  32. package/lib/test/data/click_tests/disabled_element.html +12 -0
  33. package/lib/test/data/content-editable.html +10 -0
  34. package/lib/test/data/firefox/webextension.xpi +0 -0
  35. package/lib/test/data/key_logger.html +34 -0
  36. package/lib/test/data/nestedElements.html +10 -1
  37. package/lib/test/data/scrolling_tests/page_with_partially_hidden_element.html +14 -0
  38. package/lib/test/data/single_text_input.html +12 -0
  39. package/lib/test/index.js +4 -36
  40. package/lib/webdriver.js +147 -55
  41. package/net/portprober.js +1 -1
  42. package/opera.js +1 -1
  43. package/package.json +2 -2
  44. package/phantomjs.js +2 -2
  45. package/remote/index.js +21 -14
  46. package/safari.js +2 -2
  47. package/test/firefox/extension_test.js +41 -17
  48. package/test/firefox/firefox_test.js +37 -19
  49. package/test/firefox/profile_test.js +14 -59
  50. package/test/http/http_test.js +18 -0
  51. package/test/{io_test.js → io/io_test.js} +40 -1
  52. package/test/io/zip_test.js +128 -0
  53. package/test/lib/by_test.js +21 -0
  54. package/test/lib/error_test.js +21 -1
  55. package/test/lib/http_test.js +11 -3
  56. package/test/lib/webdriver_test.js +107 -3
  57. package/test/page_loading_test.js +0 -6
  58. package/test/remote_test.js +3 -3
  59. package/test/session_test.js +68 -23
  60. package/testing/index.js +2 -3
  61. package/lib/atoms/getAttribute.js +0 -12
  62. package/lib/firefox/amd64/libnoblur64.so +0 -0
  63. package/lib/firefox/i386/libnoblur.so +0 -0
  64. package/lib/firefox/webdriver.json +0 -69
  65. package/lib/firefox/webdriver.xpi +0 -0
@@ -0,0 +1,55 @@
1
+ // Licensed to the Software Freedom Conservancy (SFC) under one
2
+ // or more contributor license agreements. See the NOTICE file
3
+ // distributed with this work for additional information
4
+ // regarding copyright ownership. The SFC licenses this file
5
+ // to you under the Apache License, Version 2.0 (the
6
+ // "License"); you may not use this file except in compliance
7
+ // with the License. You may obtain a copy of the License at
8
+ //
9
+ // http://www.apache.org/licenses/LICENSE-2.0
10
+ //
11
+ // Unless required by applicable law or agreed to in writing,
12
+ // software distributed under the License is distributed on an
13
+ // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ // KIND, either express or implied. See the License for the
15
+ // specific language governing permissions and limitations
16
+ // under the License.
17
+
18
+ /**
19
+ * @fileoverview An example of running Chrome or Firefox in headless mode.
20
+ *
21
+ * To run with Chrome, ensure you have Chrome 59+ installed and that
22
+ * chromedriver 2.30+ is present on your system PATH:
23
+ * <https://sites.google.com/a/chromium.org/chromedriver/downloads>
24
+ *
25
+ * SELENIUM_BROWSER=chrome node selenium-webdriver/example/headless.js
26
+ *
27
+ * To run with Firefox, ensure you have Firefox 57+ installed and that
28
+ * geckodriver 0.19.0+ is present on your system PATH:
29
+ * <https://github.com/mozilla/geckodriver/releases>
30
+ *
31
+ * SELENIUM_BROWSER=firefox node selenium-webdriver/example/headless.js
32
+ */
33
+
34
+ const chrome = require('../chrome');
35
+ const firefox = require('../firefox');
36
+ const {Builder, By, Key, until} = require('..');
37
+
38
+ const width = 640;
39
+ const height = 480;
40
+
41
+ let driver = new Builder()
42
+ .forBrowser('chrome')
43
+ .setChromeOptions(
44
+ new chrome.Options().headless().windowSize({width, height}))
45
+ .setFirefoxOptions(
46
+ new firefox.Options().headless().windowSize({width, height}))
47
+ .build();
48
+
49
+ driver.get('http://www.google.com/ncr')
50
+ .then(_ =>
51
+ driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN))
52
+ .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
53
+ .then(
54
+ _ => driver.quit(),
55
+ e => driver.quit().then(() => { throw e; }));
@@ -21,7 +21,7 @@
21
21
 
22
22
  'use strict';
23
23
 
24
- const {Builder, By, logging, until} = require('..');
24
+ const {Builder, By, Key, logging, until} = require('..');
25
25
 
26
26
  logging.installConsoleHandler();
27
27
  logging.getLogger('webdriver.http').setLevel(logging.Level.ALL);
@@ -29,7 +29,7 @@ logging.getLogger('webdriver.http').setLevel(logging.Level.ALL);
29
29
  var driver = new Builder().forBrowser('firefox').build();
30
30
 
31
31
  driver.get('http://www.google.com/ncr')
32
- .then(_ => driver.findElement(By.name('q')).sendKeys('webdriver'))
33
- .then(_ => driver.findElement(By.name('btnG')).click())
32
+ .then(_ =>
33
+ driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN))
34
34
  .then(_ => driver.wait(until.titleIs('webdriver - Google Search'), 1000))
35
35
  .then(_ => driver.quit());
@@ -23,18 +23,16 @@
23
23
  * (see <https://github.com/SeleniumHQ/selenium/issues/2969>).
24
24
  */
25
25
 
26
- var webdriver = require('..'),
27
- By = webdriver.By,
28
- until = webdriver.until;
26
+ const {Builder, By, Key, promise, until} = require('..');
29
27
 
30
28
  for (var i = 0; i < 3; i++) {
31
29
  (function(n) {
32
- var flow = new webdriver.promise.ControlFlow()
30
+ var flow = new promise.ControlFlow()
33
31
  .on('uncaughtException', function(e) {
34
32
  console.log('uncaughtException in flow %d: %s', n, e);
35
33
  });
36
34
 
37
- var driver = new webdriver.Builder().
35
+ var driver = new Builder().
38
36
  forBrowser('firefox').
39
37
  setControlFlow(flow). // Comment out this line to see the difference.
40
38
  build();
@@ -44,8 +42,7 @@ for (var i = 0; i < 3; i++) {
44
42
  driver.manage().window().setPosition(300 * i, 400 * i);
45
43
 
46
44
  driver.get('http://www.google.com');
47
- driver.findElement(By.name('q')).sendKeys('webdriver');
48
- driver.findElement(By.name('btnG')).click();
45
+ driver.findElement(By.name('q')).sendKeys('webdriver', Key.RETURN);
49
46
  driver.wait(until.titleIs('webdriver - Google Search'), 1000);
50
47
 
51
48
  driver.quit();
package/firefox/binary.js CHANGED
@@ -203,6 +203,8 @@ function installNoFocusLibs(profileDir) {
203
203
  * `PATH`.
204
204
  *
205
205
  * @final
206
+ * @deprecated This class will be removed in 4.0. Use the binary management
207
+ * functions available on the {@link ./index.Options firefox.Options} class.
206
208
  */
207
209
  class Binary {
208
210
  /**
@@ -242,6 +244,7 @@ class Binary {
242
244
  * Add arguments to the command line used to start Firefox.
243
245
  * @param {...(string|!Array.<string>)} var_args Either the arguments to add
244
246
  * as varargs, or the arguments as an array.
247
+ * @deprecated Use {@link ./index.Options#addArguments}.
245
248
  */
246
249
  addArguments(var_args) {
247
250
  for (var i = 0; i < arguments.length; i++) {
@@ -19,12 +19,12 @@
19
19
 
20
20
  'use strict';
21
21
 
22
- const AdmZip = require('adm-zip'),
23
- fs = require('fs'),
22
+ const fs = require('fs'),
24
23
  path = require('path'),
25
24
  xml = require('xml2js');
26
25
 
27
26
  const io = require('../io');
27
+ const zip = require('../io/zip');
28
28
 
29
29
 
30
30
  /**
@@ -56,11 +56,7 @@ function install(extension, dir) {
56
56
  if (!details.unpack) {
57
57
  return io.copy(extension, dst + '.xpi').then(() => details.id);
58
58
  } else {
59
- return Promise.resolve().then(function() {
60
- // TODO: find an async library for inflating a zip archive.
61
- new AdmZip(extension).extractAllTo(dst, true);
62
- return details.id;
63
- });
59
+ return zip.unzip(extension, dst).then(() => details.id);
64
60
  }
65
61
  } else {
66
62
  return io.copyDir(extension, dst).then(() => details.id);
@@ -79,105 +75,146 @@ var AddonDetails;
79
75
  var RdfRoot;
80
76
 
81
77
 
82
-
83
78
  /**
84
79
  * Extracts the details needed to install an add-on.
85
80
  * @param {string} addonPath Path to the extension directory.
86
81
  * @return {!Promise<!AddonDetails>} A promise for the add-on details.
87
82
  */
88
83
  function getDetails(addonPath) {
89
- return readManifest(addonPath).then(function(doc) {
90
- var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
91
- var rdf = getNamespaceId(
92
- doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
93
-
94
- var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
95
- var details = {
96
- id: getNodeText(description, em + 'id'),
97
- name: getNodeText(description, em + 'name'),
98
- version: getNodeText(description, em + 'version'),
99
- unpack: getNodeText(description, em + 'unpack') || false
100
- };
101
-
102
- if (typeof details.unpack === 'string') {
103
- details.unpack = details.unpack.toLowerCase() === 'true';
84
+ return io.stat(addonPath).then((stats) => {
85
+ if (stats.isDirectory()) {
86
+ return parseDirectory(addonPath);
87
+ } else if (addonPath.slice(-4) === '.xpi') {
88
+ return parseXpiFile(addonPath);
89
+ } else {
90
+ throw Error('Add-on path is not an xpi or a directory: ' + addonPath);
104
91
  }
92
+ });
105
93
 
106
- if (!details.id) {
107
- throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
108
- }
94
+ /**
95
+ * Parse an install.rdf for a Firefox add-on.
96
+ * @param {string} rdf The contents of install.rdf for the add-on.
97
+ * @return {!Promise<!AddonDetails>} A promise for the add-on details.
98
+ */
99
+ function parseInstallRdf(rdf) {
100
+ return parseXml(rdf).then(function(doc) {
101
+ var em = getNamespaceId(doc, 'http://www.mozilla.org/2004/em-rdf#');
102
+ var rdf = getNamespaceId(
103
+ doc, 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
104
+
105
+ var description = doc[rdf + 'RDF'][rdf + 'Description'][0];
106
+ var details = {
107
+ id: getNodeText(description, em + 'id'),
108
+ name: getNodeText(description, em + 'name'),
109
+ version: getNodeText(description, em + 'version'),
110
+ unpack: getNodeText(description, em + 'unpack') || false
111
+ };
112
+
113
+ if (typeof details.unpack === 'string') {
114
+ details.unpack = details.unpack.toLowerCase() === 'true';
115
+ }
109
116
 
110
- return details;
111
- });
117
+ if (!details.id) {
118
+ throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
119
+ }
112
120
 
113
- function getNodeText(node, name) {
114
- return node[name] && node[name][0] || '';
115
- }
121
+ return details;
122
+ });
116
123
 
117
- function getNamespaceId(doc, url) {
118
- var keys = Object.keys(doc);
119
- if (keys.length !== 1) {
120
- throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
124
+ function parseXml(text) {
125
+ return new Promise((resolve, reject) => {
126
+ xml.parseString(text, (err, data) => {
127
+ if (err) {
128
+ reject(err);
129
+ } else {
130
+ resolve(data);
131
+ }
132
+ });
133
+ });
121
134
  }
122
135
 
123
- var namespaces = /** @type {!RdfRoot} */(doc[keys[0]]).$;
124
- var id = '';
125
- Object.keys(namespaces).some(function(ns) {
126
- if (namespaces[ns] !== url) {
127
- return false;
128
- }
136
+ function getNodeText(node, name) {
137
+ return node[name] && node[name][0] || '';
138
+ }
129
139
 
130
- if (ns.indexOf(':') != -1) {
131
- id = ns.split(':')[1] + ':';
140
+ function getNamespaceId(doc, url) {
141
+ var keys = Object.keys(doc);
142
+ if (keys.length !== 1) {
143
+ throw new AddonFormatError('Malformed manifest for add-on ' + addonPath);
132
144
  }
133
- return true;
134
- });
135
- return id;
136
- }
137
- }
138
145
 
146
+ var namespaces = /** @type {!RdfRoot} */(doc[keys[0]]).$;
147
+ var id = '';
148
+ Object.keys(namespaces).some(function(ns) {
149
+ if (namespaces[ns] !== url) {
150
+ return false;
151
+ }
139
152
 
140
- /**
141
- * Reads the manifest for a Firefox add-on.
142
- * @param {string} addonPath Path to a Firefox add-on as a xpi or an extension.
143
- * @return {!Promise<!Object>} A promise for the parsed manifest.
144
- */
145
- function readManifest(addonPath) {
146
- var manifest;
153
+ if (ns.indexOf(':') != -1) {
154
+ id = ns.split(':')[1] + ':';
155
+ }
156
+ return true;
157
+ });
158
+ return id;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Parse a manifest for a Firefox WebExtension.
164
+ * @param {{
165
+ * name: string,
166
+ * version: string,
167
+ * applications: {gecko:{id:string}}
168
+ * }} json JSON representation of the manifest.
169
+ * @return {!AddonDetails} The add-on details.
170
+ */
171
+ function parseManifestJson({name, version, applications}) {
172
+ if (!(applications && applications.gecko && applications.gecko.id)) {
173
+ throw new AddonFormatError('Could not find add-on ID for ' + addonPath);
174
+ }
147
175
 
148
- if (addonPath.slice(-4) === '.xpi') {
149
- manifest = new Promise((resolve, reject) => {
150
- let zip = new AdmZip(addonPath);
176
+ return {id: applications.gecko.id, name, version, unpack: false};
177
+ }
151
178
 
152
- if (!zip.getEntry('install.rdf')) {
153
- reject(new AddonFormatError(
154
- 'Could not find install.rdf in ' + addonPath));
155
- return;
179
+ function parseXpiFile(filePath) {
180
+ return zip.load(filePath).then(archive => {
181
+ if (archive.has('install.rdf')) {
182
+ return archive.getFile('install.rdf')
183
+ .then(buf => parseInstallRdf(buf.toString('utf8')));
156
184
  }
157
185
 
158
- zip.readAsTextAsync('install.rdf', resolve);
159
- });
160
- } else {
161
- manifest = io.stat(addonPath).then(function(stats) {
162
- if (!stats.isDirectory()) {
163
- throw Error(
164
- 'Add-on path is neither a xpi nor a directory: ' + addonPath);
186
+ if (archive.has('manifest.json')) {
187
+ return archive.getFile('manifest.json')
188
+ .then(buf => JSON.parse(buf.toString('utf8')))
189
+ .then(parseManifestJson);
165
190
  }
166
- return io.read(path.join(addonPath, 'install.rdf'));
191
+
192
+ throw new AddonFormatError(
193
+ `Couldn't find install.rdf or manifest.json in ${filePath}`);
167
194
  });
168
195
  }
169
196
 
170
- return manifest.then(function(content) {
171
- return new Promise((resolve, reject) => {
172
- xml.parseString(content, (err, data) => {
173
- if (err) {
174
- reject(err);
175
- } else {
176
- resolve(data);
177
- }
178
- });
179
- });
180
- });
197
+ function parseDirectory(dirPath) {
198
+ const rdfPath = path.join(dirPath, 'install.rdf');
199
+ const jsonPath = path.join(dirPath, 'manifest.json');
200
+ return io.exists(rdfPath)
201
+ .then(rdfExists => {
202
+ if (rdfExists) {
203
+ return io.read(rdfPath)
204
+ .then(buf => parseInstallRdf(buf.toString('utf8')));
205
+ }
206
+ return io.exists(jsonPath)
207
+ .then(jsonExists => {
208
+ if (jsonExists) {
209
+ return io.read(jsonPath)
210
+ .then(buf => JSON.parse(buf.toString('utf8')))
211
+ .then(parseManifestJson);
212
+ }
213
+ throw new AddonFormatError(
214
+ `Couldn't find install.rdf or manifest.json in ${dirPath}`);
215
+ });
216
+ })
217
+ }
181
218
  }
182
219
 
183
220