appium-xcuitest-driver 10.14.13 → 10.16.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/CHANGELOG.md +12 -0
- package/build/lib/commands/certificate.d.ts.map +1 -1
- package/build/lib/commands/certificate.js +33 -22
- package/build/lib/commands/certificate.js.map +1 -1
- package/build/lib/commands/file-movement.d.ts.map +1 -1
- package/build/lib/commands/file-movement.js +73 -45
- package/build/lib/commands/file-movement.js.map +1 -1
- package/build/lib/device/afc-client.d.ts +135 -0
- package/build/lib/device/afc-client.d.ts.map +1 -0
- package/build/lib/device/afc-client.js +422 -0
- package/build/lib/device/afc-client.js.map +1 -0
- package/build/lib/device/certificate-client.d.ts +73 -0
- package/build/lib/device/certificate-client.d.ts.map +1 -0
- package/build/lib/device/certificate-client.js +163 -0
- package/build/lib/device/certificate-client.js.map +1 -0
- package/build/lib/device/real-device-management.d.ts +10 -12
- package/build/lib/device/real-device-management.d.ts.map +1 -1
- package/build/lib/device/real-device-management.js +47 -160
- package/build/lib/device/real-device-management.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/commands/certificate.ts +46 -27
- package/lib/commands/file-movement.ts +100 -56
- package/lib/device/afc-client.ts +503 -0
- package/lib/device/certificate-client.ts +195 -0
- package/lib/device/real-device-management.ts +70 -164
- package/npm-shrinkwrap.json +5 -5
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import B, {TimeoutError} from 'bluebird';
|
|
3
|
-
import {fs, tempDir,
|
|
3
|
+
import {fs, tempDir, zip, util, timing} from 'appium/support';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import {services, utilities, INSTRUMENT_CHANNEL} from 'appium-ios-device';
|
|
6
6
|
import {buildSafariPreferences, SAFARI_BUNDLE_ID} from '../app-utils';
|
|
@@ -8,12 +8,13 @@ import {log as defaultLogger} from '../logger';
|
|
|
8
8
|
import { Devicectl } from 'node-devicectl';
|
|
9
9
|
import type { AppiumLogger } from '@appium/types';
|
|
10
10
|
import type { XCUITestDriver } from '../driver';
|
|
11
|
+
import {AfcClient} from './afc-client';
|
|
11
12
|
|
|
12
13
|
const DEFAULT_APP_INSTALLATION_TIMEOUT_MS = 8 * 60 * 1000;
|
|
13
14
|
export const IO_TIMEOUT_MS = 4 * 60 * 1000;
|
|
14
15
|
// Mobile devices use NAND memory modules for the storage,
|
|
15
16
|
// and the parallelism there is not as performant as on regular SSDs
|
|
16
|
-
const MAX_IO_CHUNK_SIZE = 8;
|
|
17
|
+
export const MAX_IO_CHUNK_SIZE = 8;
|
|
17
18
|
const APPLICATION_INSTALLED_NOTIFICATION = 'com.apple.mobile.application_installed';
|
|
18
19
|
const APPLICATION_NOTIFICATION_TIMEOUT_MS = 30 * 1000;
|
|
19
20
|
const INSTALLATION_STAGING_DIR = 'PublicStaging';
|
|
@@ -23,96 +24,52 @@ const INSTALLATION_STAGING_DIR = 'PublicStaging';
|
|
|
23
24
|
/**
|
|
24
25
|
* Retrieve a file from a real device
|
|
25
26
|
*
|
|
26
|
-
* @param
|
|
27
|
-
* 'appium-ios-device' module
|
|
27
|
+
* @param client AFC client instance
|
|
28
28
|
* @param remotePath Relative path to the file on the device
|
|
29
29
|
* @returns The file content as a buffer
|
|
30
30
|
*/
|
|
31
|
-
export async function pullFile(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}).timeout(IO_TIMEOUT_MS);
|
|
37
|
-
const buffers: Buffer[] = [];
|
|
38
|
-
stream.on('data', (data: Buffer) => buffers.push(data));
|
|
39
|
-
await pullPromise;
|
|
40
|
-
return Buffer.concat(buffers);
|
|
31
|
+
export async function pullFile(client: AfcClient, remotePath: string): Promise<Buffer> {
|
|
32
|
+
return await B.resolve(client.getFileContents(remotePath)).timeout(
|
|
33
|
+
IO_TIMEOUT_MS,
|
|
34
|
+
`Timed out after ${IO_TIMEOUT_MS}ms while pulling file from '${remotePath}'`
|
|
35
|
+
);
|
|
41
36
|
}
|
|
42
37
|
|
|
43
38
|
/**
|
|
44
39
|
* Retrieve a folder from a real device
|
|
45
40
|
*
|
|
46
|
-
* @param
|
|
47
|
-
* 'appium-ios-device' module
|
|
41
|
+
* @param client AFC client instance
|
|
48
42
|
* @param remoteRootPath Relative path to the folder on the device
|
|
49
43
|
* @returns The folder content as a zipped base64-encoded buffer
|
|
50
44
|
*/
|
|
51
|
-
export async function pullFolder(
|
|
45
|
+
export async function pullFolder(client: AfcClient, remoteRootPath: string): Promise<Buffer> {
|
|
52
46
|
const tmpFolder = await tempDir.openDir();
|
|
53
47
|
try {
|
|
54
48
|
let localTopItem: string | null = null;
|
|
55
49
|
let countFilesSuccess = 0;
|
|
56
|
-
let countFilesFail = 0;
|
|
57
50
|
let countFolders = 0;
|
|
58
|
-
const pullPromises: B<void>[] = [];
|
|
59
|
-
await afcService.walkDir(remoteRootPath, true, async (remotePath: string, isDir: boolean) => {
|
|
60
|
-
const localPath = path.join(tmpFolder, remotePath);
|
|
61
|
-
const dirname = isDir ? localPath : path.dirname(localPath);
|
|
62
|
-
if (!(await folderExists(dirname))) {
|
|
63
|
-
await mkdirp(dirname);
|
|
64
|
-
}
|
|
65
|
-
if (!localTopItem || localPath.split(path.sep).length < localTopItem.split(path.sep).length) {
|
|
66
|
-
localTopItem = localPath;
|
|
67
|
-
}
|
|
68
|
-
if (isDir) {
|
|
69
|
-
++countFolders;
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
51
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
resolve();
|
|
80
|
-
});
|
|
81
|
-
const onStreamingError = (e: Error) => {
|
|
82
|
-
readStream.unpipe(writeStream);
|
|
83
|
-
defaultLogger.warn(
|
|
84
|
-
`Cannot pull '${remotePath}' to '${localPath}'. ` +
|
|
85
|
-
`The file will be skipped. Original error: ${e.message}`,
|
|
86
|
-
);
|
|
87
|
-
++countFilesFail;
|
|
88
|
-
resolve();
|
|
89
|
-
};
|
|
90
|
-
writeStream.on('error', onStreamingError);
|
|
91
|
-
readStream.on('error', onStreamingError);
|
|
92
|
-
}).timeout(IO_TIMEOUT_MS),
|
|
93
|
-
);
|
|
94
|
-
readStream.pipe(writeStream);
|
|
95
|
-
if (pullPromises.length >= MAX_IO_CHUNK_SIZE) {
|
|
96
|
-
await B.any(pullPromises);
|
|
97
|
-
for (let i = pullPromises.length - 1; i >= 0; i--) {
|
|
98
|
-
if (pullPromises[i].isFulfilled()) {
|
|
99
|
-
pullPromises.splice(i, 1);
|
|
100
|
-
}
|
|
52
|
+
await client.pull(remoteRootPath, tmpFolder, {
|
|
53
|
+
recursive: true,
|
|
54
|
+
overwrite: true,
|
|
55
|
+
onEntry: async (remotePath: string, localPath: string, isDirectory: boolean) => {
|
|
56
|
+
if (!localTopItem || localPath.split(path.sep).length < localTopItem.split(path.sep).length) {
|
|
57
|
+
localTopItem = localPath;
|
|
101
58
|
}
|
|
102
|
-
|
|
59
|
+
if (isDirectory) {
|
|
60
|
+
++countFolders;
|
|
61
|
+
} else {
|
|
62
|
+
++countFilesSuccess;
|
|
63
|
+
}
|
|
64
|
+
},
|
|
103
65
|
});
|
|
104
|
-
|
|
105
|
-
if (!_.isEmpty(pullPromises)) {
|
|
106
|
-
await B.all(pullPromises);
|
|
107
|
-
}
|
|
66
|
+
|
|
108
67
|
defaultLogger.info(
|
|
109
|
-
`Pulled ${util.pluralize('file', countFilesSuccess, true)}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)} ` +
|
|
115
|
-
`from '${remoteRootPath}'`,
|
|
68
|
+
`Pulled ${util.pluralize('file', countFilesSuccess, true)} and ${util.pluralize(
|
|
69
|
+
'folder',
|
|
70
|
+
countFolders,
|
|
71
|
+
true,
|
|
72
|
+
)} from '${remoteRootPath}'`,
|
|
116
73
|
);
|
|
117
74
|
return await zip.toInMemoryZip(localTopItem ? path.dirname(localTopItem) : tmpFolder, {
|
|
118
75
|
encodeToBase64: true,
|
|
@@ -125,8 +82,7 @@ export async function pullFolder(afcService: any, remoteRootPath: string): Promi
|
|
|
125
82
|
/**
|
|
126
83
|
* Pushes a file to a real device
|
|
127
84
|
*
|
|
128
|
-
* @param
|
|
129
|
-
* 'appium-ios-device' module
|
|
85
|
+
* @param client AFC client instance
|
|
130
86
|
* @param localPathOrPayload Either full path to the source file
|
|
131
87
|
* or a buffer payload to be written into the remote destination
|
|
132
88
|
* @param remotePath Relative path to the file on the device. The remote
|
|
@@ -134,49 +90,30 @@ export async function pullFolder(afcService: any, remoteRootPath: string): Promi
|
|
|
134
90
|
* @param opts Push file options
|
|
135
91
|
*/
|
|
136
92
|
export async function pushFile(
|
|
137
|
-
|
|
93
|
+
client: AfcClient,
|
|
138
94
|
localPathOrPayload: string | Buffer,
|
|
139
95
|
remotePath: string,
|
|
140
96
|
opts: PushFileOptions = {}
|
|
141
97
|
): Promise<void> {
|
|
142
98
|
const {timeoutMs = IO_TIMEOUT_MS} = opts;
|
|
143
99
|
const timer = new timing.Timer().start();
|
|
144
|
-
await remoteMkdirp(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
const onStreamError = (e: Error) => {
|
|
162
|
-
if (!Buffer.isBuffer(source)) {
|
|
163
|
-
source.unpipe(writeStream);
|
|
164
|
-
}
|
|
165
|
-
defaultLogger.debug(e);
|
|
166
|
-
pushError = e;
|
|
167
|
-
};
|
|
168
|
-
writeStream.on('error', onStreamError);
|
|
169
|
-
if (!Buffer.isBuffer(source)) {
|
|
170
|
-
source.on('error', onStreamError);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
if (Buffer.isBuffer(source)) {
|
|
174
|
-
writeStream.write(source);
|
|
175
|
-
writeStream.end();
|
|
176
|
-
} else {
|
|
177
|
-
source.pipe(writeStream);
|
|
178
|
-
}
|
|
179
|
-
await filePushPromise.timeout(Math.max(timeoutMs, 60000));
|
|
100
|
+
await remoteMkdirp(client, path.dirname(remotePath));
|
|
101
|
+
|
|
102
|
+
// AfcClient handles the branching internally
|
|
103
|
+
const pushPromise = Buffer.isBuffer(localPathOrPayload)
|
|
104
|
+
? client.setFileContents(remotePath, localPathOrPayload)
|
|
105
|
+
: client.writeFromStream(
|
|
106
|
+
remotePath,
|
|
107
|
+
fs.createReadStream(localPathOrPayload, {autoClose: true})
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Wrap with timeout
|
|
111
|
+
const actualTimeout = Math.max(timeoutMs, 60000);
|
|
112
|
+
await B.resolve(pushPromise).timeout(
|
|
113
|
+
actualTimeout,
|
|
114
|
+
`Timed out after ${actualTimeout}ms while pushing file to '${remotePath}'`
|
|
115
|
+
);
|
|
116
|
+
|
|
180
117
|
const fileSize = Buffer.isBuffer(localPathOrPayload)
|
|
181
118
|
? localPathOrPayload.length
|
|
182
119
|
: (await fs.stat(localPathOrPayload)).size;
|
|
@@ -189,15 +126,14 @@ export async function pushFile(
|
|
|
189
126
|
/**
|
|
190
127
|
* Pushes a folder to a real device
|
|
191
128
|
*
|
|
192
|
-
* @param
|
|
193
|
-
* 'appium-ios-device' module
|
|
129
|
+
* @param client AFC client instance
|
|
194
130
|
* @param srcRootPath The full path to the source folder
|
|
195
131
|
* @param dstRootPath The relative path to the destination folder. The folder
|
|
196
132
|
* will be deleted if already exists.
|
|
197
133
|
* @param opts Push folder options
|
|
198
134
|
*/
|
|
199
135
|
export async function pushFolder(
|
|
200
|
-
|
|
136
|
+
client: AfcClient,
|
|
201
137
|
srcRootPath: string,
|
|
202
138
|
dstRootPath: string,
|
|
203
139
|
opts: PushFolderOptions = {}
|
|
@@ -228,16 +164,16 @@ export async function pushFolder(
|
|
|
228
164
|
`Got ${util.pluralize('folder', foldersToPush.length, true)} and ` +
|
|
229
165
|
`${util.pluralize('file', filesToPush.length, true)} to push`,
|
|
230
166
|
);
|
|
231
|
-
//
|
|
167
|
+
// Create the folder structure
|
|
232
168
|
try {
|
|
233
|
-
await
|
|
169
|
+
await client.deleteDirectory(dstRootPath);
|
|
234
170
|
} catch {}
|
|
235
|
-
|
|
171
|
+
|
|
172
|
+
await client.createDirectory(dstRootPath);
|
|
236
173
|
for (const relativeFolderPath of foldersToPush) {
|
|
237
|
-
// createDirectory does not accept folder names ending with a path separator
|
|
238
174
|
const absoluteFolderPath = _.trimEnd(path.join(dstRootPath, relativeFolderPath), path.sep);
|
|
239
175
|
if (absoluteFolderPath) {
|
|
240
|
-
await
|
|
176
|
+
await client.createDirectory(absoluteFolderPath);
|
|
241
177
|
}
|
|
242
178
|
}
|
|
243
179
|
// do not forget about the root folder
|
|
@@ -250,29 +186,13 @@ export async function pushFolder(
|
|
|
250
186
|
const absoluteSourcePath = path.join(srcRootPath, relativePath);
|
|
251
187
|
const readStream = fs.createReadStream(absoluteSourcePath, {autoClose: true});
|
|
252
188
|
const absoluteDestinationPath = path.join(dstRootPath, relativePath);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
if (pushError) {
|
|
261
|
-
reject(pushError);
|
|
262
|
-
} else {
|
|
263
|
-
resolve();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
const onStreamError = (e: Error) => {
|
|
267
|
-
readStream.unpipe(writeStream);
|
|
268
|
-
defaultLogger.debug(e);
|
|
269
|
-
pushError = e;
|
|
270
|
-
};
|
|
271
|
-
writeStream.on('error', onStreamError);
|
|
272
|
-
readStream.on('error', onStreamError);
|
|
273
|
-
});
|
|
274
|
-
readStream.pipe(writeStream);
|
|
275
|
-
await filePushPromise.timeout(Math.max(timeoutMs - timer.getDuration().asMilliSeconds, 60000));
|
|
189
|
+
|
|
190
|
+
const pushPromise = client.writeFromStream(absoluteDestinationPath, readStream);
|
|
191
|
+
const actualTimeout = Math.max(timeoutMs - timer.getDuration().asMilliSeconds, 60000);
|
|
192
|
+
await B.resolve(pushPromise).timeout(
|
|
193
|
+
actualTimeout,
|
|
194
|
+
`Timed out after ${actualTimeout}ms while pushing '${relativePath}' to '${absoluteDestinationPath}'`
|
|
195
|
+
);
|
|
276
196
|
};
|
|
277
197
|
|
|
278
198
|
if (enableParallelPush) {
|
|
@@ -754,42 +674,28 @@ function isPreferDevicectlEnabled(): boolean {
|
|
|
754
674
|
return ['yes', 'true', '1'].includes(_.toLower(process.env.APPIUM_XCUITEST_PREFER_DEVICECTL));
|
|
755
675
|
};
|
|
756
676
|
|
|
757
|
-
/**
|
|
758
|
-
* Checks a presence of a local folder.
|
|
759
|
-
*
|
|
760
|
-
* @param folderPath Full path to the local folder
|
|
761
|
-
* @returns True if the folder exists and is actually a folder
|
|
762
|
-
*/
|
|
763
|
-
async function folderExists(folderPath: string): Promise<boolean> {
|
|
764
|
-
try {
|
|
765
|
-
return (await fs.stat(folderPath)).isDirectory();
|
|
766
|
-
} catch {
|
|
767
|
-
return false;
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
|
|
771
677
|
/**
|
|
772
678
|
* Creates remote folder path recursively. Noop if the given path
|
|
773
679
|
* already exists
|
|
774
680
|
*
|
|
775
|
-
* @param
|
|
776
|
-
* 'appium-ios-device' module
|
|
681
|
+
* @param client AFC client instance
|
|
777
682
|
* @param remoteRoot The relative path to the remote folder structure
|
|
778
683
|
* to be created
|
|
779
684
|
*/
|
|
780
|
-
async function remoteMkdirp(
|
|
685
|
+
async function remoteMkdirp(client: AfcClient, remoteRoot: string): Promise<void> {
|
|
781
686
|
if (remoteRoot === '.' || remoteRoot === '/') {
|
|
782
687
|
return;
|
|
783
688
|
}
|
|
689
|
+
|
|
784
690
|
try {
|
|
785
|
-
await
|
|
691
|
+
await client.listDirectory(remoteRoot);
|
|
786
692
|
return;
|
|
787
693
|
} catch {
|
|
788
|
-
//
|
|
789
|
-
|
|
790
|
-
await remoteMkdirp(afcService, path.dirname(remoteRoot));
|
|
694
|
+
// Directory is missing, create parent first
|
|
695
|
+
await remoteMkdirp(client, path.dirname(remoteRoot));
|
|
791
696
|
}
|
|
792
|
-
|
|
697
|
+
|
|
698
|
+
await client.createDirectory(remoteRoot);
|
|
793
699
|
}
|
|
794
700
|
|
|
795
701
|
//#endregion
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "appium-xcuitest-driver",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.16.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "appium-xcuitest-driver",
|
|
9
|
-
"version": "10.
|
|
9
|
+
"version": "10.16.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@appium/strongbox": "^1.0.0-rc.1",
|
|
@@ -692,9 +692,9 @@
|
|
|
692
692
|
}
|
|
693
693
|
},
|
|
694
694
|
"node_modules/appium-ios-remotexpc": {
|
|
695
|
-
"version": "0.
|
|
696
|
-
"resolved": "https://registry.npmjs.org/appium-ios-remotexpc/-/appium-ios-remotexpc-0.
|
|
697
|
-
"integrity": "sha512-
|
|
695
|
+
"version": "0.26.0",
|
|
696
|
+
"resolved": "https://registry.npmjs.org/appium-ios-remotexpc/-/appium-ios-remotexpc-0.26.0.tgz",
|
|
697
|
+
"integrity": "sha512-9c7z05u2vlwhn6OPyY60Tfug3U5LPNw1k6Se0qTRUxnGs7ttWltk+5Dn+QUy4XDRKCtogmv/azVWHBlfIz2xKg==",
|
|
698
698
|
"license": "Apache-2.0",
|
|
699
699
|
"optional": true,
|
|
700
700
|
"dependencies": {
|