1 | import fs from 'fs';
|
2 | import path from 'path';
|
3 | import _ from 'lodash';
|
4 | import { ArgumentParser } from 'argparse';
|
5 | import { rootDir } from './utils';
|
6 | import { DEFAULT_BASE_PATH } from 'appium-base-driver';
|
7 | import {
|
8 | StoreDeprecatedAction, StoreDeprecatedTrueAction,
|
9 | StoreDeprecatedDefaultCapabilityAction, StoreDeprecatedDefaultCapabilityTrueAction,
|
10 | DEFAULT_CAPS_ARG,
|
11 | } from './argsparse-actions';
|
12 |
|
13 |
|
14 | const args = [
|
15 | [['--shell'], {
|
16 | required: false,
|
17 | default: false,
|
18 | action: 'store_true',
|
19 | help: 'Enter REPL mode',
|
20 | dest: 'shell',
|
21 | }],
|
22 |
|
23 | [['--allow-cors'], {
|
24 | required: false,
|
25 | default: false,
|
26 | action: 'store_true',
|
27 | help: 'Whether the Appium server should allow web browser connections from any host',
|
28 | dest: 'allowCors',
|
29 | }],
|
30 |
|
31 | [['--reboot'], {
|
32 | default: false,
|
33 | dest: 'reboot',
|
34 | action: 'store_true',
|
35 | required: false,
|
36 | help: '(Android-only) reboot emulator after each session and kill it at the end',
|
37 | }],
|
38 |
|
39 | [['--ipa'], {
|
40 | required: false,
|
41 | default: null,
|
42 | help: '(IOS-only) abs path to compiled .ipa file',
|
43 | dest: 'ipa',
|
44 | }],
|
45 |
|
46 | [['-a', '--address'], {
|
47 | default: '0.0.0.0',
|
48 | required: false,
|
49 | help: 'IP Address to listen on',
|
50 | dest: 'address',
|
51 | }],
|
52 |
|
53 | [['-p', '--port'], {
|
54 | default: 4723,
|
55 | required: false,
|
56 | type: 'int',
|
57 | help: 'port to listen on',
|
58 | dest: 'port',
|
59 | }],
|
60 |
|
61 | [['-pa', '--base-path'], {
|
62 | required: false,
|
63 | default: DEFAULT_BASE_PATH,
|
64 | dest: 'basePath',
|
65 | help: 'Base path to use as the prefix for all webdriver routes running' +
|
66 | 'on this server'
|
67 | }],
|
68 |
|
69 | [['-ka', '--keep-alive-timeout'], {
|
70 | required: false,
|
71 | default: null,
|
72 | dest: 'keepAliveTimeout',
|
73 | type: 'int',
|
74 | help: 'Number of seconds the Appium server should apply as both the keep-alive timeout ' +
|
75 | 'and the connection timeout for all requests. Defaults to 600 (10 minutes).'
|
76 | }],
|
77 |
|
78 | [['-ca', '--callback-address'], {
|
79 | required: false,
|
80 | dest: 'callbackAddress',
|
81 | default: null,
|
82 | help: 'callback IP Address (default: same as --address)',
|
83 | }],
|
84 |
|
85 | [['-cp', '--callback-port'], {
|
86 | required: false,
|
87 | dest: 'callbackPort',
|
88 | default: null,
|
89 | type: 'int',
|
90 | help: 'callback port (default: same as port)',
|
91 | }],
|
92 |
|
93 | [['-bp', '--bootstrap-port'], {
|
94 | default: 4724,
|
95 | dest: 'bootstrapPort',
|
96 | required: false,
|
97 | type: 'int',
|
98 | help: '(Android-only) port to use on device to talk to Appium',
|
99 | }],
|
100 |
|
101 | [['-r', '--backend-retries'], {
|
102 | default: 3,
|
103 | dest: 'backendRetries',
|
104 | required: false,
|
105 | type: 'int',
|
106 | help: '(iOS-only) How many times to retry launching Instruments ' +
|
107 | 'before saying it crashed or timed out',
|
108 | }],
|
109 |
|
110 | [['--session-override'], {
|
111 | default: false,
|
112 | dest: 'sessionOverride',
|
113 | action: 'store_true',
|
114 | required: false,
|
115 | help: 'Enables session override (clobbering)',
|
116 | }],
|
117 |
|
118 | [['-l', '--pre-launch'], {
|
119 | default: false,
|
120 | dest: 'launch',
|
121 | action: 'store_true',
|
122 | required: false,
|
123 | help: 'Pre-launch the application before allowing the first session ' +
|
124 | '(Requires --app and, for Android, --app-pkg and --app-activity)',
|
125 | }],
|
126 |
|
127 | [['-g', '--log'], {
|
128 | default: null,
|
129 | dest: 'logFile',
|
130 | required: false,
|
131 | help: 'Also send log output to this file',
|
132 | }],
|
133 |
|
134 | [['--log-level'], {
|
135 | choices: [
|
136 | 'info', 'info:debug', 'info:info', 'info:warn', 'info:error',
|
137 | 'warn', 'warn:debug', 'warn:info', 'warn:warn', 'warn:error',
|
138 | 'error', 'error:debug', 'error:info', 'error:warn', 'error:error',
|
139 | 'debug', 'debug:debug', 'debug:info', 'debug:warn', 'debug:error',
|
140 | ],
|
141 | default: 'debug',
|
142 | dest: 'loglevel',
|
143 | required: false,
|
144 | help: 'log level; default (console[:file]): debug[:debug]',
|
145 | }],
|
146 |
|
147 | [['--log-timestamp'], {
|
148 | default: false,
|
149 | required: false,
|
150 | help: 'Show timestamps in console output',
|
151 | action: 'store_true',
|
152 | dest: 'logTimestamp',
|
153 | }],
|
154 |
|
155 | [['--local-timezone'], {
|
156 | default: false,
|
157 | required: false,
|
158 | help: 'Use local timezone for timestamps',
|
159 | action: 'store_true',
|
160 | dest: 'localTimezone',
|
161 | }],
|
162 |
|
163 | [['--log-no-colors'], {
|
164 | default: false,
|
165 | required: false,
|
166 | help: 'Do not use colors in console output',
|
167 | action: 'store_true',
|
168 | dest: 'logNoColors',
|
169 | }],
|
170 |
|
171 | [['-G', '--webhook'], {
|
172 | default: null,
|
173 | required: false,
|
174 | dest: 'webhook',
|
175 | help: 'Also send log output to this HTTP listener, for example localhost:9876',
|
176 | }],
|
177 |
|
178 | [['--safari'], {
|
179 | default: false,
|
180 | action: 'store_true',
|
181 | dest: 'safari',
|
182 | required: false,
|
183 | help: '(IOS-Only) Use the safari app',
|
184 | }],
|
185 |
|
186 | [['--default-device', '-dd'], {
|
187 | dest: 'defaultDevice',
|
188 | default: false,
|
189 | action: 'store_true',
|
190 | required: false,
|
191 | help: '(IOS-Simulator-only) use the default simulator that instruments ' +
|
192 | 'launches on its own',
|
193 | }],
|
194 |
|
195 | [['--force-iphone'], {
|
196 | default: false,
|
197 | dest: 'forceIphone',
|
198 | action: 'store_true',
|
199 | required: false,
|
200 | help: '(IOS-only) Use the iPhone Simulator no matter what the app wants',
|
201 | }],
|
202 |
|
203 | [['--force-ipad'], {
|
204 | default: false,
|
205 | dest: 'forceIpad',
|
206 | action: 'store_true',
|
207 | required: false,
|
208 | help: '(IOS-only) Use the iPad Simulator no matter what the app wants',
|
209 | }],
|
210 |
|
211 | [['--tracetemplate'], {
|
212 | default: null,
|
213 | dest: 'automationTraceTemplatePath',
|
214 | required: false,
|
215 | help: '(IOS-only) .tracetemplate file to use with Instruments',
|
216 | }],
|
217 |
|
218 | [['--instruments'], {
|
219 | default: null,
|
220 | dest: 'instrumentsPath',
|
221 | required: false,
|
222 | help: '(IOS-only) path to instruments binary',
|
223 | }],
|
224 |
|
225 | [['--nodeconfig'], {
|
226 | required: false,
|
227 | default: null,
|
228 | dest: 'nodeconfig',
|
229 | help: 'Configuration JSON file to register appium with selenium grid',
|
230 | }],
|
231 |
|
232 | [['-ra', '--robot-address'], {
|
233 | default: '0.0.0.0',
|
234 | dest: 'robotAddress',
|
235 | required: false,
|
236 | help: 'IP Address of robot',
|
237 | }],
|
238 |
|
239 | [['-rp', '--robot-port'], {
|
240 | default: -1,
|
241 | dest: 'robotPort',
|
242 | required: false,
|
243 | type: 'int',
|
244 | help: 'port for robot',
|
245 | }],
|
246 |
|
247 | [['--chromedriver-executable'], {
|
248 | default: null,
|
249 | dest: 'chromedriverExecutable',
|
250 | required: false,
|
251 | help: 'ChromeDriver executable full path',
|
252 | }],
|
253 |
|
254 | [['--show-config'], {
|
255 | default: false,
|
256 | dest: 'showConfig',
|
257 | action: 'store_true',
|
258 | required: false,
|
259 | help: 'Show info about the appium server configuration and exit',
|
260 | }],
|
261 |
|
262 | [['--no-perms-check'], {
|
263 | default: false,
|
264 | dest: 'noPermsCheck',
|
265 | action: 'store_true',
|
266 | required: false,
|
267 | help: 'Bypass Appium\'s checks to ensure we can read/write necessary files',
|
268 | }],
|
269 |
|
270 | [['--strict-caps'], {
|
271 | default: false,
|
272 | dest: 'enforceStrictCaps',
|
273 | action: 'store_true',
|
274 | required: false,
|
275 | help: 'Cause sessions to fail if desired caps are sent in that Appium ' +
|
276 | 'does not recognize as valid for the selected device',
|
277 | }],
|
278 |
|
279 | [['--isolate-sim-device'], {
|
280 | default: false,
|
281 | dest: 'isolateSimDevice',
|
282 | action: 'store_true',
|
283 | required: false,
|
284 | help: 'Xcode 6 has a bug on some platforms where a certain simulator ' +
|
285 | 'can only be launched without error if all other simulator devices ' +
|
286 | 'are first deleted. This option causes Appium to delete all ' +
|
287 | 'devices other than the one being used by Appium. Note that this ' +
|
288 | 'is a permanent deletion, and you are responsible for using simctl ' +
|
289 | 'or xcode to manage the categories of devices used with Appium.',
|
290 | }],
|
291 |
|
292 | [['--tmp'], {
|
293 | default: null,
|
294 | dest: 'tmpDir',
|
295 | required: false,
|
296 | help: 'Absolute path to directory Appium can use to manage temporary ' +
|
297 | 'files, like built-in iOS apps it needs to move around. On *nix/Mac ' +
|
298 | 'defaults to /tmp, on Windows defaults to C:\\Windows\\Temp',
|
299 | }],
|
300 |
|
301 | [['--trace-dir'], {
|
302 | default: null,
|
303 | dest: 'traceDir',
|
304 | required: false,
|
305 | help: 'Absolute path to directory Appium use to save ios instruments ' +
|
306 | 'traces, defaults to <tmp dir>/appium-instruments',
|
307 | }],
|
308 |
|
309 | [['--debug-log-spacing'], {
|
310 | dest: 'debugLogSpacing',
|
311 | default: false,
|
312 | action: 'store_true',
|
313 | required: false,
|
314 | help: 'Add exaggerated spacing in logs to help with visual inspection',
|
315 | }],
|
316 |
|
317 | [['--suppress-adb-kill-server'], {
|
318 | dest: 'suppressKillServer',
|
319 | default: false,
|
320 | action: 'store_true',
|
321 | required: false,
|
322 | help: '(Android-only) If set, prevents Appium from killing the adb server instance',
|
323 | }],
|
324 |
|
325 | [['--long-stacktrace'], {
|
326 | dest: 'longStacktrace',
|
327 | default: false,
|
328 | required: false,
|
329 | action: 'store_true',
|
330 | help: 'Add long stack traces to log entries. Recommended for debugging only.',
|
331 | }],
|
332 |
|
333 | [['--webkit-debug-proxy-port'], {
|
334 | default: 27753,
|
335 | dest: 'webkitDebugProxyPort',
|
336 | required: false,
|
337 | type: 'int',
|
338 | help: '(IOS-only) Local port used for communication with ios-webkit-debug-proxy'
|
339 | }],
|
340 |
|
341 | [['--webdriveragent-port'], {
|
342 | default: 8100,
|
343 | dest: 'wdaLocalPort',
|
344 | required: false,
|
345 | type: 'int',
|
346 | help: '(IOS-only, XCUITest-only) Local port used for communication with WebDriverAgent'
|
347 | }],
|
348 |
|
349 | [['-dc', DEFAULT_CAPS_ARG], {
|
350 | dest: 'defaultCapabilities',
|
351 | default: {},
|
352 | type: parseDefaultCaps,
|
353 | required: false,
|
354 | help: 'Set the default desired capabilities, which will be set on each ' +
|
355 | 'session unless overridden by received capabilities. For example: ' +
|
356 | '[ \'{"app": "myapp.app", "deviceName": "iPhone Simulator"}\' ' +
|
357 | '| /path/to/caps.json ]'
|
358 | }],
|
359 |
|
360 | [['--relaxed-security'], {
|
361 | default: false,
|
362 | dest: 'relaxedSecurityEnabled',
|
363 | action: 'store_true',
|
364 | required: false,
|
365 | help: 'Disable additional security checks, so it is possible to use some advanced features, provided ' +
|
366 | 'by drivers supporting this option. Only enable it if all the ' +
|
367 | 'clients are in the trusted network and it\'s not the case if a client could potentially ' +
|
368 | 'break out of the session sandbox. Specific features can be overridden by ' +
|
369 | 'using the --deny-insecure flag',
|
370 | }],
|
371 |
|
372 | [['--allow-insecure'], {
|
373 | dest: 'allowInsecure',
|
374 | default: [],
|
375 | type: parseSecurityFeatures,
|
376 | required: false,
|
377 | help: 'Set which insecure features are allowed to run in this server\'s sessions. ' +
|
378 | 'Features are defined on a driver level; see documentation for more details. ' +
|
379 | 'This should be either a comma-separated list of feature names, or a path to ' +
|
380 | 'a file where each feature name is on a line. Note that features defined via ' +
|
381 | '--deny-insecure will be disabled, even if also listed here. For example: ' +
|
382 | 'execute_driver_script,adb_shell',
|
383 | }],
|
384 |
|
385 | [['--deny-insecure'], {
|
386 | dest: 'denyInsecure',
|
387 | default: [],
|
388 | type: parseSecurityFeatures,
|
389 | required: false,
|
390 | help: 'Set which insecure features are not allowed to run in this server\'s sessions. ' +
|
391 | 'Features are defined on a driver level; see documentation for more details. ' +
|
392 | 'This should be either a comma-separated list of feature names, or a path to ' +
|
393 | 'a file where each feature name is on a line. Features listed here will not be ' +
|
394 | 'enabled even if also listed in --allow-insecure, and even if --relaxed-security ' +
|
395 | 'is turned on. For example: execute_driver_script,adb_shell',
|
396 | }],
|
397 |
|
398 | [['--command-timeout'], {
|
399 | default: 60,
|
400 | dest: 'defaultCommandTimeout',
|
401 | type: 'int',
|
402 | required: false,
|
403 | deprecated_for: 'newCommandTimeout capability',
|
404 | action: StoreDeprecatedAction,
|
405 | help: 'No effect. This used to be the default command ' +
|
406 | 'timeout for the server to use for all sessions (in seconds and ' +
|
407 | 'should be less than 2147483). Use newCommandTimeout cap instead'
|
408 | }],
|
409 |
|
410 | [['-k', '--keep-artifacts'], {
|
411 | default: false,
|
412 | dest: 'keepArtifacts',
|
413 | action: StoreDeprecatedTrueAction,
|
414 | required: false,
|
415 | help: 'No effect, trace is now in tmp dir by default and is ' +
|
416 | 'cleared before each run. Please also refer to the --trace-dir flag.',
|
417 | }],
|
418 |
|
419 | [['--platform-name'], {
|
420 | dest: 'platformName',
|
421 | default: null,
|
422 | required: false,
|
423 | action: StoreDeprecatedDefaultCapabilityAction,
|
424 | help: 'Name of the mobile platform: iOS, Android, or FirefoxOS',
|
425 | }],
|
426 |
|
427 | [['--platform-version'], {
|
428 | dest: 'platformVersion',
|
429 | default: null,
|
430 | required: false,
|
431 | action: StoreDeprecatedDefaultCapabilityAction,
|
432 | help: 'Version of the mobile platform',
|
433 | }],
|
434 |
|
435 | [['--automation-name'], {
|
436 | dest: 'automationName',
|
437 | default: null,
|
438 | required: false,
|
439 | action: StoreDeprecatedDefaultCapabilityAction,
|
440 | help: 'Name of the automation tool: Appium, XCUITest, etc.',
|
441 | }],
|
442 |
|
443 | [['--device-name'], {
|
444 | dest: 'deviceName',
|
445 | default: null,
|
446 | required: false,
|
447 | action: StoreDeprecatedDefaultCapabilityAction,
|
448 | help: 'Name of the mobile device to use, for example: ' +
|
449 | 'iPhone Retina (4-inch), Android Emulator',
|
450 | }],
|
451 |
|
452 | [['--browser-name'], {
|
453 | dest: 'browserName',
|
454 | default: null,
|
455 | required: false,
|
456 | action: StoreDeprecatedDefaultCapabilityAction,
|
457 | help: 'Name of the mobile browser: Safari or Chrome',
|
458 | }],
|
459 |
|
460 | [['--app'], {
|
461 | dest: 'app',
|
462 | required: false,
|
463 | default: null,
|
464 | action: StoreDeprecatedDefaultCapabilityAction,
|
465 | help: 'IOS: abs path to simulator-compiled .app file or the ' +
|
466 | 'bundle_id of the desired target on device; Android: abs path to .apk file',
|
467 | }],
|
468 |
|
469 | [['-lt', '--launch-timeout'], {
|
470 | default: 90000,
|
471 | dest: 'launchTimeout',
|
472 | type: 'int',
|
473 | required: false,
|
474 | action: StoreDeprecatedDefaultCapabilityAction,
|
475 | help: '(iOS-only) how long in ms to wait for Instruments to launch',
|
476 | }],
|
477 |
|
478 | [['--language'], {
|
479 | default: null,
|
480 | dest: 'language',
|
481 | required: false,
|
482 | action: StoreDeprecatedDefaultCapabilityAction,
|
483 | help: 'Language for the iOS simulator / Android Emulator, like: en, es',
|
484 | }],
|
485 |
|
486 | [['--locale'], {
|
487 | default: null,
|
488 | dest: 'locale',
|
489 | required: false,
|
490 | action: StoreDeprecatedDefaultCapabilityAction,
|
491 | help: 'Locale for the iOS simulator / Android Emulator, like en_US, de_DE',
|
492 | }],
|
493 |
|
494 | [['-U', '--udid'], {
|
495 | dest: 'udid',
|
496 | required: false,
|
497 | default: null,
|
498 | action: StoreDeprecatedDefaultCapabilityAction,
|
499 | help: 'Unique device identifier of the connected physical device',
|
500 | }],
|
501 |
|
502 | [['--orientation'], {
|
503 | dest: 'orientation',
|
504 | default: null,
|
505 | required: false,
|
506 | action: StoreDeprecatedDefaultCapabilityAction,
|
507 | help: '(IOS-only) use LANDSCAPE or PORTRAIT to initialize all requests ' +
|
508 | 'to this orientation',
|
509 | }],
|
510 |
|
511 | [['--no-reset'], {
|
512 | default: false,
|
513 | dest: 'noReset',
|
514 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
515 | required: false,
|
516 | help: 'Do not reset app state between sessions (IOS: do not delete app ' +
|
517 | 'plist files; Android: do not uninstall app before new session)',
|
518 | }],
|
519 |
|
520 | [['--full-reset'], {
|
521 | default: false,
|
522 | dest: 'fullReset',
|
523 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
524 | required: false,
|
525 | help: '(iOS) Delete the entire simulator folder. (Android) Reset app ' +
|
526 | 'state by uninstalling app instead of clearing app data. On ' +
|
527 | 'Android, this will also remove the app after the session is complete.',
|
528 | }],
|
529 |
|
530 | [['--app-pkg'], {
|
531 | dest: 'appPackage',
|
532 | default: null,
|
533 | required: false,
|
534 | action: StoreDeprecatedDefaultCapabilityAction,
|
535 | help: '(Android-only) Java package of the Android app you want to run ' +
|
536 | '(e.g., com.example.android.myApp)',
|
537 | }],
|
538 |
|
539 | [['--app-activity'], {
|
540 | dest: 'appActivity',
|
541 | default: null,
|
542 | required: false,
|
543 | action: StoreDeprecatedDefaultCapabilityAction,
|
544 | help: '(Android-only) Activity name for the Android activity you want ' +
|
545 | 'to launch from your package (e.g., MainActivity)',
|
546 | }],
|
547 |
|
548 | [['--app-wait-package'], {
|
549 | dest: 'appWaitPackage',
|
550 | default: false,
|
551 | required: false,
|
552 | action: StoreDeprecatedDefaultCapabilityAction,
|
553 | help: '(Android-only) Package name for the Android activity you want ' +
|
554 | 'to wait for (e.g., com.example.android.myApp)',
|
555 | }],
|
556 |
|
557 | [['--app-wait-activity'], {
|
558 | dest: 'appWaitActivity',
|
559 | default: false,
|
560 | required: false,
|
561 | action: StoreDeprecatedDefaultCapabilityAction,
|
562 | help: '(Android-only) Activity name for the Android activity you want ' +
|
563 | 'to wait for (e.g., SplashActivity)',
|
564 | }],
|
565 |
|
566 | [['--device-ready-timeout'], {
|
567 | dest: 'deviceReadyTimeout',
|
568 | default: 5,
|
569 | required: false,
|
570 | type: 'int',
|
571 | action: StoreDeprecatedDefaultCapabilityAction,
|
572 | help: '(Android-only) Timeout in seconds while waiting for device to become ready',
|
573 | }],
|
574 |
|
575 | [['--android-coverage'], {
|
576 | dest: 'androidCoverage',
|
577 | default: false,
|
578 | required: false,
|
579 | action: StoreDeprecatedDefaultCapabilityAction,
|
580 | help: '(Android-only) Fully qualified instrumentation class. Passed to -w in ' +
|
581 | 'adb shell am instrument -e coverage true -w ' +
|
582 | '(e.g. com.my.Pkg/com.my.Pkg.instrumentation.MyInstrumentation)',
|
583 | }],
|
584 |
|
585 | [['--avd'], {
|
586 | dest: 'avd',
|
587 | default: null,
|
588 | required: false,
|
589 | action: StoreDeprecatedDefaultCapabilityAction,
|
590 | help: '(Android-only) Name of the avd to launch (e.g. @Nexus_5)',
|
591 | }],
|
592 |
|
593 | [['--avd-args'], {
|
594 | dest: 'avdArgs',
|
595 | default: null,
|
596 | required: false,
|
597 | action: StoreDeprecatedDefaultCapabilityAction,
|
598 | help: '(Android-only) Additional emulator arguments to launch the avd (e.g. -no-snapshot-load)',
|
599 | }],
|
600 |
|
601 | [['--use-keystore'], {
|
602 | default: false,
|
603 | dest: 'useKeystore',
|
604 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
605 | required: false,
|
606 | help: '(Android-only) When set the keystore will be used to sign apks.',
|
607 | }],
|
608 |
|
609 | [['--keystore-path'], {
|
610 | default: path.resolve(process.env.HOME || process.env.USERPROFILE || '', '.android', 'debug.keystore'),
|
611 | dest: 'keystorePath',
|
612 | required: false,
|
613 | action: StoreDeprecatedDefaultCapabilityAction,
|
614 | help: '(Android-only) Path to keystore',
|
615 | }],
|
616 |
|
617 | [['--keystore-password'], {
|
618 | default: 'android',
|
619 | dest: 'keystorePassword',
|
620 | required: false,
|
621 | action: StoreDeprecatedDefaultCapabilityAction,
|
622 | help: '(Android-only) Password to keystore',
|
623 | }],
|
624 |
|
625 | [['--key-alias'], {
|
626 | default: 'androiddebugkey',
|
627 | dest: 'keyAlias',
|
628 | required: false,
|
629 | action: StoreDeprecatedDefaultCapabilityAction,
|
630 | help: '(Android-only) Key alias',
|
631 | }],
|
632 |
|
633 | [['--key-password'], {
|
634 | default: 'android',
|
635 | dest: 'keyPassword',
|
636 | required: false,
|
637 | action: StoreDeprecatedDefaultCapabilityAction,
|
638 | help: '(Android-only) Key password',
|
639 | }],
|
640 |
|
641 | [['--intent-action'], {
|
642 | dest: 'intentAction',
|
643 | default: 'android.intent.action.MAIN',
|
644 | required: false,
|
645 | action: StoreDeprecatedDefaultCapabilityAction,
|
646 | help: '(Android-only) Intent action which will be used to start activity (e.g. android.intent.action.MAIN)',
|
647 | }],
|
648 |
|
649 | [['--intent-category'], {
|
650 | dest: 'intentCategory',
|
651 | default: 'android.intent.category.LAUNCHER',
|
652 | required: false,
|
653 | action: StoreDeprecatedDefaultCapabilityAction,
|
654 | help: '(Android-only) Intent category which will be used to start activity ' +
|
655 | '(e.g. android.intent.category.APP_CONTACTS)',
|
656 | }],
|
657 |
|
658 | [['--intent-flags'], {
|
659 | dest: 'intentFlags',
|
660 | default: '0x10200000',
|
661 | required: false,
|
662 | action: StoreDeprecatedDefaultCapabilityAction,
|
663 | help: '(Android-only) Flags that will be used to start activity (e.g. 0x10200000)',
|
664 | }],
|
665 |
|
666 | [['--intent-args'], {
|
667 | dest: 'optionalIntentArguments',
|
668 | default: null,
|
669 | required: false,
|
670 | action: StoreDeprecatedDefaultCapabilityAction,
|
671 | help: '(Android-only) Additional intent arguments that will be used to start activity (e.g. 0x10200000)',
|
672 | }],
|
673 |
|
674 | [['--dont-stop-app-on-reset'], {
|
675 | dest: 'dontStopAppOnReset',
|
676 | default: false,
|
677 | required: false,
|
678 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
679 | help: '(Android-only) When included, refrains from stopping the app before restart',
|
680 | }],
|
681 |
|
682 | [['--calendar-format'], {
|
683 | default: null,
|
684 | dest: 'calendarFormat',
|
685 | required: false,
|
686 | action: StoreDeprecatedDefaultCapabilityAction,
|
687 | help: '(IOS-only) calendar format for the iOS simulator (e.g. gregorian)',
|
688 | }],
|
689 |
|
690 | [['--native-instruments-lib'], {
|
691 | default: false,
|
692 | dest: 'nativeInstrumentsLib',
|
693 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
694 | required: false,
|
695 | help: '(IOS-only) IOS has a weird built-in unavoidable ' +
|
696 | 'delay. We patch this in appium. If you do not want it patched, pass in this flag.',
|
697 | }],
|
698 |
|
699 | [['--keep-keychains'], {
|
700 | default: false,
|
701 | dest: 'keepKeyChains',
|
702 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
703 | required: false,
|
704 | help: '(iOS-only) Whether to keep keychains ' +
|
705 | '(Library/Keychains) when reset app between sessions',
|
706 | }],
|
707 |
|
708 | [['--localizable-strings-dir'], {
|
709 | required: false,
|
710 | dest: 'localizableStringsDir',
|
711 | default: 'en.lproj',
|
712 | action: StoreDeprecatedDefaultCapabilityAction,
|
713 | help: '(IOS-only) the relative path of the dir where Localizable.strings file resides (e.g. en.lproj)',
|
714 | }],
|
715 |
|
716 | [['--show-ios-log'], {
|
717 | default: false,
|
718 | dest: 'showIOSLog',
|
719 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
720 | required: false,
|
721 | help: '(IOS-only) if set, the iOS system log will be written to the console',
|
722 | }],
|
723 |
|
724 | [['--async-trace'], {
|
725 | dest: 'longStacktrace',
|
726 | default: false,
|
727 | required: false,
|
728 | action: StoreDeprecatedDefaultCapabilityTrueAction,
|
729 | help: 'Add long stack traces to log entries. Recommended for debugging only.',
|
730 | }],
|
731 |
|
732 | [['--chromedriver-port'], {
|
733 | default: null,
|
734 | dest: 'chromedriverPort',
|
735 | required: false,
|
736 | type: 'int',
|
737 | action: StoreDeprecatedDefaultCapabilityAction,
|
738 | help: 'Port upon which ChromeDriver will run. If not given, ' +
|
739 | 'Android driver will pick a random available port.',
|
740 | }],
|
741 |
|
742 | [['--log-filters'], {
|
743 | dest: 'logFilters',
|
744 | default: null,
|
745 | required: false,
|
746 | help: 'Set the full path to a JSON file containing one or more log filtering rules',
|
747 | }],
|
748 | ];
|
749 |
|
750 | function parseSecurityFeatures (features) {
|
751 | const splitter = (splitOn, str) => `${str}`.split(splitOn)
|
752 | .map((s) => s.trim())
|
753 | .filter(Boolean);
|
754 | let parsedFeatures;
|
755 | try {
|
756 | parsedFeatures = splitter(',', features);
|
757 | } catch (err) {
|
758 | throw new TypeError('Could not parse value of --allow/deny-insecure. Should be ' +
|
759 | 'a list of strings separated by commas, or a path to a file ' +
|
760 | 'listing one feature name per line.');
|
761 | }
|
762 |
|
763 | if (parsedFeatures.length === 1 && fs.existsSync(parsedFeatures[0])) {
|
764 |
|
765 | try {
|
766 | const fileFeatures = fs.readFileSync(parsedFeatures[0], 'utf8');
|
767 | parsedFeatures = splitter('\n', fileFeatures);
|
768 | } catch (err) {
|
769 | throw new TypeError(`Attempted to read --allow/deny-insecure feature names ` +
|
770 | `from file ${parsedFeatures[0]} but got error: ${err.message}`);
|
771 | }
|
772 | }
|
773 |
|
774 | return parsedFeatures;
|
775 | }
|
776 |
|
777 | function parseDefaultCaps (capsOrPath) {
|
778 | let caps = capsOrPath;
|
779 | let loadedFromFile = false;
|
780 | try {
|
781 |
|
782 |
|
783 |
|
784 |
|
785 |
|
786 | if (_.isString(capsOrPath) && fs.statSync(capsOrPath).isFile()) {
|
787 | caps = fs.readFileSync(capsOrPath, 'utf8');
|
788 | loadedFromFile = true;
|
789 | }
|
790 | } catch (err) {
|
791 |
|
792 | }
|
793 | try {
|
794 | const result = JSON.parse(caps);
|
795 | if (!_.isPlainObject(result)) {
|
796 | throw new Error(`'${_.truncate(result, {length: 100})}' is not an object`);
|
797 | }
|
798 | return result;
|
799 | } catch (e) {
|
800 | const msg = loadedFromFile
|
801 | ? `Default capabilities in '${capsOrPath}' must be a valid JSON`
|
802 | : `Default capabilities must be a valid JSON`;
|
803 | throw new TypeError(`${msg}. Original error: ${e.message}`);
|
804 | }
|
805 | }
|
806 |
|
807 | function getParser () {
|
808 | const parser = new ArgumentParser({
|
809 | add_help: true,
|
810 | description: 'A webdriver-compatible server for use with native and hybrid iOS and Android applications.',
|
811 | prog: process.argv[1] || 'Appium'
|
812 | });
|
813 | parser.rawArgs = args;
|
814 | for (const [flagsOrNames, options] of args) {
|
815 | parser.add_argument(...flagsOrNames, options);
|
816 | }
|
817 | parser.add_argument('-v', '--version', {
|
818 | action: 'version',
|
819 | version: require(path.resolve(rootDir, 'package.json')).version,
|
820 | });
|
821 | return parser;
|
822 | }
|
823 |
|
824 | function getDefaultArgs () {
|
825 | return args.reduce((acc, [, {dest, default: defaultValue}]) => {
|
826 | acc[dest] = defaultValue;
|
827 | return acc;
|
828 | }, {});
|
829 | }
|
830 |
|
831 | export default getParser;
|
832 | export { getDefaultArgs, getParser };
|