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.
- package/CHANGES.md +112 -0
- package/NOTICE +1 -1
- package/README.md +5 -11
- package/chrome.js +122 -33
- package/edge.js +2 -2
- package/example/async_await_test.js +69 -0
- package/example/chrome_android.js +2 -3
- package/example/chrome_mobile_emulation.js +3 -4
- package/example/firefox_channels.js +3 -3
- package/example/google_search.js +3 -3
- package/example/google_search_generator.js +2 -5
- package/example/google_search_test.js +5 -6
- package/example/headless.js +55 -0
- package/example/logging.js +3 -3
- package/example/parallel_flows.js +4 -7
- package/firefox/binary.js +3 -0
- package/firefox/extension.js +118 -81
- package/firefox/index.js +117 -263
- package/firefox/profile.js +18 -137
- package/ie.js +2 -2
- package/index.js +4 -1
- package/io/index.js +59 -0
- package/io/zip.js +214 -0
- package/lib/atoms/get-attribute.js +9 -0
- package/lib/atoms/is-displayed.js +73 -78
- package/lib/by.js +8 -0
- package/lib/capabilities.js +61 -22
- package/lib/command.js +2 -0
- package/lib/error.js +32 -2
- package/lib/http.js +53 -44
- package/lib/promise.js +72 -25
- package/lib/test/data/click_tests/disabled_element.html +12 -0
- package/lib/test/data/content-editable.html +10 -0
- package/lib/test/data/firefox/webextension.xpi +0 -0
- package/lib/test/data/key_logger.html +34 -0
- package/lib/test/data/nestedElements.html +10 -1
- package/lib/test/data/scrolling_tests/page_with_partially_hidden_element.html +14 -0
- package/lib/test/data/single_text_input.html +12 -0
- package/lib/test/index.js +4 -36
- package/lib/webdriver.js +147 -55
- package/net/portprober.js +1 -1
- package/opera.js +1 -1
- package/package.json +2 -2
- package/phantomjs.js +2 -2
- package/remote/index.js +21 -14
- package/safari.js +2 -2
- package/test/firefox/extension_test.js +41 -17
- package/test/firefox/firefox_test.js +37 -19
- package/test/firefox/profile_test.js +14 -59
- package/test/http/http_test.js +18 -0
- package/test/{io_test.js → io/io_test.js} +40 -1
- package/test/io/zip_test.js +128 -0
- package/test/lib/by_test.js +21 -0
- package/test/lib/error_test.js +21 -1
- package/test/lib/http_test.js +11 -3
- package/test/lib/webdriver_test.js +107 -3
- package/test/page_loading_test.js +0 -6
- package/test/remote_test.js +3 -3
- package/test/session_test.js +68 -23
- package/testing/index.js +2 -3
- package/lib/atoms/getAttribute.js +0 -12
- package/lib/firefox/amd64/libnoblur64.so +0 -0
- package/lib/firefox/i386/libnoblur.so +0 -0
- package/lib/firefox/webdriver.json +0 -69
- package/lib/firefox/webdriver.xpi +0 -0
package/firefox/profile.js
CHANGED
|
@@ -22,50 +22,17 @@
|
|
|
22
22
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
|
-
const
|
|
26
|
-
fs = require('fs'),
|
|
25
|
+
const fs = require('fs'),
|
|
27
26
|
path = require('path'),
|
|
28
27
|
vm = require('vm');
|
|
29
28
|
|
|
30
29
|
const isDevMode = require('../lib/devmode'),
|
|
31
30
|
Symbols = require('../lib/symbols'),
|
|
32
31
|
io = require('../io'),
|
|
32
|
+
{Zip, unzip} = require('../io/zip'),
|
|
33
33
|
extension = require('./extension');
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
/** @const */
|
|
37
|
-
const WEBDRIVER_PREFERENCES_PATH = isDevMode
|
|
38
|
-
? path.join(__dirname, '../../../firefox-driver/webdriver.json')
|
|
39
|
-
: path.join(__dirname, '../lib/firefox/webdriver.json');
|
|
40
|
-
|
|
41
|
-
/** @const */
|
|
42
|
-
const WEBDRIVER_EXTENSION_PATH = isDevMode
|
|
43
|
-
? path.join(__dirname,
|
|
44
|
-
'../../../../build/javascript/firefox-driver/webdriver.xpi')
|
|
45
|
-
: path.join(__dirname, '../lib/firefox/webdriver.xpi');
|
|
46
|
-
|
|
47
|
-
/** @const */
|
|
48
|
-
const WEBDRIVER_EXTENSION_NAME = 'fxdriver@googlecode.com';
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
/** @type {Object} */
|
|
53
|
-
var defaultPreferences = null;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Synchronously loads the default preferences used for the FirefoxDriver.
|
|
57
|
-
* @return {!Object} The default preferences JSON object.
|
|
58
|
-
*/
|
|
59
|
-
function getDefaultPreferences() {
|
|
60
|
-
if (!defaultPreferences) {
|
|
61
|
-
var contents = /** @type {string} */(
|
|
62
|
-
fs.readFileSync(WEBDRIVER_PREFERENCES_PATH, 'utf8'));
|
|
63
|
-
defaultPreferences = /** @type {!Object} */(JSON.parse(contents));
|
|
64
|
-
}
|
|
65
|
-
return defaultPreferences;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
|
|
69
36
|
/**
|
|
70
37
|
* Parses a user.js file in a Firefox profile directory.
|
|
71
38
|
* @param {string} f Path to the file to parse.
|
|
@@ -107,9 +74,13 @@ function writeUserPrefs(prefs, dir) {
|
|
|
107
74
|
var userPrefs = path.join(dir, 'user.js');
|
|
108
75
|
return loadUserPrefs(userPrefs).then(function(overrides) {
|
|
109
76
|
Object.assign(prefs, overrides);
|
|
110
|
-
Object.assign(prefs, getDefaultPreferences()['frozen']);
|
|
111
77
|
|
|
112
|
-
|
|
78
|
+
let keys = Object.keys(prefs);
|
|
79
|
+
if (!keys.length) {
|
|
80
|
+
return dir;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let contents = Object.keys(prefs).map(function(key) {
|
|
113
84
|
return 'user_pref(' + JSON.stringify(key) + ', ' +
|
|
114
85
|
JSON.stringify(prefs[key]) + ');';
|
|
115
86
|
}).join('\n');
|
|
@@ -130,13 +101,10 @@ function writeUserPrefs(prefs, dir) {
|
|
|
130
101
|
* @param {!Array.<string>} extensions The extensions to install, as a
|
|
131
102
|
* path to an unpacked extension directory or a path to a xpi file.
|
|
132
103
|
* @param {string} dir The profile directory to install to.
|
|
133
|
-
* @param {boolean=} opt_excludeWebDriverExt Whether to skip installation of
|
|
134
|
-
* the default WebDriver extension.
|
|
135
104
|
* @return {!Promise<string>} A promise for the main profile directory
|
|
136
105
|
* once all extensions have been installed.
|
|
137
106
|
*/
|
|
138
|
-
function installExtensions(extensions, dir
|
|
139
|
-
var hasWebDriver = !!opt_excludeWebDriverExt;
|
|
107
|
+
function installExtensions(extensions, dir) {
|
|
140
108
|
var next = 0;
|
|
141
109
|
var extensionDir = path.join(dir, 'extensions');
|
|
142
110
|
|
|
@@ -145,11 +113,7 @@ function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
|
|
|
145
113
|
|
|
146
114
|
function installNext() {
|
|
147
115
|
if (next >= extensions.length) {
|
|
148
|
-
|
|
149
|
-
fulfill(dir);
|
|
150
|
-
} else {
|
|
151
|
-
install(WEBDRIVER_EXTENSION_PATH);
|
|
152
|
-
}
|
|
116
|
+
fulfill(dir);
|
|
153
117
|
} else {
|
|
154
118
|
install(extensions[next++]);
|
|
155
119
|
}
|
|
@@ -157,7 +121,6 @@ function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
|
|
|
157
121
|
|
|
158
122
|
function install(ext) {
|
|
159
123
|
extension.install(ext, extensionDir).then(function(id) {
|
|
160
|
-
hasWebDriver = hasWebDriver || (id === WEBDRIVER_EXTENSION_NAME);
|
|
161
124
|
installNext();
|
|
162
125
|
}, reject);
|
|
163
126
|
}
|
|
@@ -165,26 +128,6 @@ function installExtensions(extensions, dir, opt_excludeWebDriverExt) {
|
|
|
165
128
|
}
|
|
166
129
|
|
|
167
130
|
|
|
168
|
-
/**
|
|
169
|
-
* Decodes a base64 encoded profile.
|
|
170
|
-
* @param {string} data The base64 encoded string.
|
|
171
|
-
* @return {!Promise<string>} A promise for the path to the decoded profile
|
|
172
|
-
* directory.
|
|
173
|
-
*/
|
|
174
|
-
function decode(data) {
|
|
175
|
-
return io.tmpFile().then(function(file) {
|
|
176
|
-
var buf = new Buffer(data, 'base64');
|
|
177
|
-
return io.write(file, buf)
|
|
178
|
-
.then(io.tmpDir)
|
|
179
|
-
.then(function(dir) {
|
|
180
|
-
var zip = new AdmZip(file);
|
|
181
|
-
zip.extractAllTo(dir); // Sync only? Why?? :-(
|
|
182
|
-
return dir;
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
131
|
|
|
189
132
|
/**
|
|
190
133
|
* Models a Firefox profile directory for use with the FirefoxDriver. The
|
|
@@ -201,15 +144,9 @@ class Profile {
|
|
|
201
144
|
/** @private {!Object} */
|
|
202
145
|
this.preferences_ = {};
|
|
203
146
|
|
|
204
|
-
/** @private {boolean} */
|
|
205
|
-
this.nativeEventsEnabled_ = true;
|
|
206
|
-
|
|
207
147
|
/** @private {(string|undefined)} */
|
|
208
148
|
this.template_ = opt_dir;
|
|
209
149
|
|
|
210
|
-
/** @private {number} */
|
|
211
|
-
this.port_ = 0;
|
|
212
|
-
|
|
213
150
|
/** @private {!Array<string>} */
|
|
214
151
|
this.extensions_ = [];
|
|
215
152
|
}
|
|
@@ -245,12 +182,6 @@ class Profile {
|
|
|
245
182
|
* @throws {Error} If attempting to set a frozen preference.
|
|
246
183
|
*/
|
|
247
184
|
setPreference(key, value) {
|
|
248
|
-
var frozen = getDefaultPreferences()['frozen'];
|
|
249
|
-
if (frozen.hasOwnProperty(key) && frozen[key] !== value) {
|
|
250
|
-
throw Error('You may not set ' + key + '=' + JSON.stringify(value)
|
|
251
|
-
+ '; value is frozen for proper WebDriver functionality ('
|
|
252
|
-
+ key + '=' + JSON.stringify(frozen[key]) + ')');
|
|
253
|
-
}
|
|
254
185
|
this.preferences_[key] = value;
|
|
255
186
|
}
|
|
256
187
|
|
|
@@ -285,23 +216,6 @@ class Profile {
|
|
|
285
216
|
this.preferences_['webdriver_firefox_allowed_hosts'] = host;
|
|
286
217
|
}
|
|
287
218
|
|
|
288
|
-
/**
|
|
289
|
-
* @return {number} The port this profile is currently configured to use, or
|
|
290
|
-
* 0 if the port will be selected at random when the profile is written
|
|
291
|
-
* to disk.
|
|
292
|
-
*/
|
|
293
|
-
getPort() {
|
|
294
|
-
return this.port_;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Sets the port to use for the WebDriver extension loaded by this profile.
|
|
299
|
-
* @param {number} port The desired port, or 0 to use any free port.
|
|
300
|
-
*/
|
|
301
|
-
setPort(port) {
|
|
302
|
-
this.port_ = port;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
219
|
/**
|
|
306
220
|
* @return {boolean} Whether the FirefoxDriver is configured to automatically
|
|
307
221
|
* accept untrusted SSL certificates.
|
|
@@ -335,32 +249,12 @@ class Profile {
|
|
|
335
249
|
return !!this.preferences_['webdriver_assume_untrusted_issuer'];
|
|
336
250
|
}
|
|
337
251
|
|
|
338
|
-
/**
|
|
339
|
-
* Sets whether to use native events with this profile.
|
|
340
|
-
* @param {boolean} enabled .
|
|
341
|
-
*/
|
|
342
|
-
setNativeEventsEnabled(enabled) {
|
|
343
|
-
this.nativeEventsEnabled_ = enabled;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Returns whether native events are enabled in this profile.
|
|
348
|
-
* @return {boolean} .
|
|
349
|
-
*/
|
|
350
|
-
nativeEventsEnabled() {
|
|
351
|
-
return this.nativeEventsEnabled_;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
252
|
/**
|
|
355
253
|
* Writes this profile to disk.
|
|
356
|
-
* @param {boolean=} opt_excludeWebDriverExt Whether to exclude the WebDriver
|
|
357
|
-
* extension from the generated profile. Used to reduce the size of an
|
|
358
|
-
* {@link #encode() encoded profile} since the server will always install
|
|
359
|
-
* the extension itself.
|
|
360
254
|
* @return {!Promise<string>} A promise for the path to the new profile
|
|
361
255
|
* directory.
|
|
362
256
|
*/
|
|
363
|
-
writeToDisk(
|
|
257
|
+
writeToDisk() {
|
|
364
258
|
var profileDir = io.tmpDir();
|
|
365
259
|
if (this.template_) {
|
|
366
260
|
profileDir = profileDir.then(function(dir) {
|
|
@@ -371,10 +265,7 @@ class Profile {
|
|
|
371
265
|
}
|
|
372
266
|
|
|
373
267
|
// Freeze preferences for async operations.
|
|
374
|
-
|
|
375
|
-
Object.assign(prefs, getDefaultPreferences()['mutable']);
|
|
376
|
-
Object.assign(prefs, getDefaultPreferences()['frozen']);
|
|
377
|
-
Object.assign(prefs, this.preferences_);
|
|
268
|
+
let prefs = Object.assign({}, this.preferences_);
|
|
378
269
|
|
|
379
270
|
// Freeze extensions for async operations.
|
|
380
271
|
var extensions = this.extensions_.concat();
|
|
@@ -382,7 +273,7 @@ class Profile {
|
|
|
382
273
|
return profileDir.then(function(dir) {
|
|
383
274
|
return writeUserPrefs(prefs, dir);
|
|
384
275
|
}).then(function(dir) {
|
|
385
|
-
return installExtensions(extensions, dir
|
|
276
|
+
return installExtensions(extensions, dir);
|
|
386
277
|
});
|
|
387
278
|
}
|
|
388
279
|
|
|
@@ -395,20 +286,11 @@ class Profile {
|
|
|
395
286
|
*
|
|
396
287
|
*/
|
|
397
288
|
encode() {
|
|
398
|
-
return this.writeToDisk(
|
|
399
|
-
|
|
400
|
-
zip.
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
entry.header.method = 0;
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
return io.tmpFile().then(function(file) {
|
|
407
|
-
zip.writeZip(file); // Sync! Why oh why :-(
|
|
408
|
-
return io.read(file);
|
|
409
|
-
});
|
|
410
|
-
}).then(function(data) {
|
|
411
|
-
return data.toString('base64');
|
|
289
|
+
return this.writeToDisk().then(function(dir) {
|
|
290
|
+
let zip = new Zip;
|
|
291
|
+
return zip.addDir(dir)
|
|
292
|
+
.then(() => zip.toBuffer())
|
|
293
|
+
.then(buf => buf.toString('base64'));
|
|
412
294
|
});
|
|
413
295
|
}
|
|
414
296
|
|
|
@@ -426,5 +308,4 @@ class Profile {
|
|
|
426
308
|
|
|
427
309
|
|
|
428
310
|
exports.Profile = Profile;
|
|
429
|
-
exports.decode = decode;
|
|
430
311
|
exports.loadUserPrefs = loadUserPrefs;
|
package/ie.js
CHANGED
|
@@ -420,8 +420,8 @@ class Driver extends webdriver.WebDriver {
|
|
|
420
420
|
var client = service.start().then(url => new http.HttpClient(url));
|
|
421
421
|
var executor = new http.Executor(client);
|
|
422
422
|
|
|
423
|
-
return /** @type {!Driver} */(
|
|
424
|
-
executor, caps, opt_flow,
|
|
423
|
+
return /** @type {!Driver} */(super.createSession(
|
|
424
|
+
executor, caps, opt_flow, () => service.kill()));
|
|
425
425
|
}
|
|
426
426
|
|
|
427
427
|
/**
|
package/index.js
CHANGED
|
@@ -131,7 +131,10 @@ const THENABLE_DRIVERS = new Map;
|
|
|
131
131
|
function createDriver(ctor, ...args) {
|
|
132
132
|
let thenableWebDriverProxy = THENABLE_DRIVERS.get(ctor);
|
|
133
133
|
if (!thenableWebDriverProxy) {
|
|
134
|
-
/**
|
|
134
|
+
/**
|
|
135
|
+
* @extends {WebDriver} // Needed since `ctor` is dynamically typed.
|
|
136
|
+
* @implements {ThenableWebDriver}
|
|
137
|
+
*/
|
|
135
138
|
thenableWebDriverProxy = class extends ctor {
|
|
136
139
|
/**
|
|
137
140
|
* @param {!IThenable<!Session>} session
|
package/io/index.js
CHANGED
|
@@ -298,3 +298,62 @@ exports.mkdir = function(aPath) {
|
|
|
298
298
|
});
|
|
299
299
|
});
|
|
300
300
|
};
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Recursively creates a directory and any ancestors that do not yet exist.
|
|
305
|
+
*
|
|
306
|
+
* @param {string} dir The directory path to create.
|
|
307
|
+
* @return {!Promise<string>} A promise that will resolve with the path of the
|
|
308
|
+
* created directory.
|
|
309
|
+
*/
|
|
310
|
+
exports.mkdirp = function mkdirp(dir) {
|
|
311
|
+
return checkedCall(callback => {
|
|
312
|
+
fs.mkdir(dir, undefined, err => {
|
|
313
|
+
if (!err) {
|
|
314
|
+
callback(null, dir);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
switch (err.code) {
|
|
319
|
+
case 'EEXIST':
|
|
320
|
+
callback(null, dir);
|
|
321
|
+
return;
|
|
322
|
+
case 'ENOENT':
|
|
323
|
+
return mkdirp(path.dirname(dir))
|
|
324
|
+
.then(() => mkdirp(dir))
|
|
325
|
+
.then(() => callback(null, dir), err => callback(err));
|
|
326
|
+
default:
|
|
327
|
+
callback(err);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Recursively walks a directory, returning a promise that will resolve with
|
|
337
|
+
* a list of all files/directories seen.
|
|
338
|
+
*
|
|
339
|
+
* @param {string} rootPath the directory to walk.
|
|
340
|
+
* @return {!Promise<!Array<{path: string, dir: boolean}>>} a promise that will
|
|
341
|
+
* resolve with a list of entries seen. For each entry, the recorded path
|
|
342
|
+
* will be relative to `rootPath`.
|
|
343
|
+
*/
|
|
344
|
+
exports.walkDir = function(rootPath) {
|
|
345
|
+
let seen = [];
|
|
346
|
+
return (function walk(dir) {
|
|
347
|
+
return checkedCall(callback => fs.readdir(dir, callback))
|
|
348
|
+
.then(files => Promise.all(files.map(file => {
|
|
349
|
+
file = path.join(dir, file);
|
|
350
|
+
return checkedCall(cb => fs.stat(file, cb)).then(stats => {
|
|
351
|
+
seen.push({
|
|
352
|
+
path: path.relative(rootPath, file),
|
|
353
|
+
dir: stats.isDirectory()
|
|
354
|
+
});
|
|
355
|
+
return stats.isDirectory() && walk(file);
|
|
356
|
+
});
|
|
357
|
+
})));
|
|
358
|
+
})(rootPath).then(() => seen);
|
|
359
|
+
};
|
package/io/zip.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
// Licensed to the Software Freedom Conservancy (SFC) under one
|
|
2
|
+
// or more contributor license agreements. See the NOTICE file
|
|
3
|
+
// distributed with this work for additional information
|
|
4
|
+
// regarding copyright ownership. The SFC licenses this file
|
|
5
|
+
// to you under the Apache License, Version 2.0 (the
|
|
6
|
+
// "License"); you may not use this file except in compliance
|
|
7
|
+
// with the License. You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing,
|
|
12
|
+
// software distributed under the License is distributed on an
|
|
13
|
+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
// KIND, either express or implied. See the License for the
|
|
15
|
+
// specific language governing permissions and limitations
|
|
16
|
+
// under the License.
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const jszip = require('jszip');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const io = require('./index');
|
|
24
|
+
const {InvalidArgumentError} = require('../lib/error');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Manages a zip archive.
|
|
28
|
+
*/
|
|
29
|
+
class Zip {
|
|
30
|
+
constructor() {
|
|
31
|
+
/** @private @const */
|
|
32
|
+
this.z_ = new jszip;
|
|
33
|
+
|
|
34
|
+
/** @private @const {!Set<!Promise<?>>} */
|
|
35
|
+
this.pendingAdds_ = new Set;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Adds a file to this zip.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} filePath path to the file to add.
|
|
42
|
+
* @param {string=} zipPath path to the file in the zip archive, defaults
|
|
43
|
+
* to the basename of `filePath`.
|
|
44
|
+
* @return {!Promise<?>} a promise that will resolve when added.
|
|
45
|
+
*/
|
|
46
|
+
addFile(filePath, zipPath = path.basename(filePath)) {
|
|
47
|
+
let add = io.read(filePath)
|
|
48
|
+
.then(buffer => this.z_.file(/** @type {string} */(zipPath), buffer));
|
|
49
|
+
this.pendingAdds_.add(add);
|
|
50
|
+
return add.then(
|
|
51
|
+
() => this.pendingAdds_.delete(add),
|
|
52
|
+
(e) => {
|
|
53
|
+
this.pendingAdds_.delete(add);
|
|
54
|
+
throw e;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Recursively adds a directory and all of its contents to this archive.
|
|
60
|
+
*
|
|
61
|
+
* @param {string} dirPath path to the directory to add.
|
|
62
|
+
* @param {string=} zipPath path to the folder in the archive to add the
|
|
63
|
+
* directory contents to. Defaults to the root folder.
|
|
64
|
+
* @return {!Promise<?>} returns a promise that will resolve when the
|
|
65
|
+
* the operation is complete.
|
|
66
|
+
*/
|
|
67
|
+
addDir(dirPath, zipPath = '') {
|
|
68
|
+
return io.walkDir(dirPath).then(entries => {
|
|
69
|
+
let archive = this.z_;
|
|
70
|
+
if (zipPath) {
|
|
71
|
+
archive = archive.folder(zipPath);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let files = [];
|
|
75
|
+
entries.forEach(spec => {
|
|
76
|
+
if (spec.dir) {
|
|
77
|
+
archive.folder(spec.path);
|
|
78
|
+
} else {
|
|
79
|
+
files.push(
|
|
80
|
+
this.addFile(
|
|
81
|
+
path.join(dirPath, spec.path),
|
|
82
|
+
path.join(zipPath, spec.path)));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return Promise.all(files);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {string} path File path to test for within the archive.
|
|
92
|
+
* @return {boolean} Whether this zip archive contains an entry with the given
|
|
93
|
+
* path.
|
|
94
|
+
*/
|
|
95
|
+
has(path) {
|
|
96
|
+
return this.z_.file(path) !== null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns the contents of the file in this zip archive with the given `path`.
|
|
101
|
+
* The returned promise will be rejected with an {@link InvalidArgumentError}
|
|
102
|
+
* if either `path` does not exist within the archive, or if `path` refers
|
|
103
|
+
* to a directory.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} path the path to the file whose contents to return.
|
|
106
|
+
* @return {!Promise<!Buffer>} a promise that will be resolved with the file's
|
|
107
|
+
* contents as a buffer.
|
|
108
|
+
*/
|
|
109
|
+
getFile(path) {
|
|
110
|
+
let file = this.z_.file(path);
|
|
111
|
+
if (!file) {
|
|
112
|
+
return Promise.reject(
|
|
113
|
+
new InvalidArgumentError(`No such file in zip archive: ${path}`));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (file.dir) {
|
|
117
|
+
return Promise.reject(
|
|
118
|
+
new InvalidArgumentError(
|
|
119
|
+
`The requested file is a directory: ${path}`));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return Promise.resolve(file.async('nodebuffer'));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Returns the compressed data for this archive in a buffer. _This method will
|
|
127
|
+
* not wait for any outstanding {@link #addFile add}
|
|
128
|
+
* {@link #addDir operations} before encoding the archive._
|
|
129
|
+
*
|
|
130
|
+
* @param {string} compression The desired compression.
|
|
131
|
+
* Must be `STORE` (the default) or `DEFLATE`.
|
|
132
|
+
* @return {!Promise<!Buffer>} a promise that will resolve with this archive
|
|
133
|
+
* as a buffer.
|
|
134
|
+
*/
|
|
135
|
+
toBuffer(compression = 'STORE') {
|
|
136
|
+
if (compression !== 'STORE' && compression !== 'DEFLATE') {
|
|
137
|
+
return Promise.reject(
|
|
138
|
+
new InvalidArgumentError(
|
|
139
|
+
`compression must be one of {STORE, DEFLATE}, got ${compression}`));
|
|
140
|
+
}
|
|
141
|
+
return Promise.resolve(
|
|
142
|
+
this.z_.generateAsync({compression, type: 'nodebuffer'}));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Asynchronously opens a zip archive.
|
|
149
|
+
*
|
|
150
|
+
* @param {string} path to the zip archive to load.
|
|
151
|
+
* @return {!Promise<!Zip>} a promise that will resolve with the opened
|
|
152
|
+
* archive.
|
|
153
|
+
*/
|
|
154
|
+
function load(path) {
|
|
155
|
+
return io.read(path).then(data => {
|
|
156
|
+
let zip = new Zip;
|
|
157
|
+
return zip.z_.loadAsync(data).then(() => zip);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Asynchronously unzips an archive file.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} src path to the source file to unzip.
|
|
166
|
+
* @param {string} dst path to the destination directory.
|
|
167
|
+
* @return {!Promise<string>} a promise that will resolve with `dst` once the
|
|
168
|
+
* archive has been unzipped.
|
|
169
|
+
*/
|
|
170
|
+
function unzip(src, dst) {
|
|
171
|
+
return load(src).then(zip => {
|
|
172
|
+
let promisedDirs = new Map;
|
|
173
|
+
let promises = [];
|
|
174
|
+
|
|
175
|
+
zip.z_.forEach((relPath, file) => {
|
|
176
|
+
let p;
|
|
177
|
+
if (file.dir) {
|
|
178
|
+
p = createDir(relPath);
|
|
179
|
+
} else {
|
|
180
|
+
let dirname = path.dirname(relPath);
|
|
181
|
+
if (dirname === '.') {
|
|
182
|
+
p = writeFile(relPath, file);
|
|
183
|
+
} else {
|
|
184
|
+
p = createDir(dirname).then(() => writeFile(relPath, file));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
promises.push(p);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return Promise.all(promises).then(() => dst);
|
|
191
|
+
|
|
192
|
+
function createDir(dir) {
|
|
193
|
+
let p = promisedDirs.get(dir);
|
|
194
|
+
if (!p) {
|
|
195
|
+
p = io.mkdirp(path.join(dst, dir));
|
|
196
|
+
promisedDirs.set(dir, p);
|
|
197
|
+
}
|
|
198
|
+
return p;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function writeFile(relPath, file) {
|
|
202
|
+
return file.async('nodebuffer')
|
|
203
|
+
.then(buffer => io.write(path.join(dst, relPath), buffer));
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
// PUBLIC API
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
exports.Zip = Zip;
|
|
213
|
+
exports.load = load;
|
|
214
|
+
exports.unzip = unzip;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// GENERATED CODE - DO NOT EDIT
|
|
2
|
+
module.exports = function(){return function(){var d=this;function f(a){return"string"==typeof a};function h(a,b){this.code=a;this.a=l[a]||m;this.message=b||"";a=this.a.replace(/((?:^|\s+)[a-z])/g,function(a){return a.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||""}
|
|
3
|
+
(function(){var a=Error;function b(){}b.prototype=a.prototype;h.b=a.prototype;h.prototype=new b;h.prototype.constructor=h;h.a=function(b,c,g){for(var e=Array(arguments.length-2),k=2;k<arguments.length;k++)e[k-2]=arguments[k];return a.prototype[c].apply(b,e)}})();var m="unknown error",l={15:"element not selectable",11:"element not visible"};l[31]=m;l[30]=m;l[24]="invalid cookie domain";l[29]="invalid element coordinates";l[12]="invalid element state";l[32]="invalid selector";l[51]="invalid selector";
|
|
4
|
+
l[52]="invalid selector";l[17]="javascript error";l[405]="unsupported operation";l[34]="move target out of bounds";l[27]="no such alert";l[7]="no such element";l[8]="no such frame";l[23]="no such window";l[28]="script timeout";l[33]="session not created";l[10]="stale element reference";l[21]="timeout";l[25]="unable to set cookie";l[26]="unexpected alert open";l[13]=m;l[9]="unknown command";h.prototype.toString=function(){return this.name+": "+this.message};var n;a:{var p=d.navigator;if(p){var q=p.userAgent;if(q){n=q;break a}}n=""}function r(a){return-1!=n.indexOf(a)};function t(a,b){for(var e=a.length,c=f(a)?a.split(""):a,g=0;g<e;g++)g in c&&b.call(void 0,c[g],g,a)};function v(){return r("iPhone")&&!r("iPod")&&!r("iPad")};function w(){return(r("Chrome")||r("CriOS"))&&!r("Edge")};var x=r("Opera"),y=r("Trident")||r("MSIE"),z=r("Edge"),A=r("Gecko")&&!(-1!=n.toLowerCase().indexOf("webkit")&&!r("Edge"))&&!(r("Trident")||r("MSIE"))&&!r("Edge"),aa=-1!=n.toLowerCase().indexOf("webkit")&&!r("Edge");function B(){var a=d.document;return a?a.documentMode:void 0}var C;
|
|
5
|
+
a:{var D="",E=function(){var a=n;if(A)return/rv\:([^\);]+)(\)|;)/.exec(a);if(z)return/Edge\/([\d\.]+)/.exec(a);if(y)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(aa)return/WebKit\/(\S+)/.exec(a);if(x)return/(?:Version)[ \/]?(\S+)/.exec(a)}();E&&(D=E?E[1]:"");if(y){var F=B();if(null!=F&&F>parseFloat(D)){C=String(F);break a}}C=D}var G;var H=d.document;G=H&&y?B()||("CSS1Compat"==H.compatMode?parseInt(C,10):5):void 0;var ba=r("Firefox"),ca=v()||r("iPod"),da=r("iPad"),I=r("Android")&&!(w()||r("Firefox")||r("Opera")||r("Silk")),ea=w(),J=r("Safari")&&!(w()||r("Coast")||r("Opera")||r("Edge")||r("Silk")||r("Android"))&&!(v()||r("iPad")||r("iPod"));function K(a){return(a=a.exec(n))?a[1]:""}(function(){if(ba)return K(/Firefox\/([0-9.]+)/);if(y||z||x)return C;if(ea)return v()||r("iPad")||r("iPod")?K(/CriOS\/([0-9.]+)/):K(/Chrome\/([0-9.]+)/);if(J&&!(v()||r("iPad")||r("iPod")))return K(/Version\/([0-9.]+)/);if(ca||da){var a=/Version\/(\S+).*Mobile\/(\S+)/.exec(n);if(a)return a[1]+"."+a[2]}else if(I)return(a=K(/Android\s+([0-9.]+)/))?a:K(/Version\/([0-9.]+)/);return""})();var L,M=function(){if(!A)return!1;var a=d.Components;if(!a)return!1;try{if(!a.classes)return!1}catch(g){return!1}var b=a.classes,a=a.interfaces,e=b["@mozilla.org/xpcom/version-comparator;1"].getService(a.nsIVersionComparator),c=b["@mozilla.org/xre/app-info;1"].getService(a.nsIXULAppInfo).version;L=function(a){e.compare(c,""+a)};return!0}(),N=y&&!(8<=Number(G)),fa=y&&!(9<=Number(G));I&&M&&L(2.3);I&&M&&L(4);J&&M&&L(6);var ga={SCRIPT:1,STYLE:1,HEAD:1,IFRAME:1,OBJECT:1},O={IMG:" ",BR:"\n"};function P(a,b,e){if(!(a.nodeName in ga))if(3==a.nodeType)e?b.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):b.push(a.nodeValue);else if(a.nodeName in O)b.push(O[a.nodeName]);else for(a=a.firstChild;a;)P(a,b,e),a=a.nextSibling};function Q(a,b){b=b.toLowerCase();return"style"==b?ha(a.style.cssText):N&&"value"==b&&R(a,"INPUT")?a.value:fa&&!0===a[b]?String(a.getAttribute(b)):(a=a.getAttributeNode(b))&&a.specified?a.value:null}var ia=/[;]+(?=(?:(?:[^"]*"){2})*[^"]*$)(?=(?:(?:[^']*'){2})*[^']*$)(?=(?:[^()]*\([^()]*\))*[^()]*$)/;
|
|
6
|
+
function ha(a){var b=[];t(a.split(ia),function(a){var c=a.indexOf(":");0<c&&(a=[a.slice(0,c),a.slice(c+1)],2==a.length&&b.push(a[0].toLowerCase(),":",a[1],";"))});b=b.join("");return b=";"==b.charAt(b.length-1)?b:b+";"}function S(a,b){N&&"value"==b&&R(a,"OPTION")&&null===Q(a,"value")?(b=[],P(a,b,!1),a=b.join("")):a=a[b];return a}function R(a,b){b&&"string"!==typeof b&&(b=b.toString());return!!a&&1==a.nodeType&&(!b||a.tagName.toUpperCase()==b)}
|
|
7
|
+
function T(a){return R(a,"OPTION")?!0:R(a,"INPUT")?(a=a.type.toLowerCase(),"checkbox"==a||"radio"==a):!1};var ja={"class":"className",readonly:"readOnly"},U="async autofocus autoplay checked compact complete controls declare defaultchecked defaultselected defer disabled draggable ended formnovalidate hidden indeterminate iscontenteditable ismap itemscope loop multiple muted nohref noresize noshade novalidate nowrap open paused pubdate readonly required reversed scoped seamless seeking selected spellcheck truespeed willvalidate".split(" ");function V(a,b){var e=null,c=b.toLowerCase();if("style"==c)return(e=a.style)&&!f(e)&&(e=e.cssText),e;if(("selected"==c||"checked"==c)&&T(a)){if(!T(a))throw new h(15,"Element is not selectable");b="selected";e=a.type&&a.type.toLowerCase();if("checkbox"==e||"radio"==e)b="checked";return S(a,b)?"true":null}var g=R(a,"A");if(R(a,"IMG")&&"src"==c||g&&"href"==c)return(e=Q(a,c))&&(e=S(a,c)),e;if("spellcheck"==c){e=Q(a,c);if(null!==e){if("false"==e.toLowerCase())return"false";if("true"==e.toLowerCase())return"true"}return S(a,
|
|
8
|
+
c)+""}g=ja[b]||b;a:if(f(U))c=f(c)&&1==c.length?U.indexOf(c,0):-1;else{for(var u=0;u<U.length;u++)if(u in U&&U[u]===c){c=u;break a}c=-1}if(0<=c)return(e=null!==Q(a,b)||S(a,g))?"true":null;try{var k=S(a,g)}catch(ka){}(c=null==k)||(c=typeof k,c="object"==c&&null!=k||"function"==c);c?e=Q(a,b):e=k;return null!=e?e.toString():null}var W=["_"],X=d;W[0]in X||!X.execScript||X.execScript("var "+W[0]);
|
|
9
|
+
for(var Y;W.length&&(Y=W.shift());){var Z;if(Z=!W.length)Z=void 0!==V;Z?X[Y]=V:X[Y]&&X[Y]!==Object.prototype[Y]?X=X[Y]:X=X[Y]={}};; return this._.apply(null,arguments);}.apply({navigator:typeof window!='undefined'?window.navigator:null,document:typeof window!='undefined'?window.document:null}, arguments);};
|