@appium/support 2.54.2 → 2.55.3

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 (43) hide show
  1. package/build/lib/fs.js +1 -1
  2. package/build/lib/image-util.js +17 -10
  3. package/build/lib/index.js +98 -0
  4. package/build/lib/log-internal.js +1 -1
  5. package/build/lib/logging.js +4 -3
  6. package/build/lib/net.js +6 -4
  7. package/build/lib/plist.js +4 -4
  8. package/build/lib/system.js +3 -3
  9. package/build/lib/tempdir.js +1 -1
  10. package/build/lib/timing.js +1 -1
  11. package/build/lib/util.js +17 -17
  12. package/build/lib/zip.js +25 -15
  13. package/build/test/assets/sample_binary.plist +0 -0
  14. package/build/test/assets/sample_text.plist +28 -0
  15. package/build/test/fs-specs.js +264 -0
  16. package/build/test/helpers.js +35 -0
  17. package/build/test/image-util-e2e-specs.js +227 -0
  18. package/build/test/index-specs.js +49 -0
  19. package/build/test/log-internals-specs.js +97 -0
  20. package/build/test/logger/helpers.js +71 -0
  21. package/build/test/logger/logger-force-specs.js +41 -0
  22. package/build/test/logger/logger-normal-specs.js +113 -0
  23. package/build/test/logger/logger-test-specs.js +40 -0
  24. package/build/test/mjpeg-e2e-specs.js +96 -0
  25. package/build/test/net-e2e-specs.js +32 -0
  26. package/build/test/node-e2e-specs.js +22 -0
  27. package/build/test/plist-specs.js +54 -0
  28. package/build/test/process-specs.js +104 -0
  29. package/build/test/system-specs.js +136 -0
  30. package/build/test/tempdir-specs.js +86 -0
  31. package/build/test/timing-specs.js +125 -0
  32. package/build/test/util-e2e-specs.js +136 -0
  33. package/build/test/util-specs.js +537 -0
  34. package/build/test/zip-e2e-specs.js +233 -0
  35. package/index.js +1 -28
  36. package/lib/image-util.js +15 -7
  37. package/lib/index.js +28 -0
  38. package/lib/log-internal.js +1 -1
  39. package/lib/logging.js +1 -1
  40. package/lib/net.js +5 -4
  41. package/lib/zip.js +45 -31
  42. package/package.json +9 -15
  43. package/build/index.js +0 -98
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ require("source-map-support/register");
6
+
7
+ var _path = _interopRequireDefault(require("path"));
8
+
9
+ var zip = _interopRequireWildcard(require("../lib/zip"));
10
+
11
+ var _index = require("../lib/index");
12
+
13
+ var _helpers = require("./helpers");
14
+
15
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
16
+
17
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
18
+
19
+ describe('#zip', function () {
20
+ const optionMap = new Map([['native JS unzip', {}], ['system unzip', {
21
+ useSystemUnzip: true
22
+ }]]);
23
+ optionMap.forEach((options, desc) => {
24
+ describe(desc, function () {
25
+ let assetsPath;
26
+ let zippedFilePath;
27
+ let tmpRoot;
28
+ beforeEach(async function () {
29
+ assetsPath = await _index.tempDir.openDir();
30
+ tmpRoot = await _index.tempDir.openDir();
31
+ const zippedBase64 = 'UEsDBAoAAAAAALlzk0oAAAAAAAAAAAAAAAAJABAAdW56aXBwZWQvVVgMANBO+VjO1vdY9QEUAFBLAwQKAAAAAADAc5NKAAAAAAAAAAAAAAAAEgAQAHVuemlwcGVkL3Rlc3QtZGlyL1VYDADQTvlY19b3WPUBFABQSwMEFAAIAAgAwnOTSgAAAAAAAAAAAAAAABcAEAB1bnppcHBlZC90ZXN0LWRpci9hLnR4dFVYDACDTvlY3Nb3WPUBFADzSM3JyVcIzy/KSQEAUEsHCFaxF0oNAAAACwAAAFBLAwQUAAgACADEc5NKAAAAAAAAAAAAAAAAFwAQAHVuemlwcGVkL3Rlc3QtZGlyL2IudHh0VVgMAINO+Vjf1vdY9QEUAHPLz1dwSiwCAFBLBwhIfrZJCQAAAAcAAABQSwECFQMKAAAAAAC5c5NKAAAAAAAAAAAAAAAACQAMAAAAAAAAAABA7UEAAAAAdW56aXBwZWQvVVgIANBO+VjO1vdYUEsBAhUDCgAAAAAAwHOTSgAAAAAAAAAAAAAAABIADAAAAAAAAAAAQO1BNwAAAHVuemlwcGVkL3Rlc3QtZGlyL1VYCADQTvlY19b3WFBLAQIVAxQACAAIAMJzk0pWsRdKDQAAAAsAAAAXAAwAAAAAAAAAAECkgXcAAAB1bnppcHBlZC90ZXN0LWRpci9hLnR4dFVYCACDTvlY3Nb3WFBLAQIVAxQACAAIAMRzk0pIfrZJCQAAAAcAAAAXAAwAAAAAAAAAAECkgdkAAAB1bnppcHBlZC90ZXN0LWRpci9iLnR4dFVYCACDTvlY39b3WFBLBQYAAAAABAAEADEBAAA3AQAAAAA=';
32
+ zippedFilePath = _path.default.resolve(tmpRoot, 'zipped.zip');
33
+ await _index.fs.writeFile(zippedFilePath, zippedBase64, 'base64');
34
+ await zip.extractAllTo(zippedFilePath, assetsPath, options);
35
+ });
36
+ afterEach(async function () {
37
+ for (const tmpPath of [assetsPath, tmpRoot]) {
38
+ if (!(await _index.fs.exists(tmpPath))) {
39
+ continue;
40
+ }
41
+
42
+ await _index.fs.rimraf(tmpPath);
43
+ }
44
+ });
45
+ describe('extractAllTo()', function () {
46
+ it('should extract contents of a .zip file to a directory', async function () {
47
+ await _index.fs.readFile(_path.default.resolve(assetsPath, 'unzipped', 'test-dir', 'a.txt'), {
48
+ encoding: 'utf8'
49
+ }).should.eventually.equal('Hello World');
50
+ await _index.fs.readFile(_path.default.resolve(assetsPath, 'unzipped', 'test-dir', 'b.txt'), {
51
+ encoding: 'utf8'
52
+ }).should.eventually.equal('Foo Bar');
53
+ });
54
+ });
55
+ describe('assertValidZip', function () {
56
+ it('should not throw an error if a valid ZIP file is passed', async function () {
57
+ await zip.assertValidZip(zippedFilePath).should.eventually.be.fulfilled;
58
+ });
59
+ it('should throw an error if the file does not exist', async function () {
60
+ await zip.assertValidZip('blabla').should.eventually.be.rejected;
61
+ });
62
+ it('should throw an error if the file is invalid', async function () {
63
+ await zip.assertValidZip(_path.default.resolve(assetsPath, 'unzipped', 'test-dir', 'a.txt')).should.eventually.be.rejected;
64
+ });
65
+ });
66
+ describe('readEntries()', function () {
67
+ const expectedEntries = [{
68
+ name: 'unzipped/'
69
+ }, {
70
+ name: 'unzipped/test-dir/'
71
+ }, {
72
+ name: 'unzipped/test-dir/a.txt',
73
+ contents: 'Hello World'
74
+ }, {
75
+ name: 'unzipped/test-dir/b.txt',
76
+ contents: 'Foo Bar'
77
+ }];
78
+ it('should iterate entries (directories and files) of zip file', async function () {
79
+ let i = 0;
80
+ await zip.readEntries(zippedFilePath, async ({
81
+ entry,
82
+ extractEntryTo
83
+ }) => {
84
+ entry.fileName.should.equal(expectedEntries[i].name);
85
+
86
+ if (expectedEntries[i].contents) {
87
+ await extractEntryTo(tmpRoot);
88
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, entry.fileName), {
89
+ flags: 'r',
90
+ encoding: 'utf8'
91
+ }).should.eventually.equal(expectedEntries[i].contents);
92
+ }
93
+
94
+ i++;
95
+ });
96
+ });
97
+ it('should stop iterating zipFile if onEntry callback returns false', async function () {
98
+ let i = 0;
99
+ await zip.readEntries(zippedFilePath, async () => {
100
+ i++;
101
+ return false;
102
+ });
103
+ i.should.equal(1);
104
+ });
105
+ it('should be rejected if it uses a non-zip file', async function () {
106
+ let promise = zip.readEntries(_path.default.resolve(assetsPath, 'unzipped', 'test-dir', 'a.txt'), async () => {});
107
+ await promise.should.eventually.be.rejected;
108
+ });
109
+ });
110
+ describe('toInMemoryZip()', function () {
111
+ it('should convert a local file to an in-memory zip buffer', async function () {
112
+ const testFolder = _path.default.resolve(assetsPath, 'unzipped');
113
+
114
+ const buffer = await zip.toInMemoryZip(testFolder);
115
+ Buffer.isBuffer(buffer).should.be.true;
116
+ await _index.fs.writeFile(_path.default.resolve(tmpRoot, 'test.zip'), buffer);
117
+ await zip.extractAllTo(_path.default.resolve(tmpRoot, 'test.zip'), _path.default.resolve(tmpRoot, 'output'), {
118
+ fileNamesEncoding: 'utf8'
119
+ });
120
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, 'output', 'test-dir', 'a.txt'), {
121
+ encoding: 'utf8'
122
+ }).should.eventually.equal('Hello World');
123
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, 'output', 'test-dir', 'b.txt'), {
124
+ encoding: 'utf8'
125
+ }).should.eventually.equal('Foo Bar');
126
+ });
127
+ it('should convert a local folder to an in-memory base64-encoded zip buffer', async function () {
128
+ const testFolder = _path.default.resolve(assetsPath, 'unzipped');
129
+
130
+ const buffer = await zip.toInMemoryZip(testFolder, {
131
+ encodeToBase64: true
132
+ });
133
+ await _index.fs.writeFile(_path.default.resolve(tmpRoot, 'test.zip'), Buffer.from(buffer.toString(), 'base64'));
134
+ await zip.extractAllTo(_path.default.resolve(tmpRoot, 'test.zip'), _path.default.resolve(tmpRoot, 'output'));
135
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, 'output', 'test-dir', 'a.txt'), {
136
+ encoding: 'utf8'
137
+ }).should.eventually.equal('Hello World');
138
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, 'output', 'test-dir', 'b.txt'), {
139
+ encoding: 'utf8'
140
+ }).should.eventually.equal('Foo Bar');
141
+ });
142
+ it('should be rejected if use a bad path', async function () {
143
+ await zip.toInMemoryZip(_path.default.resolve(assetsPath, 'bad_path')).should.be.rejectedWith(/no such/i);
144
+ });
145
+ it('should be rejected if max size is exceeded', async function () {
146
+ const testFolder = _path.default.resolve(assetsPath, 'unzipped');
147
+
148
+ await zip.toInMemoryZip(testFolder, {
149
+ maxSize: 1
150
+ }).should.be.rejectedWith(/must not be greater/);
151
+ });
152
+ });
153
+ describe('_extractEntryTo()', function () {
154
+ let entry, mockZipFile, mockZipStream;
155
+ beforeEach(async function () {
156
+ entry = {
157
+ fileName: _path.default.resolve(await _index.tempDir.openDir(), 'temp', 'file')
158
+ };
159
+ mockZipStream = new _helpers.MockReadWriteStream();
160
+ mockZipFile = {
161
+ openReadStream: (entry, cb) => cb(null, mockZipStream)
162
+ };
163
+ });
164
+ it('should be rejected if zip stream emits an error', async function () {
165
+ mockZipStream.pipe = () => {
166
+ mockZipStream.emit('error', new Error('zip stream error'));
167
+ };
168
+
169
+ await zip._extractEntryTo(mockZipFile, entry).should.be.rejectedWith('zip stream error');
170
+ });
171
+ it('should be rejected if write stream emits an error', async function () {
172
+ mockZipStream.pipe = writeStream => {
173
+ writeStream.emit('error', new Error('write stream error'));
174
+ mockZipStream.end();
175
+ writeStream.end();
176
+ };
177
+
178
+ await zip._extractEntryTo(mockZipFile, entry).should.be.rejectedWith('write stream error');
179
+ });
180
+ });
181
+ describe('toArchive', function () {
182
+ it('should zip all files into an archive', async function () {
183
+ const testFolder = _path.default.resolve(assetsPath, 'unzipped');
184
+
185
+ const dstPath = _path.default.resolve(tmpRoot, 'test.zip');
186
+
187
+ await zip.toArchive(dstPath, {
188
+ cwd: testFolder
189
+ });
190
+ await zip.extractAllTo(dstPath, _path.default.resolve(tmpRoot, 'output'));
191
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, 'output', 'test-dir', 'a.txt'), {
192
+ encoding: 'utf8'
193
+ }).should.eventually.equal('Hello World');
194
+ await _index.fs.readFile(_path.default.resolve(tmpRoot, 'output', 'test-dir', 'b.txt'), {
195
+ encoding: 'utf8'
196
+ }).should.eventually.equal('Foo Bar');
197
+ });
198
+ });
199
+ });
200
+ });
201
+ describe('unicode filename handling', function () {
202
+ let zippedFilePath, assetsPath, tmpRoot;
203
+ beforeEach(async function () {
204
+ assetsPath = await _index.tempDir.openDir();
205
+ tmpRoot = await _index.tempDir.openDir();
206
+ const zippedBase64 = 'UEsDBBQACAAIABF8/EYAAAAAAAAAABoAAAATACAAa2Fuamkt5q2j5LiW5LiVLmFwcFVUDQAHAgO4VVpX+GBZV/hgdXgLAAEE9QEAAAQUAAAAK8nILFYAorz8EoWi1MScnEqFxDyFxIICLgBQSwcIR93jPhoAAAAaAAAAUEsBAhQDFAAIAAgAEXz8Rkfd4z4aAAAAGgAAABMAIAAAAAAAAAAAAKSBAAAAAGthbmppLeato+S4luS4lS5hcHBVVA0ABwIDuFVaV/hgWVf4YHV4CwABBPUBAAAEFAAAAFBLBQYAAAAAAQABAGEAAAB7AAAAAAA=';
207
+ zippedFilePath = _path.default.resolve(tmpRoot, 'zipped.zip');
208
+ await _index.fs.writeFile(zippedFilePath, zippedBase64, 'base64');
209
+ await zip.extractAllTo(zippedFilePath, assetsPath, {
210
+ useSystemUnzip: true
211
+ });
212
+ });
213
+ afterEach(async function () {
214
+ for (const tmpPath of [assetsPath, tmpRoot]) {
215
+ if (!(await _index.fs.exists(tmpPath))) {
216
+ continue;
217
+ }
218
+
219
+ await _index.fs.rimraf(tmpPath);
220
+ }
221
+ });
222
+ it('should retain the proper filenames', async function () {
223
+ const expectedPath = _path.default.join(assetsPath, 'kanji-正世丕.app');
224
+
225
+ if (!(await _index.fs.exists(expectedPath))) {
226
+ throw new chai.AssertionError(`Expected ${expectedPath} to exist, but it does not`);
227
+ }
228
+ });
229
+ });
230
+ });require('source-map-support').install();
231
+
232
+
233
+ //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInRlc3QvemlwLWUyZS1zcGVjcy5qcyJdLCJuYW1lcyI6WyJkZXNjcmliZSIsIm9wdGlvbk1hcCIsIk1hcCIsInVzZVN5c3RlbVVuemlwIiwiZm9yRWFjaCIsIm9wdGlvbnMiLCJkZXNjIiwiYXNzZXRzUGF0aCIsInppcHBlZEZpbGVQYXRoIiwidG1wUm9vdCIsImJlZm9yZUVhY2giLCJ0ZW1wRGlyIiwib3BlbkRpciIsInppcHBlZEJhc2U2NCIsInBhdGgiLCJyZXNvbHZlIiwiZnMiLCJ3cml0ZUZpbGUiLCJ6aXAiLCJleHRyYWN0QWxsVG8iLCJhZnRlckVhY2giLCJ0bXBQYXRoIiwiZXhpc3RzIiwicmltcmFmIiwiaXQiLCJyZWFkRmlsZSIsImVuY29kaW5nIiwic2hvdWxkIiwiZXZlbnR1YWxseSIsImVxdWFsIiwiYXNzZXJ0VmFsaWRaaXAiLCJiZSIsImZ1bGZpbGxlZCIsInJlamVjdGVkIiwiZXhwZWN0ZWRFbnRyaWVzIiwibmFtZSIsImNvbnRlbnRzIiwiaSIsInJlYWRFbnRyaWVzIiwiZW50cnkiLCJleHRyYWN0RW50cnlUbyIsImZpbGVOYW1lIiwiZmxhZ3MiLCJwcm9taXNlIiwidGVzdEZvbGRlciIsImJ1ZmZlciIsInRvSW5NZW1vcnlaaXAiLCJCdWZmZXIiLCJpc0J1ZmZlciIsInRydWUiLCJmaWxlTmFtZXNFbmNvZGluZyIsImVuY29kZVRvQmFzZTY0IiwiZnJvbSIsInRvU3RyaW5nIiwicmVqZWN0ZWRXaXRoIiwibWF4U2l6ZSIsIm1vY2taaXBGaWxlIiwibW9ja1ppcFN0cmVhbSIsIk1vY2tSZWFkV3JpdGVTdHJlYW0iLCJvcGVuUmVhZFN0cmVhbSIsImNiIiwicGlwZSIsImVtaXQiLCJFcnJvciIsIl9leHRyYWN0RW50cnlUbyIsIndyaXRlU3RyZWFtIiwiZW5kIiwiZHN0UGF0aCIsInRvQXJjaGl2ZSIsImN3ZCIsImV4cGVjdGVkUGF0aCIsImpvaW4iLCJjaGFpIiwiQXNzZXJ0aW9uRXJyb3IiXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFB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QUFTLENBQUMsa0JBQWtCO0FBQzFCLFdBQUssTUFBTUMsT0FBWCxJQUFzQixDQUFDZCxVQUFELEVBQWFFLE9BQWIsQ0FBdEIsRUFBNkM7QUFDM0MsWUFBSSxFQUFDLE1BQU1PLFVBQUdNLE1BQUgsQ0FBVUQsT0FBVixDQUFQLENBQUosRUFBK0I7QUFDN0I7QUFDRDs7QUFDRCxjQUFNTCxVQUFHTyxNQUFILENBQVVGLE9BQVYsQ0FBTjtBQUNEO0FBQ0YsS0FQUSxDQUFUO0FBU0FHLElBQUFBLEVBQUUsQ0FBQyxvQ0FBRCxFQUF1QyxrQkFBa0I7QUFDekQsWUFBTThDLFlBQVksR0FBR3hELGNBQUt5RCxJQUFMLENBQVVoRSxVQUFWLEVBQXNCLGVBQXRCLENBQXJCOztBQUVBLFVBQUksRUFBQyxNQUFNUyxVQUFHTSxNQUFILENBQVVnRCxZQUFWLENBQVAsQ0FBSixFQUFvQztBQUNsQyxjQUFNLElBQUlFLElBQUksQ0FBQ0MsY0FBVCxDQUF5QixZQUFXSCxZQUFhLDRCQUFqRCxDQUFOO0FBQ0Q7QUFDRixLQU5DLENBQUY7QUFPRCxHQTdCTyxDQUFSO0FBOEJELENBNU5PLENBQVIiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcbmltcG9ydCAqIGFzIHppcCBmcm9tICcuLi9saWIvemlwJztcbmltcG9ydCB7IHRlbXBEaXIsIGZzIH0gZnJvbSAnLi4vbGliL2luZGV4JztcbmltcG9ydCB7IE1vY2tSZWFkV3JpdGVTdHJlYW0gfSBmcm9tICcuL2hlbHBlcnMnO1xuXG5cbmRlc2NyaWJlKCcjemlwJywgZnVuY3Rpb24gKCkge1xuXG4gIGNvbnN0IG9wdGlvbk1hcCA9IG5ldyBNYXAoW1snbmF0aXZlIEpTIHVuemlwJywge31dLCBbJ3N5c3RlbSB1bnppcCcsIHt1c2VTeXN0ZW1VbnppcDogdHJ1ZX1dXSk7XG5cbiAgb3B0aW9uTWFwLmZvckVhY2goKG9wdGlvbnMsIGRlc2MpID0+IHtcbiAgICBkZXNjcmliZShkZXNjLCBmdW5jdGlvbiAoKSB7XG4gICAgICBsZXQgYXNzZXRzUGF0aDtcbiAgICAgIGxldCB6aXBwZWRGaWxlUGF0aDtcbiAgICAgIGxldCB0bXBSb290O1xuXG4gICAgICBiZWZvcmVFYWNoKGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgYXNzZXRzUGF0aCA9IGF3YWl0IHRlbXBEaXIub3BlbkRpcigpO1xuICAgICAgICB0bXBSb290ID0gYXdhaXQgdGVtcERpci5vcGVuRGlyKCk7XG4gICAgICAgIGNvbnN0IHppcHBlZEJhc2U2NCA9ICdVRXNEQkFvQUFBQUFBTGx6azBvQUFBQUFBQUFBQUFBQUFBQUpBQkFBZFc1NmFYQndaV1F2VlZnTUFOQk8rVmpPMXZkWTlRRVVBRkJMQXdRS0FBQUFBQURBYzVOS0FBQUFBQUFBQUFBQUFBQUFFZ0FRQUhWdWVtbHdjR1ZrTDNSbGMzUXRaR2x5TDFWWURBRFFUdmxZMTliM1dQVUJGQUJRU3dNRUZBQUlBQWdBd25PVFNnQUFBQUFBQUFBQUFBQUFBQmNBRUFCMWJucHBjSEJsWkM5MFpYTjBMV1JwY2k5aExuUjRkRlZZREFDRFR2bFkzTmIzV1BVQkZBRHpTTTNKeVZjSXp5L0tTUUVBVUVzSENGYXhGMG9OQUFBQUN3QUFBRkJMQXdRVUFBZ0FDQURFYzVOS0FBQUFBQUFBQUFBQUFBQUFGd0FRQUhWdWVtbHdjR1ZrTDNSbGMzUXRaR2x5TDJJdWRIaDBWVmdNQUlOTytWamYxdmRZOVFFVUFIUEx6MWR3U2l3Q0FGQkxCd2hJZnJaSkNRQUFBQWNBQUFCUVN3RUNGUU1LQUFBQUFBQzVjNU5LQUFBQUFBQUFBQUFBQUFBQUNRQU1BQUFBQUFBQUFBQkE3VUVBQUFBQWRXNTZhWEJ3WldRdlZWZ0lBTkJPK1ZqTzF2ZFlVRXNCQWhVRENnQUFBQUFBd0hPVFNnQUFBQUFBQUFBQUFBQUFBQklBREFBQUFBQUFBQUFBUU8xQk53QUFBSFZ1ZW1sd2NHVmtMM1JsYzNRdFpHbHlMMVZZQ0FEUVR2bFkxOWIzV0ZCTEFRSVZBeFFBQ0FBSUFNSnprMHBXc1JkS0RRQUFBQXNBQUFBWEFBd0FBQUFBQUFBQUFFQ2tnWGNBQUFCMWJucHBjSEJsWkM5MFpYTjBMV1JwY2k5aExuUjRkRlZZQ0FDRFR2bFkzTmIzV0ZCTEFRSVZBeFFBQ0FBSUFNUnprMHBJZnJaSkNRQUFBQWNBQUFBWEFBd0FBQUFBQUFBQUFFQ2tnZGtBQUFCMWJucHBjSEJsWkM5MFpYTjBMV1JwY2k5aUxuUjRkRlZZQ0FDRFR2bFkzOWIzV0ZCTEJRWUFBQUFBQkFBRUFERUJBQUEzQVFBQUFBQT0nO1xuICAgICAgICB6aXBwZWRGaWxlUGF0aCA9IHBhdGgucmVzb2x2ZSh0bXBSb290LCAnemlwcGVkLnppcCcpO1xuICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUoemlwcGVkRmlsZVBhdGgsIHppcHBlZEJhc2U2NCwgJ2Jhc2U2NCcpO1xuICAgICAgICBhd2FpdCB6aXAuZXh0cmFjdEFsbFRvKHppcHBlZEZpbGVQYXRoLCBhc3NldHNQYXRoLCBvcHRpb25zKTtcbiAgICAgIH0pO1xuXG4gICAgICBhZnRlckVhY2goYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICBmb3IgKGNvbnN0IHRtcFBhdGggb2YgW2Fzc2V0c1BhdGgsIHRtcFJvb3RdKSB7XG4gICAgICAgICAgaWYgKCFhd2FpdCBmcy5leGlzdHModG1wUGF0aCkpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cbiAgICAgICAgICBhd2FpdCBmcy5yaW1yYWYodG1wUGF0aCk7XG4gICAgICAgIH1cbiAgICAgIH0pO1xuXG4gICAgICBkZXNjcmliZSgnZXh0cmFjdEFsbFRvKCknLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGl0KCdzaG91bGQgZXh0cmFjdCBjb250ZW50cyBvZiBhIC56aXAgZmlsZSB0byBhIGRpcmVjdG9yeScsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBhd2FpdCBmcy5yZWFkRmlsZShwYXRoLnJlc29sdmUoYXNzZXRzUGF0aCwgJ3VuemlwcGVkJywgJ3Rlc3QtZGlyJywgJ2EudHh0JyksIHtlbmNvZGluZzogJ3V0ZjgnfSkuc2hvdWxkLmV2ZW50dWFsbHkuZXF1YWwoJ0hlbGxvIFdvcmxkJyk7XG4gICAgICAgICAgYXdhaXQgZnMucmVhZEZpbGUocGF0aC5yZXNvbHZlKGFzc2V0c1BhdGgsICd1bnppcHBlZCcsICd0ZXN0LWRpcicsICdiLnR4dCcpLCB7ZW5jb2Rpbmc6ICd1dGY4J30pLnNob3VsZC5ldmVudHVhbGx5LmVxdWFsKCdGb28gQmFyJyk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICAgIGRlc2NyaWJlKCdhc3NlcnRWYWxpZFppcCcsIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaXQoJ3Nob3VsZCBub3QgdGhyb3cgYW4gZXJyb3IgaWYgYSB2YWxpZCBaSVAgZmlsZSBpcyBwYXNzZWQnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgYXdhaXQgemlwLmFzc2VydFZhbGlkWmlwKHppcHBlZEZpbGVQYXRoKS5zaG91bGQuZXZlbnR1YWxseS5iZS5mdWxmaWxsZWQ7XG4gICAgICAgIH0pO1xuICAgICAgICBpdCgnc2hvdWxkIHRocm93IGFuIGVycm9yIGlmIHRoZSBmaWxlIGRvZXMgbm90IGV4aXN0JywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGF3YWl0IHppcC5hc3NlcnRWYWxpZFppcCgnYmxhYmxhJykuc2hvdWxkLmV2ZW50dWFsbHkuYmUucmVqZWN0ZWQ7XG4gICAgICAgIH0pO1xuICAgICAgICBpdCgnc2hvdWxkIHRocm93IGFuIGVycm9yIGlmIHRoZSBmaWxlIGlzIGludmFsaWQnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgYXdhaXQgemlwLmFzc2VydFZhbGlkWmlwKHBhdGgucmVzb2x2ZShhc3NldHNQYXRoLCAndW56aXBwZWQnLCAndGVzdC1kaXInLCAnYS50eHQnKSkuc2hvdWxkLmV2ZW50dWFsbHkuYmUucmVqZWN0ZWQ7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICAgIGRlc2NyaWJlKCdyZWFkRW50cmllcygpJywgZnVuY3Rpb24gKCkge1xuICAgICAgICBjb25zdCBleHBlY3RlZEVudHJpZXMgPSBbXG4gICAgICAgICAge25hbWU6ICd1bnppcHBlZC8nfSxcbiAgICAgICAgICB7bmFtZTogJ3VuemlwcGVkL3Rlc3QtZGlyLyd9LFxuICAgICAgICAgIHtuYW1lOiAndW56aXBwZWQvdGVzdC1kaXIvYS50eHQnLCBjb250ZW50czogJ0hlbGxvIFdvcmxkJ30sXG4gICAgICAgICAge25hbWU6ICd1bnppcHBlZC90ZXN0LWRpci9iLnR4dCcsIGNvbnRlbnRzOiAnRm9vIEJhcid9LFxuICAgICAgICBdO1xuXG4gICAgICAgIGl0KCdzaG91bGQgaXRlcmF0ZSBlbnRyaWVzIChkaXJlY3RvcmllcyBhbmQgZmlsZXMpIG9mIHppcCBmaWxlJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGxldCBpID0gMDtcbiAgICAgICAgICBhd2FpdCB6aXAucmVhZEVudHJpZXMoemlwcGVkRmlsZVBhdGgsIGFzeW5jICh7ZW50cnksIGV4dHJhY3RFbnRyeVRvfSkgPT4ge1xuICAgICAgICAgICAgZW50cnkuZmlsZU5hbWUuc2hvdWxkLmVxdWFsKGV4cGVjdGVkRW50cmllc1tpXS5uYW1lKTtcblxuICAgICAgICAgICAgLy8gSWYgaXQncyBhIGZpbGUsIHRlc3QgdGhhdCB3ZSBjYW4gZXh0cmFjdCBpdCB0byBhIHRlbXBvcmFyeSBkaXJlY3RvcnkgYW5kIHRoYXQgdGhlIGNvbnRlbnRzIGFyZSBjb3JyZWN0XG4gICAgICAgICAgICBpZiAoZXhwZWN0ZWRFbnRyaWVzW2ldLmNvbnRlbnRzKSB7XG4gICAgICAgICAgICAgIGF3YWl0IGV4dHJhY3RFbnRyeVRvKHRtcFJvb3QpO1xuICAgICAgICAgICAgICBhd2FpdCBmcy5yZWFkRmlsZShwYXRoLnJlc29sdmUodG1wUm9vdCwgZW50cnkuZmlsZU5hbWUpLCB7XG4gICAgICAgICAgICAgICAgZmxhZ3M6ICdyJyxcbiAgICAgICAgICAgICAgICBlbmNvZGluZzogJ3V0ZjgnXG4gICAgICAgICAgICAgIH0pLnNob3VsZC5ldmVudHVhbGx5LmVxdWFsKGV4cGVjdGVkRW50cmllc1tpXS5jb250ZW50cyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBpKys7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGl0KCdzaG91bGQgc3RvcCBpdGVyYXRpbmcgemlwRmlsZSBpZiBvbkVudHJ5IGNhbGxiYWNrIHJldHVybnMgZmFsc2UnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgbGV0IGkgPSAwO1xuICAgICAgICAgIGF3YWl0IHppcC5yZWFkRW50cmllcyh6aXBwZWRGaWxlUGF0aCwgYXN5bmMgKCkgPT4geyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIHJlcXVpcmUtYXdhaXRcbiAgICAgICAgICAgIGkrKztcbiAgICAgICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBpLnNob3VsZC5lcXVhbCgxKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoJ3Nob3VsZCBiZSByZWplY3RlZCBpZiBpdCB1c2VzIGEgbm9uLXppcCBmaWxlJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGxldCBwcm9taXNlID0gemlwLnJlYWRFbnRyaWVzKHBhdGgucmVzb2x2ZShhc3NldHNQYXRoLCAndW56aXBwZWQnLCAndGVzdC1kaXInLCAnYS50eHQnKSwgYXN5bmMgKCkgPT4ge30pO1xuICAgICAgICAgIGF3YWl0IHByb21pc2Uuc2hvdWxkLmV2ZW50dWFsbHkuYmUucmVqZWN0ZWQ7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICAgIGRlc2NyaWJlKCd0b0luTWVtb3J5WmlwKCknLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGl0KCdzaG91bGQgY29udmVydCBhIGxvY2FsIGZpbGUgdG8gYW4gaW4tbWVtb3J5IHppcCBidWZmZXInLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgLy8gQ29udmVydCBkaXJlY3RvcnkgdG8gaW4tbWVtb3J5IGJ1ZmZlclxuICAgICAgICAgIGNvbnN0IHRlc3RGb2xkZXIgPSBwYXRoLnJlc29sdmUoYXNzZXRzUGF0aCwgJ3VuemlwcGVkJyk7XG4gICAgICAgICAgY29uc3QgYnVmZmVyID0gYXdhaXQgemlwLnRvSW5NZW1vcnlaaXAodGVzdEZvbGRlcik7XG4gICAgICAgICAgQnVmZmVyLmlzQnVmZmVyKGJ1ZmZlcikuc2hvdWxkLmJlLnRydWU7XG5cbiAgICAgICAgICAvLyBXcml0ZSB0aGUgYnVmZmVyIHRvIGEgemlwIGZpbGVcbiAgICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUocGF0aC5yZXNvbHZlKHRtcFJvb3QsICd0ZXN0LnppcCcpLCBidWZmZXIpO1xuXG4gICAgICAgICAgLy8gVW56aXAgdGhlIGZpbGUgYW5kIHRlc3QgdGhhdCBpdCBoYXMgdGhlIHNhbWUgY29udGVudHMgYXMgdGhlIGRpcmVjdG9yeSB0aGF0IHdhcyB6aXBwZWRcbiAgICAgICAgICBhd2FpdCB6aXAuZXh0cmFjdEFsbFRvKHBhdGgucmVzb2x2ZSh0bXBSb290LCAndGVzdC56aXAnKSwgcGF0aC5yZXNvbHZlKHRtcFJvb3QsICdvdXRwdXQnKSwge1xuICAgICAgICAgICAgZmlsZU5hbWVzRW5jb2Rpbmc6ICd1dGY4J1xuICAgICAgICAgIH0pO1xuICAgICAgICAgIGF3YWl0IGZzLnJlYWRGaWxlKHBhdGgucmVzb2x2ZSh0bXBSb290LCAnb3V0cHV0JywgJ3Rlc3QtZGlyJywgJ2EudHh0JyksIHtcbiAgICAgICAgICAgIGVuY29kaW5nOiAndXRmOCdcbiAgICAgICAgICB9KS5zaG91bGQuZXZlbnR1YWxseS5lcXVhbCgnSGVsbG8gV29ybGQnKTtcbiAgICAgICAgICBhd2FpdCBmcy5yZWFkRmlsZShwYXRoLnJlc29sdmUodG1wUm9vdCwgJ291dHB1dCcsICd0ZXN0LWRpcicsICdiLnR4dCcpLCB7XG4gICAgICAgICAgICBlbmNvZGluZzogJ3V0ZjgnXG4gICAgICAgICAgfSkuc2hvdWxkLmV2ZW50dWFsbHkuZXF1YWwoJ0ZvbyBCYXInKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoJ3Nob3VsZCBjb252ZXJ0IGEgbG9jYWwgZm9sZGVyIHRvIGFuIGluLW1lbW9yeSBiYXNlNjQtZW5jb2RlZCB6aXAgYnVmZmVyJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGNvbnN0IHRlc3RGb2xkZXIgPSBwYXRoLnJlc29sdmUoYXNzZXRzUGF0aCwgJ3VuemlwcGVkJyk7XG4gICAgICAgICAgY29uc3QgYnVmZmVyID0gYXdhaXQgemlwLnRvSW5NZW1vcnlaaXAodGVzdEZvbGRlciwge1xuICAgICAgICAgICAgZW5jb2RlVG9CYXNlNjQ6IHRydWUsXG4gICAgICAgICAgfSk7XG5cbiAgICAgICAgICBhd2FpdCBmcy53cml0ZUZpbGUocGF0aC5yZXNvbHZlKHRtcFJvb3QsICd0ZXN0LnppcCcpLCBCdWZmZXIuZnJvbShidWZmZXIudG9TdHJpbmcoKSwgJ2Jhc2U2NCcpKTtcblxuICAgICAgICAgIC8vIFVuemlwIHRoZSBmaWxlIGFuZCB0ZXN0IHRoYXQgaXQgaGFzIHRoZSBzYW1lIGNvbnRlbnRzIGFzIHRoZSBkaXJlY3RvcnkgdGhhdCB3YXMgemlwcGVkXG4gICAgICAgICAgYXdhaXQgemlwLmV4dHJhY3RBbGxUbyhwYXRoLnJlc29sdmUodG1wUm9vdCwgJ3Rlc3QuemlwJyksIHBhdGgucmVzb2x2ZSh0bXBSb290LCAnb3V0cHV0JykpO1xuICAgICAgICAgIGF3YWl0IGZzLnJlYWRGaWxlKHBhdGgucmVzb2x2ZSh0bXBSb290LCAnb3V0cHV0JywgJ3Rlc3QtZGlyJywgJ2EudHh0JyksIHtcbiAgICAgICAgICAgIGVuY29kaW5nOiAndXRmOCdcbiAgICAgICAgICB9KS5zaG91bGQuZXZlbnR1YWxseS5lcXVhbCgnSGVsbG8gV29ybGQnKTtcbiAgICAgICAgICBhd2FpdCBmcy5yZWFkRmlsZShwYXRoLnJlc29sdmUodG1wUm9vdCwgJ291dHB1dCcsICd0ZXN0LWRpcicsICdiLnR4dCcpLCB7XG4gICAgICAgICAgICBlbmNvZGluZzogJ3V0ZjgnXG4gICAgICAgICAgfSkuc2hvdWxkLmV2ZW50dWFsbHkuZXF1YWwoJ0ZvbyBCYXInKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoJ3Nob3VsZCBiZSByZWplY3RlZCBpZiB1c2UgYSBiYWQgcGF0aCcsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBhd2FpdCB6aXAudG9Jbk1lbW9yeVppcChwYXRoLnJlc29sdmUoYXNzZXRzUGF0aCwgJ2JhZF9wYXRoJykpXG4gICAgICAgICAgICAuc2hvdWxkLmJlLnJlamVjdGVkV2l0aCgvbm8gc3VjaC9pKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoJ3Nob3VsZCBiZSByZWplY3RlZCBpZiBtYXggc2l6ZSBpcyBleGNlZWRlZCcsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBjb25zdCB0ZXN0Rm9sZGVyID0gcGF0aC5yZXNvbHZlKGFzc2V0c1BhdGgsICd1bnppcHBlZCcpO1xuICAgICAgICAgIGF3YWl0IHppcC50b0luTWVtb3J5WmlwKHRlc3RGb2xkZXIsIHtcbiAgICAgICAgICAgIG1heFNpemU6IDEsXG4gICAgICAgICAgfSkuc2hvdWxkLmJlLnJlamVjdGVkV2l0aCgvbXVzdCBub3QgYmUgZ3JlYXRlci8pO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuXG4gICAgICBkZXNjcmliZSgnX2V4dHJhY3RFbnRyeVRvKCknLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGxldCBlbnRyeSwgbW9ja1ppcEZpbGUsIG1vY2taaXBTdHJlYW07XG4gICAgICAgIGJlZm9yZUVhY2goYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICAgIGVudHJ5ID0ge2ZpbGVOYW1lOiBwYXRoLnJlc29sdmUoYXdhaXQgdGVtcERpci5vcGVuRGlyKCksICd0ZW1wJywgJ2ZpbGUnKX07XG4gICAgICAgICAgbW9ja1ppcFN0cmVhbSA9IG5ldyBNb2NrUmVhZFdyaXRlU3RyZWFtKCk7XG4gICAgICAgICAgbW9ja1ppcEZpbGUgPSB7XG4gICAgICAgICAgICBvcGVuUmVhZFN0cmVhbTogKGVudHJ5LCBjYikgPT4gY2IobnVsbCwgbW9ja1ppcFN0cmVhbSksIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgcHJvbWlzZS9wcmVmZXItYXdhaXQtdG8tY2FsbGJhY2tzXG4gICAgICAgICAgfTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgaXQoJ3Nob3VsZCBiZSByZWplY3RlZCBpZiB6aXAgc3RyZWFtIGVtaXRzIGFuIGVycm9yJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgICAgIG1vY2taaXBTdHJlYW0ucGlwZSA9ICgpID0+IHtcbiAgICAgICAgICAgIG1vY2taaXBTdHJlYW0uZW1pdCgnZXJyb3InLCBuZXcgRXJyb3IoJ3ppcCBzdHJlYW0gZXJyb3InKSk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBhd2FpdCB6aXAuX2V4dHJhY3RFbnRyeVRvKG1vY2taaXBGaWxlLCBlbnRyeSkuc2hvdWxkLmJlLnJlamVjdGVkV2l0aCgnemlwIHN0cmVhbSBlcnJvcicpO1xuICAgICAgICB9KTtcblxuICAgICAgICBpdCgnc2hvdWxkIGJlIHJlamVjdGVkIGlmIHdyaXRlIHN0cmVhbSBlbWl0cyBhbiBlcnJvcicsIGFzeW5jIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICBtb2NrWmlwU3RyZWFtLnBpcGUgPSAod3JpdGVTdHJlYW0pID0+IHtcbiAgICAgICAgICAgIHdyaXRlU3RyZWFtLmVtaXQoJ2Vycm9yJywgbmV3IEVycm9yKCd3cml0ZSBzdHJlYW0gZXJyb3InKSk7XG4gICAgICAgICAgICBtb2NrWmlwU3RyZWFtLmVuZCgpO1xuICAgICAgICAgICAgd3JpdGVTdHJlYW0uZW5kKCk7XG4gICAgICAgICAgfTtcbiAgICAgICAgICBhd2FpdCB6aXAuX2V4dHJhY3RFbnRyeVRvKG1vY2taaXBGaWxlLCBlbnRyeSkuc2hvdWxkLmJlLnJlamVjdGVkV2l0aCgnd3JpdGUgc3RyZWFtIGVycm9yJyk7XG4gICAgICAgIH0pO1xuICAgICAgfSk7XG5cbiAgICAgIGRlc2NyaWJlKCd0b0FyY2hpdmUnLCBmdW5jdGlvbiAoKSB7XG4gICAgICAgIGl0KCdzaG91bGQgemlwIGFsbCBmaWxlcyBpbnRvIGFuIGFyY2hpdmUnLCBhc3luYyBmdW5jdGlvbiAoKSB7XG4gICAgICAgICAgY29uc3QgdGVzdEZvbGRlciA9IHBhdGgucmVzb2x2ZShhc3NldHNQYXRoLCAndW56aXBwZWQnKTtcbiAgICAgICAgICBjb25zdCBkc3RQYXRoID0gcGF0aC5yZXNvbHZlKHRtcFJvb3QsICd0ZXN0LnppcCcpO1xuICAgICAgICAgIGF3YWl0IHppcC50b0FyY2hpdmUoZHN0UGF0aCwge1xuICAgICAgICAgICAgY3dkOiB0ZXN0Rm9sZGVyLFxuICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgLy8gVW56aXAgdGhlIGZpbGUgYW5kIHRlc3QgdGhhdCBpdCBoYXMgdGhlIHNhbWUgY29udGVudHMgYXMgdGhlIGRpcmVjdG9yeSB0aGF0IHdhcyB6aXBwZWRcbiAgICAgICAgICBhd2FpdCB6aXAuZXh0cmFjdEFsbFRvKGRzdFBhdGgsIHBhdGgucmVzb2x2ZSh0bXBSb290LCAnb3V0cHV0JykpO1xuICAgICAgICAgIGF3YWl0IGZzLnJlYWRGaWxlKHBhdGgucmVzb2x2ZSh0bXBSb290LCAnb3V0cHV0JywgJ3Rlc3QtZGlyJywgJ2EudHh0JyksIHtcbiAgICAgICAgICAgIGVuY29kaW5nOiAndXRmOCdcbiAgICAgICAgICB9KS5zaG91bGQuZXZlbnR1YWxseS5lcXVhbCgnSGVsbG8gV29ybGQnKTtcbiAgICAgICAgICBhd2FpdCBmcy5yZWFkRmlsZShwYXRoLnJlc29sdmUodG1wUm9vdCwgJ291dHB1dCcsICd0ZXN0LWRpcicsICdiLnR4dCcpLCB7XG4gICAgICAgICAgICBlbmNvZGluZzogJ3V0ZjgnXG4gICAgICAgICAgfSkuc2hvdWxkLmV2ZW50dWFsbHkuZXF1YWwoJ0ZvbyBCYXInKTtcbiAgICAgICAgfSk7XG4gICAgICB9KTtcblxuXG4gICAgfSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCd1bmljb2RlIGZpbGVuYW1lIGhhbmRsaW5nJywgZnVuY3Rpb24gKCkge1xuICAgIGxldCB6aXBwZWRGaWxlUGF0aCwgYXNzZXRzUGF0aCwgdG1wUm9vdDtcblxuICAgIGJlZm9yZUVhY2goYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgYXNzZXRzUGF0aCA9IGF3YWl0IHRlbXBEaXIub3BlbkRpcigpO1xuICAgICAgdG1wUm9vdCA9IGF3YWl0IHRlbXBEaXIub3BlbkRpcigpO1xuXG4gICAgICBjb25zdCB6aXBwZWRCYXNlNjQgPSAnVUVzREJCUUFDQUFJQUJGOC9FWUFBQUFBQUFBQUFCb0FBQUFUQUNBQWEyRnVhbWt0NXEyajVMaVc1TGlWTG1Gd2NGVlVEUUFIQWdPNFZWcFgrR0JaVi9oZ2RYZ0xBQUVFOVFFQUFBUVVBQUFBSzhuSUxGWUFvcno4RW9XaTFNU2NuRXFGeER5RnhJSUNMZ0JRU3djSVI5M2pQaG9BQUFBYUFBQUFVRXNCQWhRREZBQUlBQWdBRVh6OFJrZmQ0ejRhQUFBQUdnQUFBQk1BSUFBQUFBQUFBQUFBQUtTQkFBQUFBR3RoYm1wcExlYXRvK1M0bHVTNGxTNWhjSEJWVkEwQUJ3SUR1RlZhVi9oZ1dWZjRZSFY0Q3dBQkJQVUJBQUFFRkFBQUFGQkxCUVlBQUFBQUFRQUJBR0VBQUFCN0FBQUFBQUE9JztcbiAgICAgIHppcHBlZEZpbGVQYXRoID0gcGF0aC5yZXNvbHZlKHRtcFJvb3QsICd6aXBwZWQuemlwJyk7XG4gICAgICBhd2FpdCBmcy53cml0ZUZpbGUoemlwcGVkRmlsZVBhdGgsIHppcHBlZEJhc2U2NCwgJ2Jhc2U2NCcpO1xuICAgICAgYXdhaXQgemlwLmV4dHJhY3RBbGxUbyh6aXBwZWRGaWxlUGF0aCwgYXNzZXRzUGF0aCwge3VzZVN5c3RlbVVuemlwOiB0cnVlfSk7XG4gICAgfSk7XG5cbiAgICBhZnRlckVhY2goYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgZm9yIChjb25zdCB0bXBQYXRoIG9mIFthc3NldHNQYXRoLCB0bXBSb290XSkge1xuICAgICAgICBpZiAoIWF3YWl0IGZzLmV4aXN0cyh0bXBQYXRoKSkge1xuICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICB9XG4gICAgICAgIGF3YWl0IGZzLnJpbXJhZih0bXBQYXRoKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIGl0KCdzaG91bGQgcmV0YWluIHRoZSBwcm9wZXIgZmlsZW5hbWVzJywgYXN5bmMgZnVuY3Rpb24gKCkge1xuICAgICAgY29uc3QgZXhwZWN0ZWRQYXRoID0gcGF0aC5qb2luKGFzc2V0c1BhdGgsICdrYW5qaS3mraPkuJbkuJUuYXBwJyk7XG4gICAgICAvLyB3ZSBjYW5ub3QgdXNlIHRoZSBgc2hvdWxkYCBzeW50YXggYmVjYXVzZSBgZnMuZXhpc3RzYCByZXNvbHZlcyB0byBhIHByaW1pdGl2ZSAoYm9vbGVhbilcbiAgICAgIGlmICghYXdhaXQgZnMuZXhpc3RzKGV4cGVjdGVkUGF0aCkpIHtcbiAgICAgICAgdGhyb3cgbmV3IGNoYWkuQXNzZXJ0aW9uRXJyb3IoYEV4cGVjdGVkICR7ZXhwZWN0ZWRQYXRofSB0byBleGlzdCwgYnV0IGl0IGRvZXMgbm90YCk7XG4gICAgICB9XG4gICAgfSk7XG4gIH0pO1xufSk7XG4iXSwiZmlsZSI6InRlc3QvemlwLWUyZS1zcGVjcy5qcyIsInNvdXJjZVJvb3QiOiIuLi8uLiJ9
package/index.js CHANGED
@@ -1,28 +1 @@
1
- import * as tempDir from './lib/tempdir';
2
- import * as system from './lib/system';
3
- import * as util from './lib/util';
4
- import * as fsIndex from './lib/fs';
5
- import * as net from './lib/net';
6
- import * as plist from './lib/plist';
7
- import * as mkdirpIndex from './lib/mkdirp';
8
- import * as logger from './lib/logging';
9
- import * as process from './lib/process';
10
- import * as zip from './lib/zip';
11
- import * as imageUtil from './lib/image-util';
12
- import * as mjpeg from './lib/mjpeg';
13
- import * as node from './lib/node';
14
- import * as timing from './lib/timing';
15
-
16
-
17
- const { fs } = fsIndex;
18
- const { cancellableDelay } = util;
19
- const { mkdirp } = mkdirpIndex;
20
-
21
- export {
22
- tempDir, system, util, fs, cancellableDelay, plist, mkdirp, logger, process,
23
- zip, imageUtil, net, mjpeg, node, timing,
24
- };
25
- export default {
26
- tempDir, system, util, fs, cancellableDelay, plist, mkdirp, logger, process,
27
- zip, imageUtil, net, mjpeg, node, timing,
28
- };
1
+ module.exports = require('./build/lib');
package/lib/image-util.js CHANGED
@@ -8,6 +8,10 @@ import log from './logger';
8
8
  import { requirePackage } from './node';
9
9
 
10
10
 
11
+ const OCV_OLD = 'opencv4nodejs';
12
+ const OCV_NEW = '@u4/opencv4nodejs';
13
+
14
+
11
15
  const { MIME_JPEG, MIME_PNG, MIME_BMP } = Jimp;
12
16
  let cv = null;
13
17
 
@@ -127,16 +131,20 @@ async function initOpenCV () {
127
131
  }
128
132
 
129
133
  log.debug(`Initializing opencv`);
130
- try {
131
- cv = await requirePackage('opencv4nodejs');
132
- } catch (err) {
133
- log.warn(`Unable to load 'opencv4nodejs': ${err.message}`);
134
+ for (const ocvPackage of [OCV_OLD, OCV_NEW]) {
135
+ try {
136
+ log.debug(`Attempting to load '${ocvPackage}'`);
137
+ cv = await requirePackage(ocvPackage);
138
+ break;
139
+ } catch (err) {
140
+ log.warn(`Unable to load '${ocvPackage}': ${err.message}`);
141
+ }
134
142
  }
135
143
 
136
144
  if (!cv) {
137
- throw new Error(`'opencv4nodejs' module is required to use OpenCV features. ` +
138
- `Please install it first ('npm i -g opencv4nodejs') and restart Appium. ` +
139
- 'Read https://github.com/justadudewhohacks/opencv4nodejs#how-to-install for more details on this topic.');
145
+ throw new Error(`An opencv node module is required to use OpenCV features. ` +
146
+ `Please install one first (e.g., 'npm i -g ${OCV_NEW}') and restart Appium. ` +
147
+ 'Read https://github.com/UrielCh/opencv4nodejs#how-to-install for more details on this topic.');
140
148
  }
141
149
  }
142
150
 
package/lib/index.js ADDED
@@ -0,0 +1,28 @@
1
+ import * as tempDir from './tempdir';
2
+ import * as system from './system';
3
+ import * as util from './util';
4
+ import * as fsIndex from './fs';
5
+ import * as net from './net';
6
+ import * as plist from './plist';
7
+ import * as mkdirpIndex from './mkdirp';
8
+ import * as logger from './logging';
9
+ import * as process from './process';
10
+ import * as zip from './zip';
11
+ import * as imageUtil from './image-util';
12
+ import * as mjpeg from './mjpeg';
13
+ import * as node from './node';
14
+ import * as timing from './timing';
15
+
16
+
17
+ const { fs } = fsIndex;
18
+ const { cancellableDelay } = util;
19
+ const { mkdirp } = mkdirpIndex;
20
+
21
+ export {
22
+ tempDir, system, util, fs, cancellableDelay, plist, mkdirp, logger, process,
23
+ zip, imageUtil, net, mjpeg, node, timing,
24
+ };
25
+ export default {
26
+ tempDir, system, util, fs, cancellableDelay, plist, mkdirp, logger, process,
27
+ zip, imageUtil, net, mjpeg, node, timing,
28
+ };
@@ -103,7 +103,7 @@ class SecureValuesPreprocessor {
103
103
  /**
104
104
  * Loads rules from the given JSON file
105
105
  *
106
- * @param {string|Array<string|Rule>} source The full path to the JSON file containing secure
106
+ * @param {string|string[]|Rule[]>} source The full path to the JSON file containing secure
107
107
  * values replacement rules or the rules themselves represented as an array
108
108
  * @throws {Error} If the format of the source file is invalid or
109
109
  * it does not exist
package/lib/logging.js CHANGED
@@ -117,7 +117,7 @@ function getLogger (prefix = null) {
117
117
  * appear in Appium logs.
118
118
  * Each call to this method replaces the previously loaded rules if any existed.
119
119
  *
120
- * @param {string} rulesJsonPath The full path to the JSON file containing
120
+ * @param {string|string[]|Rule[]} rulesJsonPath The full path to the JSON file containing
121
121
  * the replacement rules. Each rule could either be a string to be replaced
122
122
  * or an object with predefined properties. See the `Rule` type definition in
123
123
  * `log-internals.js` to get more details on its format.
package/lib/net.js CHANGED
@@ -9,6 +9,7 @@ import Timer from './timing';
9
9
  import axios from 'axios';
10
10
  import FormData from 'form-data';
11
11
 
12
+ const DEFAULT_TIMEOUT_MS = 4 * 60 * 1000;
12
13
 
13
14
  function toAxiosAuth (auth) {
14
15
  if (!_.isPlainObject(auth)) {
@@ -25,7 +26,7 @@ function toAxiosAuth (auth) {
25
26
  async function uploadFileToHttp (localFileStream, parsedUri, uploadOptions = {}) {
26
27
  const {
27
28
  method = 'POST',
28
- timeout = 5000,
29
+ timeout = DEFAULT_TIMEOUT_MS,
29
30
  headers,
30
31
  auth,
31
32
  fileFieldName = 'file',
@@ -128,7 +129,7 @@ async function uploadFileToFtp (localFileStream, parsedUri, uploadOptions = {})
128
129
  * (e.g. timings and speed)
129
130
  * @property {string} method [POST] - The HTTP method used for file upload
130
131
  * @property {AuthCredentials} auth
131
- * @property {number} timeout [5000] - The actual request timeout in milliseconds
132
+ * @property {number} timeout [240000] - The actual request timeout in milliseconds
132
133
  * @property {Object} headers - Additional request headers mapping
133
134
  * @property {?string} fileFieldName [file] - The name of the form field containing the file
134
135
  * content to be uploaded. Any falsy value make the request to use non-multipart upload
@@ -186,7 +187,7 @@ async function uploadFile (localPath, remoteUri, uploadOptions = {}) {
186
187
  * @property {boolean} isMetered [true] - Whether to log the actual download performance
187
188
  * (e.g. timings and speed)
188
189
  * @property {AuthCredentials} auth
189
- * @property {number} timeout [5000] - The actual request timeout in milliseconds
190
+ * @property {number} timeout [240000] - The actual request timeout in milliseconds
190
191
  * @property {Object} headers - Request headers mapping
191
192
  */
192
193
 
@@ -202,7 +203,7 @@ async function downloadFile (remoteUrl, dstPath, downloadOptions = {}) {
202
203
  const {
203
204
  isMetered = true,
204
205
  auth,
205
- timeout = 5000,
206
+ timeout = DEFAULT_TIMEOUT_MS,
206
207
  headers,
207
208
  } = downloadOptions;
208
209
 
package/lib/zip.js CHANGED
@@ -7,6 +7,7 @@ import path from 'path';
7
7
  import { mkdirp } from '../lib/mkdirp';
8
8
  import stream from 'stream';
9
9
  import fs from './fs';
10
+ import { isWindows } from './system';
10
11
  import { Base64Encode } from 'base64-stream';
11
12
  import { toReadableSizeString, GiB } from './util';
12
13
  import Timer from './timing';
@@ -173,13 +174,7 @@ class ZipExtractor {
173
174
  * For ZIP archives created on MacOS it is usually expected to be `utf8`.
174
175
  * By default it is autodetected based on the entry metadata and is only needed to be set explicitly
175
176
  * if the particular archive does not comply to the standards, which leads to corrupted file names
176
- * after extraction.
177
- * @property {?number} defaultDirMode [0o755] The default permissions for extracted folders. It is only
178
- * applied when the extractor is unable to retrieve this value for a directory from the archive
179
- * metadata.
180
- * @property {?number} defaultFileMode [0o644] The default permissions for extracted files. It is only
181
- * applied when the extractor is unable to retrieve this value for a file from the archive
182
- * metadata.
177
+ * after extraction. Only applicable if system unzip binary is NOT being used.
183
178
  * @property {?boolean} useSystemUnzip [false] If true, attempt to use system unzip; if this fails,
184
179
  * fallback to the JS unzip implementation.
185
180
  */
@@ -214,22 +209,43 @@ async function extractAllTo (zipFilePath, destDir, opts = {}) {
214
209
  }
215
210
 
216
211
  /**
217
- * Executes system unzip (e.g., `/usr/bin/unzip`). If available, it is
212
+ * Executes system unzip (e.g., `/usr/bin/unzip`). If available, it is
218
213
  * significantly faster than the JS implementation.
219
- * This function will reject on win32.
214
+ * By default all files in the destDir get overridden if already exist.
215
+ *
220
216
  * @param {string} zipFilePath The full path to the source ZIP file
221
- * @param {string} destDir The full path to the destination folder
217
+ * @param {string} destDir The full path to the destination folder.
218
+ * This folder is expected to already exist before extracting the archive.
222
219
  */
223
220
  async function extractWithSystemUnzip (zipFilePath, destDir) {
224
- const unzipExecutablePath = await assertSystemUnzip();
225
- // -q means quiet (no stdout)
226
- // -o means overwrite
227
- // -d is the dest dir
228
- await exec(unzipExecutablePath, [
229
- '-q',
230
- '-o', zipFilePath,
231
- '-d', destDir
232
- ]);
221
+ const isWindowsHost = isWindows();
222
+ let executablePath;
223
+ try {
224
+ executablePath = await getExecutablePath(
225
+ isWindowsHost ? 'powershell.exe' : 'unzip'
226
+ );
227
+ } catch (e) {
228
+ throw new Error('Could not find system unzip');
229
+ }
230
+
231
+ if (isWindowsHost) {
232
+ // on Windows we use PowerShell to unzip files
233
+ await exec(executablePath, [
234
+ '-command', 'Expand-Archive',
235
+ '-LiteralPath', zipFilePath,
236
+ '-DestinationPath', destDir,
237
+ '-Force'
238
+ ]);
239
+ } else {
240
+ // -q means quiet (no stdout)
241
+ // -o means overwrite
242
+ // -d is the dest dir
243
+ await exec(executablePath, [
244
+ '-q',
245
+ '-o', zipFilePath,
246
+ '-d', destDir
247
+ ]);
248
+ }
233
249
  }
234
250
 
235
251
  /**
@@ -512,21 +528,19 @@ async function toArchive (dstPath, src = {}, opts = {}) {
512
528
  }
513
529
 
514
530
  /**
515
- * Finds a system `unzip` executable. Rejects if it is not found.
531
+ * Finds and memoizes the full path to the given executable.
532
+ * Rejects if it is not found.
516
533
  */
517
- const assertSystemUnzip = _.memoize(
534
+ const getExecutablePath = _.memoize(
518
535
  /**
519
- * @returns {B<string>} Path to unzip executable
536
+ * @returns {B<string>} Full Path to the executable
520
537
  */
521
- async () => {
522
- try {
523
- const unzipPath = await fs.which('unzip');
524
- log.debug('found system unzip at %s', unzipPath);
525
- return unzipPath;
526
- } catch {
527
- throw new Error('Could not find system unzip');
528
- }
529
- });
538
+ async function getExecutablePath (binaryName) {
539
+ const fullPath = await fs.which(binaryName);
540
+ log.debug(`Found '%s' at '%s'`, binaryName, fullPath);
541
+ return fullPath;
542
+ }
543
+ );
530
544
 
531
545
  export { extractAllTo, readEntries, toInMemoryZip, _extractEntryTo,
532
546
  assertValidZip, toArchive };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appium/support",
3
- "version": "2.54.2",
3
+ "version": "2.55.3",
4
4
  "description": "Support libs used across appium packages",
5
5
  "keywords": [
6
6
  "automation",
@@ -21,29 +21,26 @@
21
21
  },
22
22
  "license": "Apache-2.0",
23
23
  "author": "https://github.com/appium",
24
- "main": "./build/index.js",
25
- "bin": {},
26
24
  "directories": {
27
25
  "lib": "lib"
28
26
  },
29
27
  "files": [
30
28
  "index.js",
31
29
  "lib",
32
- "build/index.js",
33
- "build/lib"
30
+ "build"
34
31
  ],
35
32
  "dependencies": {
36
- "@babel/runtime": "7.15.4",
33
+ "@babel/runtime": "7.16.3",
37
34
  "archiver": "5.3.0",
38
- "axios": "0.21.4",
35
+ "axios": "0.24.0",
39
36
  "base64-stream": "1.0.0",
40
37
  "bluebird": "3.7.2",
41
38
  "bplist-creator": "0.1.0",
42
- "bplist-parser": "0.3.0",
39
+ "bplist-parser": "0.3.1",
43
40
  "find-root": "1.1.0",
44
41
  "form-data": "4.0.0",
45
42
  "get-stream": "6.0.1",
46
- "glob": "7.1.7",
43
+ "glob": "7.2.0",
47
44
  "jimp": "0.16.1",
48
45
  "jsftp": "2.1.3",
49
46
  "klaw": "3.0.0",
@@ -60,16 +57,13 @@
60
57
  "rimraf": "3.0.2",
61
58
  "sanitize-filename": "1.6.3",
62
59
  "semver": "7.3.5",
63
- "shell-quote": "1.7.2",
64
- "source-map-support": "0.5.20",
60
+ "shell-quote": "1.7.3",
61
+ "source-map-support": "0.5.21",
65
62
  "teen_process": "1.16.0",
66
63
  "uuid": "8.3.2",
67
64
  "which": "2.0.2",
68
65
  "yauzl": "2.10.0"
69
66
  },
70
- "devDependencies": {
71
- "@appium/gulp-plugins": "^5.5.3"
72
- },
73
67
  "engines": {
74
68
  "node": ">=12",
75
69
  "npm": ">=6"
@@ -78,5 +72,5 @@
78
72
  "access": "public"
79
73
  },
80
74
  "homepage": "https://appium.io",
81
- "gitHead": "4ec2d1edd7f93685d99fb3186b2ab7b42c591eba"
75
+ "gitHead": "ca90a11813546ab4851e5b1f0406f420a53227e6"
82
76
  }