UNPKG

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