├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .fossa.yml ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── labeler.yml ├── lock.yml └── workflows │ ├── auto-labeler.yml │ ├── build.yml │ ├── clean-cache.yml │ ├── deploy.yml │ ├── docs.yml │ ├── fossa.yml │ ├── on-hold.yml │ ├── publish-doc.yml │ └── renovate.yml ├── .gitignore ├── .lintstagedrc.js ├── .mocharc.js ├── .npmrc ├── .prettierignore ├── .renovaterc.json ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── AUTHORS ├── CHANGELOG.md ├── CONDUCT.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── IDEAS.md ├── LICENSE ├── README.md ├── RELEASE.pdf ├── ROADMAP.md ├── SPONSORS.md ├── docs └── README.md ├── lerna.json ├── package-lock.json ├── package.json ├── packages ├── appium │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── .gitignore │ │ ├── base-mkdocs.yml │ │ ├── en │ │ │ ├── blog │ │ │ │ ├── .authors.yml │ │ │ │ ├── index.md │ │ │ │ └── posts │ │ │ │ │ ├── announcing-browserstack-as-strategic-partner.md │ │ │ │ │ ├── announcing-headspin-as-development-partner.md │ │ │ │ │ ├── announcing-sauce-labs-as-strategic-partner.md │ │ │ │ │ ├── announcing-sponsorship-program.md │ │ │ │ │ └── hello-world.md │ │ │ ├── cli │ │ │ │ ├── args.md │ │ │ │ ├── env-vars.md │ │ │ │ ├── extensions.md │ │ │ │ ├── index.md │ │ │ │ └── setup.md │ │ │ ├── commands │ │ │ │ ├── base-driver.md │ │ │ │ ├── execute-driver-plugin.md │ │ │ │ ├── images-plugin.md │ │ │ │ ├── index.md │ │ │ │ ├── relaxed-caps-plugin.md │ │ │ │ └── universal-xml-plugin.md │ │ │ ├── contributing │ │ │ │ ├── config-system.md │ │ │ │ ├── develop.md │ │ │ │ └── index.md │ │ │ ├── developing │ │ │ │ ├── build-docs.md │ │ │ │ ├── build-doctor-checks.md │ │ │ │ ├── build-drivers.md │ │ │ │ ├── build-plugins.md │ │ │ │ └── index.md │ │ │ ├── ecosystem │ │ │ │ ├── clients.md │ │ │ │ ├── drivers.md │ │ │ │ ├── index.md │ │ │ │ ├── plugins.md │ │ │ │ └── tools.md │ │ │ ├── guides │ │ │ │ ├── branch-testing.md │ │ │ │ ├── caching.md │ │ │ │ ├── caps.md │ │ │ │ ├── config.md │ │ │ │ ├── context.md │ │ │ │ ├── event-timing.md │ │ │ │ ├── execute-methods.md │ │ │ │ ├── grid.md │ │ │ │ ├── log-filters.md │ │ │ │ ├── managing-exts.md │ │ │ │ ├── migrating-1-to-2.md │ │ │ │ ├── security.md │ │ │ │ ├── settings.md │ │ │ │ └── tls.md │ │ │ ├── index.md │ │ │ ├── intro │ │ │ │ ├── appium.md │ │ │ │ ├── clients.md │ │ │ │ ├── drivers.md │ │ │ │ ├── history.md │ │ │ │ └── index.md │ │ │ ├── quickstart │ │ │ │ ├── index.md │ │ │ │ ├── install.md │ │ │ │ ├── next-steps.md │ │ │ │ ├── requirements.md │ │ │ │ ├── test-dotnet.md │ │ │ │ ├── test-java.md │ │ │ │ ├── test-js.md │ │ │ │ ├── test-py.md │ │ │ │ ├── test-rb.md │ │ │ │ └── uiauto2-driver.md │ │ │ └── resources.md │ │ ├── ja │ │ │ ├── blog │ │ │ │ └── index.md │ │ │ ├── guides │ │ │ │ └── migrating-1-to-2.md │ │ │ ├── index.md │ │ │ └── intro │ │ │ │ └── index.md │ │ ├── mkdocs-en.yml │ │ ├── mkdocs-ja.yml │ │ ├── mkdocs-zh.yml │ │ ├── overrides │ │ │ ├── assets │ │ │ │ ├── images │ │ │ │ │ ├── appium-logo-horiz-white.png │ │ │ │ │ ├── appium-logo-horiz.png │ │ │ │ │ ├── appium-logo-white.png │ │ │ │ │ ├── appium-logo.png │ │ │ │ │ ├── sponsor-logo-browserstack-dark.png │ │ │ │ │ ├── sponsor-logo-browserstack-light.png │ │ │ │ │ ├── sponsor-logo-headspin.png │ │ │ │ │ └── sponsor-logo-sauce.png │ │ │ │ └── stylesheets │ │ │ │ │ └── extra.css │ │ │ └── partials │ │ │ │ ├── footer.html │ │ │ │ └── sponsors.html │ │ ├── scripts │ │ │ ├── build-docs.js │ │ │ ├── gen-cli-args-docs.js │ │ │ └── utils.js │ │ └── zh │ │ │ ├── blog │ │ │ └── index.md │ │ │ ├── cli │ │ │ ├── args.md │ │ │ ├── extensions.md │ │ │ └── index.md │ │ │ ├── contributing │ │ │ └── index.md │ │ │ ├── ecosystem │ │ │ ├── build-docs.md │ │ │ ├── build-drivers.md │ │ │ ├── build-plugins.md │ │ │ └── index.md │ │ │ ├── guides │ │ │ ├── caps.md │ │ │ ├── config.md │ │ │ ├── context.md │ │ │ ├── event-timing.md │ │ │ ├── execute-script-overload.md │ │ │ ├── grid.md │ │ │ ├── log-filters.md │ │ │ ├── managing-exts.md │ │ │ ├── migrating-1-to-2.md │ │ │ ├── security.md │ │ │ └── settings.md │ │ │ ├── index.md │ │ │ ├── intro │ │ │ ├── clients.md │ │ │ ├── drivers.md │ │ │ ├── history.md │ │ │ └── index.md │ │ │ ├── quickstart │ │ │ ├── index.md │ │ │ ├── install.md │ │ │ ├── next-steps.md │ │ │ ├── test-java.md │ │ │ ├── test-js.md │ │ │ ├── test-py.md │ │ │ └── uiauto2-driver.md │ │ │ └── resources.md │ ├── driver.d.ts │ ├── driver.js │ ├── index.js │ ├── lib │ │ ├── appium.js │ │ ├── cli │ │ │ ├── args.js │ │ │ ├── driver-command.js │ │ │ ├── extension-command.js │ │ │ ├── extension.js │ │ │ ├── parser.js │ │ │ ├── plugin-command.js │ │ │ ├── setup-command.js │ │ │ └── utils.js │ │ ├── config-file.js │ │ ├── config.js │ │ ├── constants.js │ │ ├── doctor │ │ │ └── doctor.js │ │ ├── extension │ │ │ ├── driver-config.js │ │ │ ├── extension-config.js │ │ │ ├── index.js │ │ │ ├── manifest-migrations.js │ │ │ ├── manifest.js │ │ │ ├── package-changed.js │ │ │ └── plugin-config.js │ │ ├── grid-register.js │ │ ├── logger.js │ │ ├── logsink.js │ │ ├── main.js │ │ ├── schema │ │ │ ├── arg-spec.js │ │ │ ├── cli-args.js │ │ │ ├── cli-transformers.js │ │ │ ├── index.js │ │ │ ├── keywords.js │ │ │ └── schema.js │ │ └── utils.js │ ├── package.json │ ├── plugin.d.ts │ ├── plugin.js │ ├── sample-code │ │ ├── appium.config.sample.js │ │ ├── appium.config.sample.json │ │ ├── appium.config.sample.yaml │ │ ├── apps │ │ │ ├── ApiDemos-debug.apk │ │ │ └── TestApp.app.zip │ │ └── quickstarts │ │ │ ├── js │ │ │ ├── .gitignore │ │ │ ├── package.json │ │ │ └── test.js │ │ │ ├── py │ │ │ └── test.py │ │ │ └── rb │ │ │ ├── Gemfile │ │ │ └── test.rb │ ├── scripts │ │ ├── autoinstall-extensions.js │ │ ├── check-npm-pack-files.js │ │ └── parse-yml-commands.js │ ├── support.d.ts │ ├── support.js │ ├── test │ │ ├── e2e │ │ │ ├── args.e2e.spec.js │ │ │ ├── cli-driver.e2e.spec.js │ │ │ ├── cli-plugin.e2e.spec.js │ │ │ ├── config-file.e2e.spec.js │ │ │ ├── config.e2e.spec.js │ │ │ ├── driver.e2e.spec.js │ │ │ ├── e2e-helpers.js │ │ │ ├── local.e2e.spec.js │ │ │ ├── manifest.e2e.spec.js │ │ │ ├── plugin.e2e.spec.js │ │ │ └── schema.e2e.spec.js │ │ ├── fixtures │ │ │ ├── allow-feat.txt │ │ │ ├── caps.json │ │ │ ├── cli │ │ │ │ ├── appium-dependency.package.json │ │ │ │ ├── appium-file-dependency.package.json │ │ │ │ ├── appium-old-dependency.package.json │ │ │ │ ├── cli-error-output-boolean.txt │ │ │ │ ├── cli-error-output-color.txt │ │ │ │ ├── cli-error-output-unknown.txt │ │ │ │ └── cli-error-output.txt │ │ │ ├── config │ │ │ │ ├── allow-insecure.txt │ │ │ │ ├── appium-config-bad-nodeconfig.json │ │ │ │ ├── appium-config-bad.json │ │ │ │ ├── appium-config-driver-fake.json │ │ │ │ ├── appium-config-ext-good.json │ │ │ │ ├── appium-config-ext-unknown-props.json │ │ │ │ ├── appium-config-good-normalized.json │ │ │ │ ├── appium-config-good.js │ │ │ │ ├── appium-config-good.json │ │ │ │ ├── appium-config-good.yaml │ │ │ │ ├── appium-config-invalid.json │ │ │ │ ├── appium-config-log-filters.json │ │ │ │ ├── appium-config-plugin-fake.json │ │ │ │ ├── appium-config-security-array.json │ │ │ │ ├── appium-config-security-delimited.json │ │ │ │ ├── appium-config-security-path.json │ │ │ │ └── nodeconfig.json │ │ │ ├── default-args.js │ │ │ ├── deny-feat.txt │ │ │ ├── driver-schema.js │ │ │ ├── flattened-schema.js │ │ │ ├── log-filters.json │ │ │ ├── manifest │ │ │ │ ├── v2-empty.yaml │ │ │ │ ├── v2.yaml │ │ │ │ └── v3.yaml │ │ │ ├── plugin-schema.js │ │ │ ├── schema-with-extensions.js │ │ │ ├── test-driver-invalid-peer-dep │ │ │ │ └── package.json │ │ │ └── test-driver │ │ │ │ └── package.json │ │ ├── helpers.js │ │ └── unit │ │ │ ├── appiumdriver.spec.js │ │ │ ├── cli │ │ │ ├── cli.spec.js │ │ │ ├── schema-args.spec.js │ │ │ └── setup-command.spec.js │ │ │ ├── config-file.spec.js │ │ │ ├── config.spec.js │ │ │ ├── extension │ │ │ ├── driver-config.spec.js │ │ │ ├── extension-command.spec.js │ │ │ ├── extension-config.spec.js │ │ │ ├── manifest-migrations.spec.js │ │ │ ├── manifest.spec.js │ │ │ ├── mocks.js │ │ │ ├── package-changed.spec.js │ │ │ └── plugin-config.spec.js │ │ │ ├── grid-register.spec.js │ │ │ ├── logger.spec.js │ │ │ ├── parser.spec.js │ │ │ ├── schema │ │ │ ├── arg-spec.spec.js │ │ │ ├── cli-args.spec.js │ │ │ └── schema.spec.js │ │ │ └── utils.spec.js │ ├── tsconfig.json │ └── types │ │ ├── cli.ts │ │ ├── index.ts │ │ └── manifest │ │ ├── README.md │ │ ├── base.ts │ │ ├── index.ts │ │ ├── v3.ts │ │ └── v4.ts ├── base-driver │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ └── mjsonwp │ │ │ ├── errors.md │ │ │ └── protocol-methods.md │ ├── index.js │ ├── lib │ │ ├── basedriver │ │ │ ├── README.md │ │ │ ├── capabilities.ts │ │ │ ├── commands │ │ │ │ ├── bidi.ts │ │ │ │ ├── event.ts │ │ │ │ ├── execute.ts │ │ │ │ ├── find.ts │ │ │ │ ├── index.ts │ │ │ │ ├── log.ts │ │ │ │ ├── mixin.ts │ │ │ │ └── timeout.ts │ │ │ ├── core.ts │ │ │ ├── desired-caps.js │ │ │ ├── device-settings.js │ │ │ ├── driver.ts │ │ │ ├── helpers.js │ │ │ └── logger.js │ │ ├── constants.ts │ │ ├── express │ │ │ ├── README.md │ │ │ ├── crash.js │ │ │ ├── express-logging.js │ │ │ ├── idempotency.js │ │ │ ├── logger.js │ │ │ ├── middleware.js │ │ │ ├── server.js │ │ │ ├── static.js │ │ │ └── websocket.js │ │ ├── helpers │ │ │ ├── capabilities.js │ │ │ └── session.ts │ │ ├── index.js │ │ ├── jsonwp-proxy │ │ │ ├── README.md │ │ │ ├── protocol-converter.js │ │ │ └── proxy.js │ │ ├── jsonwp-status │ │ │ ├── README.md │ │ │ └── status.js │ │ └── protocol │ │ │ ├── README.md │ │ │ ├── bidi-commands.js │ │ │ ├── errors.js │ │ │ ├── helpers.js │ │ │ ├── index.js │ │ │ ├── protocol.js │ │ │ ├── routes.js │ │ │ └── validators.js │ ├── package.json │ ├── static │ │ ├── appium.png │ │ ├── favicon.ico │ │ ├── js │ │ │ └── jquery.min.js │ │ └── test │ │ │ ├── frameset.html │ │ │ ├── guinea-pig-app-banner.html │ │ │ ├── guinea-pig-scrollable.html │ │ │ ├── guinea-pig.html │ │ │ ├── guinea-pig2.html │ │ │ ├── guinea-pig3.html │ │ │ ├── guinea-pig4.html │ │ │ ├── guinea-pig5.html │ │ │ ├── iframes.html │ │ │ ├── shadow-dom.html │ │ │ ├── subframe1.html │ │ │ ├── subframe2.html │ │ │ ├── subframe3.html │ │ │ ├── touch.html │ │ │ └── welcome.html │ ├── test │ │ ├── e2e │ │ │ ├── basedriver │ │ │ │ ├── driver.e2e.spec.js │ │ │ │ ├── helpers.e2e.spec.js │ │ │ │ └── websockets.e2e.spec.js │ │ │ ├── express │ │ │ │ └── server.e2e.spec.js │ │ │ ├── fixtures │ │ │ │ ├── BadZippedApp.zip │ │ │ │ ├── FakeAndroidApp.apk │ │ │ │ ├── FakeAndroidApp.asd │ │ │ │ ├── FakeIOSApp.app │ │ │ │ ├── FakeIOSApp.app.zip │ │ │ │ ├── FakeIOSApp.ipa │ │ │ │ ├── custom-element-finder-bad.js │ │ │ │ └── custom-element-finder.js │ │ │ ├── jsonwp-proxy │ │ │ │ └── proxy.e2e.spec.js │ │ │ └── protocol │ │ │ │ ├── fake-driver.js │ │ │ │ ├── helpers.js │ │ │ │ └── protocol.e2e.spec.js │ │ ├── types │ │ │ └── basedriver │ │ │ │ └── driver.test-d.ts │ │ └── unit │ │ │ ├── basedriver │ │ │ ├── capabilities.spec.js │ │ │ ├── capability.spec.js │ │ │ ├── commands │ │ │ │ ├── event.spec.js │ │ │ │ └── log.spec.js │ │ │ ├── device-settings.spec.js │ │ │ ├── driver.spec.js │ │ │ ├── helpers.spec.js │ │ │ └── timeout.spec.js │ │ │ ├── express │ │ │ ├── server.spec.js │ │ │ └── static.spec.js │ │ │ ├── jsonwp-proxy │ │ │ ├── mock-request.js │ │ │ ├── protocol-converter.spec.js │ │ │ ├── proxy.spec.js │ │ │ └── url.spec.js │ │ │ ├── jsonwp-status │ │ │ └── status.spec.js │ │ │ └── protocol │ │ │ ├── errors.spec.js │ │ │ ├── routes.spec.js │ │ │ └── validator.spec.js │ └── tsconfig.json ├── base-plugin │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ └── plugin.js │ ├── package.json │ ├── test │ │ └── unit │ │ │ └── plugin.spec.js │ └── tsconfig.json ├── doctor │ ├── CHANGELOG.md │ ├── README.md │ ├── appium-doctor.js │ ├── bin │ │ └── appium-doctor.js │ ├── lib │ │ ├── android.js │ │ ├── demo.js │ │ ├── dev.js │ │ ├── doctor.js │ │ ├── env.js │ │ ├── factory.js │ │ ├── general.js │ │ ├── index.js │ │ ├── ios.js │ │ ├── logger.js │ │ ├── node-detector.js │ │ ├── prompt.js │ │ └── utils.js │ ├── package.json │ ├── test │ │ └── unit │ │ │ ├── android.spec.js │ │ │ ├── demo.spec.js │ │ │ ├── dev.spec.js │ │ │ ├── doctor.spec.js │ │ │ ├── factory.spec.js │ │ │ ├── fixtures │ │ │ └── wow.txt │ │ │ ├── general.spec.js │ │ │ ├── helper.js │ │ │ ├── index.spec.js │ │ │ ├── ios.spec.js │ │ │ ├── node-detector.spec.js │ │ │ ├── prompt.spec.js │ │ │ └── util.spec.js │ └── tsconfig.json ├── docutils │ ├── CHANGELOG.md │ ├── README.md │ ├── base-mkdocs.yml │ ├── bin │ │ └── appium-docs.js │ ├── index.js │ ├── lib │ │ ├── builder │ │ │ ├── deploy.ts │ │ │ ├── index.ts │ │ │ └── site.ts │ │ ├── cli │ │ │ ├── check.ts │ │ │ ├── command │ │ │ │ ├── build.ts │ │ │ │ ├── index.ts │ │ │ │ ├── init.ts │ │ │ │ └── validate.ts │ │ │ ├── config.ts │ │ │ └── index.ts │ │ ├── constants.ts │ │ ├── error.ts │ │ ├── fs.ts │ │ ├── index.ts │ │ ├── init.ts │ │ ├── logger.ts │ │ ├── model.ts │ │ ├── scaffold.ts │ │ ├── util.ts │ │ └── validate.ts │ ├── package.json │ ├── requirements.txt │ ├── test │ │ └── unit │ │ │ └── util.spec.js │ └── tsconfig.json ├── driver-test-support │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── e2e-suite.js │ │ ├── helpers.js │ │ ├── index.js │ │ ├── stoppable.ts │ │ └── unit-suite.js │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ └── stoppable.e2e.spec.ts │ │ └── unit │ │ │ └── index.spec.js │ └── tsconfig.json ├── eslint-config-appium-ts │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ └── package.json ├── eslint-config-appium │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ └── package.json ├── execute-driver-plugin │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── execute-child.js │ │ └── plugin.js │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ └── plugin.e2e.spec.js │ │ └── unit │ │ │ └── plugin.spec.js │ └── tsconfig.json ├── fake-driver │ ├── CHANGELOG.md │ ├── README.md │ ├── doctor │ │ ├── common.js │ │ ├── fake1.js │ │ └── fake2.js │ ├── lib │ │ ├── commands │ │ │ ├── alert.ts │ │ │ ├── contexts.ts │ │ │ ├── element.ts │ │ │ ├── find.ts │ │ │ ├── general.ts │ │ │ ├── index.ts │ │ │ └── mixin.ts │ │ ├── driver.js │ │ ├── fake-app.js │ │ ├── fake-driver-schema.js │ │ ├── fake-element.js │ │ ├── index.js │ │ ├── logger.js │ │ ├── screen.png │ │ ├── scripts │ │ │ ├── fake-error.js │ │ │ ├── fake-stdin.js │ │ │ └── fake-success.js │ │ ├── server.js │ │ └── types.ts │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ ├── alert-tests.js │ │ │ ├── context-tests.js │ │ │ ├── driver.e2e.spec.js │ │ │ ├── element-interaction-tests.js │ │ │ ├── find-element-tests.js │ │ │ └── general-tests.js │ │ ├── fixtures │ │ │ └── app.xml │ │ ├── helpers.js │ │ └── unit │ │ │ └── driver.spec.js │ └── tsconfig.json ├── fake-plugin │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── logger.js │ │ ├── plugin.js │ │ └── scripts │ │ │ ├── fake-error.js │ │ │ └── fake-success.js │ ├── package.json │ ├── test │ │ └── unit │ │ │ └── plugin.spec.js │ └── tsconfig.json ├── images-plugin │ ├── CHANGELOG.md │ ├── README.md │ ├── docs │ │ ├── find-by-image.md │ │ └── image-comparison.md │ ├── index.js │ ├── lib │ │ ├── compare.js │ │ ├── constants.js │ │ ├── finder.js │ │ ├── image-element.js │ │ ├── logger.js │ │ └── plugin.js │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ └── plugin.e2e.spec.js │ │ ├── fixtures │ │ │ ├── appstore.png │ │ │ ├── img1.png │ │ │ ├── img2.png │ │ │ ├── img2_part.png │ │ │ └── index.js │ │ └── unit │ │ │ ├── finder.spec.js │ │ │ ├── image-element.spec.js │ │ │ └── plugin.spec.js │ └── tsconfig.json ├── logger │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── index.ts │ ├── lib │ │ ├── log.ts │ │ ├── secure-values-preprocessor.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── package.json │ ├── test │ │ └── unit │ │ │ ├── basic-specs.js │ │ │ ├── display-specs.js │ │ │ └── preprocessor-specs.js │ └── tsconfig.json ├── opencv │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── autorelease-pool.js │ │ └── index.js │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ ├── images │ │ │ │ ├── appium-diagram.jpg │ │ │ │ ├── cc1.png │ │ │ │ ├── cc2.png │ │ │ │ ├── cc_rotated.png │ │ │ │ ├── findwaldo.jpg │ │ │ │ ├── full-image.b64 │ │ │ │ ├── number5.png │ │ │ │ └── waldo.jpg │ │ │ └── opencv.e2e.spec.js │ │ └── unit │ │ │ └── opencv.spec.js │ └── tsconfig.json ├── plugin-test-support │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ └── harness.js │ ├── package.json │ └── tsconfig.json ├── relaxed-caps-plugin │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ └── plugin.js │ ├── package.json │ ├── test │ │ └── unit │ │ │ └── plugin.spec.js │ └── tsconfig.json ├── schema │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── appium-config-schema.js │ │ ├── appium-config.schema.json │ │ └── index.js │ ├── package.json │ ├── scripts │ │ └── generate-schema-json.js │ └── tsconfig.json ├── strongbox │ ├── CHANGELOG.md │ ├── README.md │ ├── lib │ │ ├── base-item.ts │ │ ├── index.ts │ │ └── util.ts │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ └── strongbox.e2e.spec.ts │ │ ├── types │ │ │ └── strongbox.test-d.ts │ │ └── unit │ │ │ ├── base-item.spec.ts │ │ │ └── strongbox.spec.ts │ └── tsconfig.json ├── support │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── console.js │ │ ├── doctor.js │ │ ├── env.js │ │ ├── fs.js │ │ ├── image-util.js │ │ ├── index.ts │ │ ├── logger.js │ │ ├── logging.js │ │ ├── mjpeg.js │ │ ├── mkdirp.js │ │ ├── net.js │ │ ├── node.js │ │ ├── npm.js │ │ ├── plist.js │ │ ├── process.js │ │ ├── system.js │ │ ├── tempdir.js │ │ ├── timing.js │ │ ├── util.js │ │ └── zip.js │ ├── package.json │ ├── test │ │ ├── e2e │ │ │ ├── env.e2e.spec.js │ │ │ ├── fixture │ │ │ │ ├── appium-v1-dependency.package.json │ │ │ │ ├── appium-v1-package │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ │ ├── appium-v2-dependency.package.json │ │ │ │ ├── appium-v2-package │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ │ └── images │ │ │ │ │ └── full-image.b64 │ │ │ ├── fs.e2e.spec.js │ │ │ ├── image-util.e2e.spec.js │ │ │ ├── mjpeg.e2e.spec.js │ │ │ ├── net.e2e.spec.js │ │ │ ├── node.e2e.spec.js │ │ │ ├── npm.e2e.spec.js │ │ │ ├── util.e2e.spec.js │ │ │ └── zip.e2e.spec.js │ │ ├── helpers.js │ │ ├── mocks.js │ │ └── unit │ │ │ ├── assets │ │ │ ├── sample_binary.plist │ │ │ └── sample_text.plist │ │ │ ├── env.spec.js │ │ │ ├── fs.spec.js │ │ │ ├── index.spec.js │ │ │ ├── logger │ │ │ ├── helpers.js │ │ │ ├── logger-force.spec.js │ │ │ ├── logger-normal.spec.js │ │ │ └── logger-test.spec.js │ │ │ ├── node.spec.js │ │ │ ├── npm.spec.js │ │ │ ├── plist.spec.js │ │ │ ├── process.spec.js │ │ │ ├── system.spec.js │ │ │ ├── tempdir.spec.js │ │ │ ├── timing.spec.js │ │ │ └── util.spec.js │ └── tsconfig.json ├── test-support │ ├── CHANGELOG.md │ ├── README.md │ ├── bin │ │ ├── android-emu-travis-post.sh │ │ └── android-emu-travis-pre.sh │ ├── index.js │ ├── lib │ │ ├── env-utils.js │ │ ├── index.js │ │ ├── log-utils.js │ │ ├── logger.js │ │ ├── mock-utils.js │ │ ├── sandbox-utils.js │ │ ├── time-utils.js │ │ └── unhandled-rejection.js │ ├── package.json │ ├── test │ │ └── unit │ │ │ ├── env-utils.spec.js │ │ │ ├── fixtures │ │ │ └── ContactManager.apk │ │ │ ├── log-utils.spec.js │ │ │ ├── sandbox-utils.spec.js │ │ │ └── time-utils.spec.js │ └── tsconfig.json ├── tsconfig │ ├── CHANGELOG.md │ ├── README.md │ ├── package.json │ ├── tsconfig.json │ └── tsconfig.plugin.json ├── types │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ │ ├── action.ts │ │ ├── appium-config.ts │ │ ├── capabilities.ts │ │ ├── command.ts │ │ ├── config.ts │ │ ├── constraints.ts │ │ ├── doctor.ts │ │ ├── driver.ts │ │ ├── http.ts │ │ ├── index.ts │ │ ├── logger.ts │ │ ├── plugin.ts │ │ ├── server.ts │ │ ├── standard-caps.ts │ │ └── util.ts │ ├── package.json │ ├── scripts │ │ └── generate-schema-types.js │ ├── test-d │ │ └── plugin.test.ts │ └── tsconfig.json └── universal-xml-plugin │ ├── CHANGELOG.md │ ├── README.md │ ├── index.js │ ├── lib │ ├── attr-map.js │ ├── index.js │ ├── logger.js │ ├── node-map.js │ ├── plugin.js │ ├── source.js │ ├── transformers.js │ └── xpath.js │ ├── package.json │ ├── test │ ├── fixtures │ │ ├── android-transformed-path.xml │ │ ├── android-transformed.xml │ │ ├── android.xml │ │ ├── index.js │ │ ├── ios-edge.xml │ │ ├── ios-transformed-edge.xml │ │ ├── ios-transformed-path.xml │ │ ├── ios-transformed.xml │ │ ├── ios.xml │ │ └── web-view.xml │ └── unit │ │ ├── plugin.spec.js │ │ ├── source.spec.js │ │ └── xpath.spec.js │ └── tsconfig.json ├── renovate ├── README.md └── default.json ├── scripts └── install-fossa.sh ├── test └── setup.js ├── triagers.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/coverage/** 2 | **/node_modules/** 3 | examples/javascript-wd 4 | **/build/** 5 | **/*.min.js 6 | sample-code 7 | **/build-fixtures/** 8 | packages/appium/docs/**/assets/** 9 | packages/appium/docs/**/js/** 10 | -------------------------------------------------------------------------------- /.fossa.yml: -------------------------------------------------------------------------------- 1 | # Generated by FOSSA CLI (https://github.com/fossas/fossa-cli) 2 | # Visit https://fossa.com to learn more 3 | 4 | version: 3 5 | 6 | project: 7 | id: appium 8 | name: appium 9 | 10 | paths: 11 | exclude: 12 | - ./sample-code 13 | - '**/fixtures/**' 14 | - '**/demo/**' 15 | - '**/example/**' 16 | - '**/examples/**' 17 | 18 | targets: 19 | only: 20 | - type: npm 21 | path: . 22 | - type: npm 23 | path: ./packages/appium 24 | - type: npm 25 | path: ./packages/base-driver 26 | - type: npm 27 | path: ./packages/doctor 28 | - type: npm 29 | path: ./packages/eslint-config-appium 30 | - type: npm 31 | path: ./packages/fake-driver 32 | - type: npm 33 | path: ./packages/support 34 | - type: npm 35 | path: ./packages/test-support 36 | - type: npm 37 | path: ./packages/types 38 | - type: npm 39 | path: ./packages/schema 40 | - type: npm 41 | path: ./packages/universal-xmlplugin 42 | - type: npm 43 | path: ./packages/opencv 44 | - type: npm 45 | path: ./packages/images-plugin 46 | - type: npm 47 | path: ./packages/base-plugin 48 | - type: npm 49 | path: ./packages/relaxed-caps-plugin 50 | - type: npm 51 | path: ./packages/fake-plugin 52 | - type: npm 53 | path: ./packages/docutils 54 | 55 | revision: 56 | branch: master 57 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | # Handle as a text file but merge as binary. 4 | package-lock.json merge=binary 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: ❓ Question 4 | url: https://discuss.appium.io/ 5 | about: Please use the Appium forum for questions 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[Feat]:" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | # Configuration for Lock Threads - https://github.com/dessant/lock-threads 2 | 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 365 5 | 6 | # Skip issues and pull requests created before a given timestamp. Timestamp must 7 | # follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable 8 | skipCreatedBefore: false 9 | 10 | # Issues and pull requests with these labels will be ignored. Set to `[]` to disable 11 | exemptLabels: [] 12 | 13 | # Label to add before locking, such as `outdated`. Set to `false` to disable 14 | lockLabel: false 15 | 16 | # Comment to post before locking. Set to `false` to disable 17 | lockComment: > 18 | This thread has been automatically locked since there has not been 19 | any recent activity after it was closed. Please open a new issue for 20 | related bugs. 21 | 22 | # Assign `resolved` as the reason for locking. Set to `false` to disable 23 | setLockReason: false 24 | 25 | # Limit to only `issues` or `pulls` 26 | only: issues 27 | -------------------------------------------------------------------------------- /.github/workflows/auto-labeler.yml: -------------------------------------------------------------------------------- 1 | name: Labeler 2 | on: 3 | pull_request_target: 4 | types: [opened, synchronize] 5 | branches: [master] 6 | 7 | permissions: read-all 8 | 9 | jobs: 10 | labeler: 11 | permissions: 12 | pull-requests: write 13 | name: Auto-Label PRs 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: fuxingloh/multi-labeler@v4 17 | -------------------------------------------------------------------------------- /.github/workflows/clean-cache.yml: -------------------------------------------------------------------------------- 1 | name: Cache Cleanup 2 | 3 | # from https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#usage-limits-and-eviction-policy 4 | 5 | on: 6 | pull_request: 7 | types: 8 | - closed 9 | 10 | jobs: 11 | cleanup: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Check out code 15 | uses: actions/checkout@v4 16 | 17 | - name: Cleanup 18 | run: | 19 | gh extension install actions/gh-actions-cache 20 | 21 | REPO=${{ github.repository }} 22 | BRANCH="refs/pull/${{ github.event.pull_request.number }}/merge" 23 | 24 | echo "Fetching list of cache keys" 25 | cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) 26 | 27 | ## Setting this to not fail the workflow while deleting cache keys. 28 | set +e 29 | echo "Deleting caches..." 30 | for cacheKey in $cacheKeysForPR 31 | do 32 | gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm 33 | done 34 | echo "Done" 35 | env: 36 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/fossa.yml: -------------------------------------------------------------------------------- 1 | name: License Checks 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | permissions: 7 | contents: read 8 | 9 | jobs: 10 | fossa-check: 11 | name: Run FOSSA Analysis 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | ref: master 17 | - name: Install dependencies 18 | uses: bahmutov/npm-install@v1 19 | - name: Install fossa-cli 20 | run: ./scripts/install-fossa.sh -d 21 | - name: Run FOSSA analysis 22 | run: | 23 | fossa analyze 24 | fossa test 25 | env: 26 | FOSSA_API_KEY: ${{secrets.FOSSA_API_KEY}} 27 | -------------------------------------------------------------------------------- /.github/workflows/on-hold.yml: -------------------------------------------------------------------------------- 1 | name: Disallow "On Hold" PRs 2 | on: 3 | pull_request_target: 4 | types: [opened, labeled, unlabeled, synchronize] 5 | branches: [master] 6 | 7 | jobs: 8 | label-check: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Check for "On Hold" label 12 | uses: egmacke/action-check-label@v3 13 | with: 14 | label: On Hold 15 | state: absent 16 | -------------------------------------------------------------------------------- /.github/workflows/publish-doc.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Publish docs 5 | 6 | on: 7 | workflow_dispatch: 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [18.x] 17 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | - run: | 22 | git config user.name github-actions 23 | git config user.email github-actions@github.com 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | - name: Install dependencies (Node.js) 29 | uses: bahmutov/npm-install@v1 30 | with: 31 | useRollingCache: true 32 | install-command: npm ci 33 | - name: Install dependencies (Python) 34 | run: pip install -r packages/docutils/requirements.txt 35 | - name: Publish docs 36 | run: | 37 | cd packages/appium 38 | npm run publish:docs 39 | -------------------------------------------------------------------------------- /.github/workflows/renovate.yml: -------------------------------------------------------------------------------- 1 | name: Validate Renovate Configs 2 | on: 3 | push: 4 | branches: 5 | - master 6 | paths: 7 | - '.github/workflows/renovate.yml' # this file 8 | - '.renovaterc.json' 9 | - 'renovate/default.json' 10 | pull_request: 11 | branches: 12 | - master 13 | paths: 14 | - '.github/workflows/renovate.yml' # this file 15 | - '.renovaterc.json' 16 | - 'renovate/default.json' 17 | 18 | permissions: 19 | contents: read 20 | 21 | jobs: 22 | renovate: 23 | name: Check Renovate Configs 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Validate Renovate Config 28 | uses: rinchsan/renovate-config-validator@v0.2.0 29 | with: 30 | pattern: '{.renovaterc.json,renovate/default.json}' 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .irb-history 2 | *.swp 3 | .DS_Store 4 | node_modules 5 | *.trace 6 | xcuserdata 7 | *.sublime-* 8 | test/functional/_joined 9 | *.iml 10 | !examples/java/*.iml 11 | *.autosave 12 | .idea/ 13 | !examples/java/.idea/ 14 | *.log 15 | *.t 16 | artifacts 17 | input 18 | ./output 19 | lib/devices/android/bootstrap/bin/* 20 | lib/devices/android/bootstrap/local.properties 21 | lib/devices/android/bootstrap/project.properties 22 | lib/devices/android/bootstrap/.classpath 23 | lib/devices/android/bootstrap/target/ 24 | .*~ 25 | *~ 26 | org.eclipse.ltk.core.refactoring.prefs 27 | .appiumconfig* 28 | build/ 29 | .idea 30 | *DerivedData 31 | __pycache__ 32 | certificate.cert 33 | certificate.key 34 | 35 | # csharp 36 | *.pyc 37 | *.pdb 38 | .vs 39 | 40 | #vscode 41 | .history 42 | 43 | old 44 | .tern-project 45 | coverage 46 | docs/mkdocs.yml 47 | examples/javascript-wd/node_modules 48 | examples/php/vendor 49 | examples/php/composer.lock 50 | examples/java/out/ 51 | examples/ruby/Gemfile.lock 52 | examples/python/test/results/ 53 | examples/python/test/input 54 | examples/python/.pytest_cache/ 55 | examples/csharp/test/bin 56 | examples/csharp/test/obj 57 | examples/csharp/test/bin 58 | examples/csharp/packages 59 | appium.zip 60 | .vscode/settings.json 61 | .nyc_output 62 | *.lerna_backup 63 | **/build-fixtures/** 64 | *.tsbuildinfo 65 | packages/*/LICENSE 66 | \!packages/*/LICENSE 67 | local_appium_home 68 | *.tgz 69 | -------------------------------------------------------------------------------- /.lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.(js|ts)': ['eslint --fix', 'prettier --write'], 3 | 'appium-config-schema.js': () => [ 4 | 'npm run --workspace=./packages/schema build', 5 | 'git add -A packages/schema/lib/appium-config.schema.json', 6 | 'npm run --workspace=./packages/types build', 7 | 'git add -A packages/types/lib/appium-config.ts', 8 | ], 9 | '!(package|package-lock)*.json': ['prettier --write'], 10 | }; 11 | -------------------------------------------------------------------------------- /.mocharc.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | 'use strict'; 4 | 5 | module.exports = { 6 | require: [require.resolve('./test/setup.js')], 7 | // forbids use of .only() in CI 8 | forbidOnly: Boolean(process.env.CI), 9 | color: true, 10 | timeout: '5s', 11 | }; 12 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # Do not disable package-lock. Disabling package-lock also disables npm-shrinkwrap 2 | package-lock=true 3 | save-exact=true 4 | foreground-scripts=true 5 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | **/node_modules/** 2 | **/build/** 3 | **/fixtures/** 4 | **/*.min.* 5 | **/*.md 6 | package-lock.json 7 | package.json 8 | **/.vscode/** 9 | **/*.html 10 | **/generated/** 11 | # generated 12 | packages/types/lib/appium-config.ts 13 | **/*.hbs 14 | -------------------------------------------------------------------------------- /.renovaterc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>appium/appium//renovate/default"], 4 | "schedule": ["after 10pm and before 5:00am"], 5 | "timezone": "America/Vancouver" 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "stateful.runme" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "args": [ 5 | "--log-level=debug" 6 | ], 7 | "console": "integratedTerminal", 8 | "cwd": "${workspaceFolder}", 9 | "env": { 10 | "_FORCE_LOGS": "1" 11 | }, 12 | "internalConsoleOptions": "openOnSessionStart", 13 | "name": "Run Appium", 14 | "program": "${workspaceFolder}/node_modules/.bin/appium", 15 | "request": "launch", 16 | "skipFiles": [ 17 | "/**" 18 | ], 19 | "sourceMaps": true, 20 | "type": "node" 21 | }, 22 | { 23 | "args": [ 24 | "--colors", 25 | "--no-timeout", 26 | "${file}" 27 | ], 28 | "autoAttachChildProcesses": true, 29 | "console": "integratedTerminal", 30 | "cwd": "${workspaceFolder}", 31 | "internalConsoleOptions": "openOnSessionStart", 32 | "name": "Test Current File w/ Mocha", 33 | "program": "${workspaceFolder}/node_modules/.bin/mocha", 34 | "request": "launch", 35 | "runtimeArgs": [ 36 | "--require", 37 | "ts-node/register" 38 | ], 39 | "skipFiles": [ 40 | "/**", 41 | "**/node_modules/mocha/**", 42 | "**/node_modules/lodash/**", 43 | "**/node_modules/ts-node/**" 44 | ], 45 | "sourceMaps": true, 46 | "type": "node" 47 | } 48 | ], 49 | "version": "0.2.0" 50 | } 51 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "dev", 7 | "group": { 8 | "kind": "build", 9 | "isDefault": true 10 | }, 11 | "problemMatcher": [ 12 | "$tsc-watch" 13 | ], 14 | "label": "npm: dev", 15 | "detail": "npm run dev", 16 | "isBackground": true 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Authors ordered by first contribution 2 | 3 | Dan Cuellar 4 | E. James Infusino 5 | Jason Huggins 6 | Jason Carr 7 | Jayme Deffenbaugh 8 | Roman Salvador 9 | Luke Inman-Semerau 10 | Pradeep Bishnoi 11 | Charles Nowacek 12 | Jayakumar Chinnappan 13 | Robin Keller 14 | Adam Christian 15 | Jonathan Lipps 16 | Sebastian Tiedtke 17 | Christian Bromann 18 | Jeremy Avnet <> 19 | Bernard Kobos 20 | Santiago Suarez Ordoñez 21 | Joe Mathes <> 22 | -------------------------------------------------------------------------------- /RELEASE.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/RELEASE.pdf -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Docs moved 2 | 3 | The old Appium 1.x docs used to be in this directory. Now, they are available only on the [1.x 4 | branch](https://github.com/appium/appium/tree/1.x/docs). 5 | 6 | Appium 2.x docs are located in [packages/appium/docs](../packages/appium/docs) in this repo. 7 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/lerna", 3 | "changelogPreset": "conventional-changelog-conventionalcommits", 4 | "command": { 5 | "add": { 6 | "exact": true 7 | }, 8 | "publish": { 9 | "message": "chore: publish" 10 | }, 11 | "run": { 12 | "concurrency": 8 13 | }, 14 | "version": { 15 | "conventionalCommits": true, 16 | "createRelease": "github", 17 | "allowBranch": "master" 18 | } 19 | }, 20 | "ignoreChanges": ["**/test/**", "**/*.md"], 21 | "useNx": false, 22 | "version": "independent" 23 | } 24 | -------------------------------------------------------------------------------- /packages/appium/docs/.gitignore: -------------------------------------------------------------------------------- 1 | en/latest 2 | en/assets 3 | ja/assets 4 | zh/assets 5 | site 6 | -------------------------------------------------------------------------------- /packages/appium/docs/en/blog/.authors.yml: -------------------------------------------------------------------------------- 1 | authors: 2 | jlipps: 3 | name: "Jonathan Lipps" 4 | description: "Appium project lead. Learn more about Jonathan at https://jlipps.com" 5 | avatar: "https://gravatar.com/avatar/fdaadd825d92709bcf7fe76dffa12c40681c6108c67bcddf19478b2d00e3794b.jpg" 6 | slug: "jlipps" 7 | url: "https://jlipps.com" 8 | 9 | -------------------------------------------------------------------------------- /packages/appium/docs/en/blog/index.md: -------------------------------------------------------------------------------- 1 | # Appium Blog 2 | 3 | -------------------------------------------------------------------------------- /packages/appium/docs/en/blog/posts/hello-world.md: -------------------------------------------------------------------------------- 1 | --- 2 | authors: 3 | - jlipps 4 | date: 2024-03-07 5 | --- 6 | 7 | # Hello World! 8 | 9 | This is the first post in the Appium blog. There's not much to see here yet. We're creating this 10 | space so we can make announcements or post other news, or information about events, that don't 11 | really fit inside the documentation itself. 12 | 13 | 14 | 15 | Stay tuned for more! 16 | -------------------------------------------------------------------------------- /packages/appium/docs/en/cli/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Command-Line Overview 6 | --- 7 | 8 | Appium provides a command-line executable (`appium`), which will likely be your main way of interacting with 9 | the Appium server. This section of the Appium documentation describes how to use this executable. 10 | 11 | To start off, you can run `appium -v` or `appium --version` to return the installed version, 12 | or run `appium -h` or `appium --help` to return the help message. 13 | 14 | The main `appium` executable provides the following subcommands: 15 | 16 | 1. `appium server` (or simply `appium`) - launch an Appium server 17 | - [See here for accepted arguments](./args.md) 18 | - For advanced features, [see here for accepted environment variables](./env-vars.md) 19 | 2. `appium driver` - manage Appium drivers 20 | - [See here for details](./extensions.md) 21 | 3. `appium plugin` - manage Appium plugins 22 | - [See here for details](./extensions.md) 23 | 4. `appium setup` - batch install a preset of drivers and plugins 24 | - [See here for details](./setup.md) 25 | 26 | Like the main command, you can also run each subcommand with the `-h` or `--help` flag to learn more about it. 27 | -------------------------------------------------------------------------------- /packages/appium/docs/en/cli/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Setup Command-Line Usage 6 | --- 7 | 8 | The `setup` command aims to simplify the initial procedure of setting up Appium. It allows to install 9 | multiple extensions (drivers/plugins) in one go, without the need to run 10 | `appium install ` multiple times. 11 | 12 | The command has several presets that can be used to install different sets of extensions. 13 | The presets are as follows: 14 | 15 | |Preset|Installation Command|Included Drivers|Included Plugins| 16 | |--|--|--|--| 17 | |Mobile|`appium setup mobile` or `appium setup`|`uiautomator2`, `xcuitest`[^1], `espresso`|`images`| 18 | |Desktop application|`appium setup desktop`|`mac2`[^1], `windows`[^2]|`images`| 19 | |Desktop browser|`appium setup browser`|`safari`[^1], `gecko`, `chromium`|`images`| 20 | 21 | Attempting to install a preset while already having one or more of its included extensions installed 22 | will only install the missing extensions. 23 | 24 | Refer to the [Ecosystem documentation](../ecosystem/index.md) to learn more about the extensions 25 | listed above. 26 | 27 | [^1]: Only installed if the host machine is running macOS. 28 | [^2]: Only installed if the host machine is running Windows. 29 | -------------------------------------------------------------------------------- /packages/appium/docs/en/commands/execute-driver-plugin.md: -------------------------------------------------------------------------------- 1 | # Plugin: execute-driver 2 | 3 | ### `executeDriverScript` 4 | 5 | `POST` **`/session/:sessionId/appium/execute_driver`** 6 | 7 | Implementation of a command within a plugin 8 | 9 | At minimum, `D` must be `ExternalDriver`, but a plugin can be more narrow about which drivers it supports. 10 | 11 | 12 | 13 | #### Parameters 14 | 15 | | Name | Type | 16 | | :------ | :------ | 17 | | `script` | [script: string, scriptType: string, timeoutMs: number] | 18 | | `type?` | [script: string, scriptType: string, timeoutMs: number] | 19 | 20 | #### Response 21 | 22 | `unknown` 23 | -------------------------------------------------------------------------------- /packages/appium/docs/en/commands/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Intro to Commands 6 | --- 7 | 8 | Here you can find various commands exposed by the main Appium module through its base driver, as 9 | well as the commands available in several plugins. 10 | 11 | !!! note 12 | 13 | The Appium base driver only exposes a few commands, as it is not meant to be used on its own. 14 | Please refer to the documentation of your [Appium driver](../ecosystem/drivers.md) to learn 15 | about the commands it exposes, and then check your [Appium client](../ecosystem/clients.md) 16 | documentation for the exact syntax of that command. 17 | 18 | The command listings can be found here: 19 | 20 | * [Base Driver](./base-driver.md) 21 | * [Execute Driver Plugin](./execute-driver-plugin.md) 22 | * [Images Plugin](./images-plugin.md) 23 | * [Relaxed Caps Plugin](./relaxed-caps-plugin.md) 24 | * [Universal XML Plugin](./universal-xml-plugin.md) 25 | -------------------------------------------------------------------------------- /packages/appium/docs/en/commands/relaxed-caps-plugin.md: -------------------------------------------------------------------------------- 1 | # Plugin: relaxed-caps 2 | 3 | ### `createSession` 4 | 5 | `POST` **`/session`** 6 | 7 | Start a new automation session 8 | 9 | **`See`** 10 | 11 | [https://w3c.github.io/webdriver/#new-session](https://w3c.github.io/webdriver/#new-session) 12 | 13 | 14 | 15 | #### Parameters 16 | 17 | | Name | Type | Description | 18 | | :------ | :------ | :------ | 19 | | `desiredCapabilities?` | `any` | the new session capabilities | 20 | | `requiredCapabilities?` | `any` | another place the new session capabilities could be sent (typically left undefined) | 21 | | `capabilities?` | `any` | another place the new session capabilities could be sent (typically left undefined) | 22 | 23 | #### Response 24 | 25 | `any` 26 | 27 | The capabilities object representing the created session 28 | -------------------------------------------------------------------------------- /packages/appium/docs/en/developing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Intro to Development 6 | --- 7 | 8 | Appium 2 is built with a modular structure, which means that Appium extensions (drivers and plugins) 9 | are decoupled from the main Appium module, and you only need to install the extensions that you 10 | want to use. This modular structure also unlocks the ability to develop entirely new extensions! 11 | 12 | This section of the Appium documentation is intended to help aspiring developers with creating their 13 | own Appium extension: 14 | 15 | * For creating a driver, see the [Build Drivers](./build-drivers.md) page 16 | * For creating a plugin, take a look at the [Build Plugins](build-plugins.md) page 17 | * Drivers and plugins both need documentation, so check out the [Build Documentation](./build-docs.md) page 18 | * For creating a doctor check, see the [Building Doctor Checks](./build-doctor-checks.md) page 19 | -------------------------------------------------------------------------------- /packages/appium/docs/en/ecosystem/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Ecosystem Overview 6 | --- 7 | 8 | Appium has a wide ecosystem of related software and tools. This section of the Appium documentation 9 | aims to compile a listing of various officially-supported and community-supported Appium projects: 10 | 11 |
12 | 13 | - :material-car: __Drivers__ 14 | 15 | --- 16 | 17 | Link Appium to your test device 18 | 19 | [:octicons-arrow-right-24: View all drivers](./drivers.md) 20 | 21 | - :octicons-code-16: __Clients__ 22 | 23 | --- 24 | 25 | Link Appium to your automation code 26 | 27 | [:octicons-arrow-right-24: View all clients](./clients.md) 28 | 29 | - :fontawesome-solid-plug: __Plugins__ 30 | 31 | --- 32 | 33 | Modify and extend Appium functionality 34 | 35 | [:octicons-arrow-right-24: View all plugins](./plugins.md) 36 | 37 | - :material-wrench: __Tools__ 38 | 39 | --- 40 | 41 | Interact with Appium in other ways 42 | 43 | [:octicons-arrow-right-24: View all tools](./tools.md) 44 | 45 |
-------------------------------------------------------------------------------- /packages/appium/docs/en/quickstart/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Quickstart Intro 6 | --- 7 | 8 | Let's get up and running with Appium! To successfully use this quickstart, it's recommended that 9 | you first have read the [Introduction](../intro/index.md), so that you understand the concepts involved in 10 | running Appium and writing Appium scripts. 11 | 12 | The basic plan for this quickstart is as follows: 13 | 14 | 1. Install Appium 15 | 1. Install an Appium driver and its dependencies 16 | - This guide provides instructions for the [UiAutomator2 driver](https://github.com/appium/appium-uiautomator2-driver) 17 | 1. Install an Appium client library in your language of choice 18 | - This guide contains options for JavaScript, Python, Java, Ruby, and .NET 19 | 1. Write and run a simple Appium automation script using a sample application 20 | 21 | ### Requirements 22 | 23 | Before getting started, make sure your system satisfies the 24 | [requirements](../quickstart/requirements.md) for running the Appium server. Additional requirements 25 | will be discussed in conjunction with installing the UiAutomator2 driver. The guide also assumes 26 | you have basic command line proficiency on your platform, for example being able to run commands, set 27 | and persist environment variables, etc... 28 | 29 | Now you're ready to get started! So head on over to [Installing Appium](./install.md). 30 | -------------------------------------------------------------------------------- /packages/appium/docs/en/quickstart/next-steps.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Next Steps 6 | --- 7 | 8 | Now that you've successfully set up your system for Android automation and run a simple test, 9 | you'll want to continue exploring this documentation. In particular, these are good guides and 10 | reference materials especially for beginners: 11 | 12 | - The [Ecosystem](../ecosystem/index.md) page: browse the available drivers, clients, plugins, and tools 13 | - [Managing Appium Drivers and Plugins](../guides/managing-exts.md) 14 | - [Capabilities](../guides/caps.md) 15 | - [Settings](../guides/settings.md) 16 | 17 | You'll also find that the [Appium Inspector](https://github.com/appium/appium-inspector) is an 18 | indispensable tool for writing Appium tests, as it enables visual inspection of apps and 19 | helps you to discover element locators for use in your test scripts. 20 | 21 | You might also take advantage of one of the many online Appium courses available to you. 22 | 23 | Good luck and have fun! 24 | -------------------------------------------------------------------------------- /packages/appium/docs/en/quickstart/requirements.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: System Requirements 6 | --- 7 | 8 | The basic requirements for the Appium server are: 9 | 10 | * A macOS, Linux, or Windows operating system 11 | * [Node.js](https://nodejs.org) version in the [SemVer](https://semver.org) range `^14.17.0 || ^16.13.0 || >=18.0.0` 12 | * LTS is recommended 13 | * [`npm`](https://npmjs.com) version `>=8` (`npm` is usually bundled with Node.js, but can be upgraded 14 | independently) 15 | 16 | By itself, Appium is relatively lightweight and doesn't have significant disk space or RAM 17 | requirements. It can even be run in resource-constrained environments like Raspberry Pi, so long as 18 | Node.js is available. 19 | 20 | ### Driver Requirements 21 | 22 | Drivers for automating specific platforms will likely have other requirements. Refer to the 23 | documentation of the [Appium driver(s)](../ecosystem/drivers.md) for that platform for additional 24 | dependencies. It is almost universally the case that Appium drivers for a given platform will 25 | require the developer toolchain and SDKs for that platform to be installed. 26 | 27 | In order to assist with driver requirements, each (official) driver comes with the Appium Doctor tool, 28 | which allows to verify if all requirements have been set up. Learn more about how to use this tool in 29 | the [Command-Line Usage documentation](../cli/extensions.md#doctor). 30 | -------------------------------------------------------------------------------- /packages/appium/docs/en/quickstart/test-java.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Write a Test (Java) 6 | --- 7 | 8 | The Appium team maintains an official [client](https://github.com/appium/java-client) for the Java programming language. 9 | It is built on top of [Selenium](https://github.com/SeleniumHQ/selenium). 10 | You can also use this client in your Kotlin projects. 11 | 12 | Follow the [Add Appium java client to your test framework](https://github.com/appium/java-client#add-appium-java-client-to-your-test-framework) 13 | tutorial in order to connect the library to your test framework sources. 14 | 15 | The Appium Java client has dedicated classes to support most of the official Appium drivers. For other drivers 16 | you could simply use the [AppiumDriver](https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/AppiumDriver.java) class 17 | or build your custom derivatives from it. Check the [Drivers Support](https://github.com/appium/java-client#drivers-support) 18 | article to learn more about the current driver class implementations. 19 | 20 | Follow the [Usage Examples](https://github.com/appium/java-client#usage-examples) article in order understand 21 | how to invoke Java client features from your test framework. 22 | 23 | Once you've managed to successfully run a test, you can read on for some [next steps](./next-steps.md) to explore. -------------------------------------------------------------------------------- /packages/appium/docs/en/resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - navigation 4 | - toc 5 | 6 | title: Additional Resources 7 | --- 8 | 9 | Here you can find links to additional Appium resources around the web: 10 | 11 | ## Websites 12 | 13 | - [Appium Pro](https://appiumpro.com) - a blog and newsletter written by one of Appium's 14 | maintainers, Jonathan Lipps, with lots of useful guides 15 | 16 | ## Online Courses 17 | 18 | - [Appium and Selenium Fundamentals](https://ui.headspin.io/university/learn/appium-selenium-fundamentals-2020) - a comprehensive video course on learning Python, Selenium, and Appium by Jonathan Lipps 19 | - [Mobile Test Automation with Appium](https://testautomationu.applitools.com/appium-java-tutorial/) - a video course by Moataz Nabil 20 | - [Advanced Appium](https://www.linkedin.com/learning/advanced-appium) - a video course by Jonathan Lipps 21 | -------------------------------------------------------------------------------- /packages/appium/docs/ja/blog/index.md: -------------------------------------------------------------------------------- 1 | # Blog 2 | 3 | -------------------------------------------------------------------------------- /packages/appium/docs/mkdocs-ja.yml: -------------------------------------------------------------------------------- 1 | INHERIT: ./base-mkdocs.yml 2 | site_url: https://appium.io/docs/ja 3 | edit_uri: edit/master/packages/appium/docs/ja 4 | site_description: The Appium automation project documentation 5 | docs_dir: ja 6 | site_dir: site/docs/ja 7 | theme: 8 | language: ja 9 | nav: 10 | - はじめに: index.md 11 | - 導入: 12 | - Appiumについて: 13 | - Appiumの全体像: intro/index.md 14 | - Command Reference: 15 | - ../en/commands/base-driver.md 16 | - ../en/commands/execute-driver-plugin.md 17 | - ../en/commands/images-plugin.md 18 | - ../en/commands/relaxed-caps-plugin.md 19 | - ../en/commands/universal-xml-plugin.md 20 | - 手引き: 21 | - guides/migrating-1-to-2.md 22 | -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/appium-logo-horiz-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/appium-logo-horiz-white.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/appium-logo-horiz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/appium-logo-horiz.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/appium-logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/appium-logo-white.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/appium-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/appium-logo.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-dark.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/sponsor-logo-browserstack-light.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/sponsor-logo-headspin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/sponsor-logo-headspin.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/docs/overrides/assets/images/sponsor-logo-sauce.png -------------------------------------------------------------------------------- /packages/appium/docs/overrides/assets/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | .md-source__fact--version { 2 | display: none; 3 | } 4 | .md-source__fact:nth-child(1n + 2):before { 5 | margin-left: 0 !important; 6 | } 7 | 8 | .appium-sponsor-thanks { 9 | width: 100%; 10 | margin: 0.5rem 0.6rem auto 0.6rem; 11 | display: flex; 12 | align-items: center; 13 | } 14 | 15 | .appium-sponsor-thanks img { 16 | margin: auto 6px; 17 | height: 40px; 18 | } 19 | 20 | .homepageSponsors { 21 | display: flex; 22 | flex-direction: row; 23 | justify-content: center; 24 | align-items: center; 25 | margin: 10px auto; 26 | max-width: 800px; 27 | } 28 | 29 | .homepageSponsor { 30 | flex: 1; 31 | } 32 | -------------------------------------------------------------------------------- /packages/appium/docs/overrides/partials/sponsors.html: -------------------------------------------------------------------------------- 1 |
2 | Supported by 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | -------------------------------------------------------------------------------- /packages/appium/docs/scripts/utils.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | // for simplicity this file is not transpiled and is run directly via an npm script 4 | // 5 | const {logger} = require('@appium/support'); 6 | const path = require('path'); 7 | 8 | const log = logger.getLogger('Docs'); 9 | 10 | const DOCS_REMOTE = 'origin'; 11 | const DOCS_BRANCH = 'gh-pages'; 12 | const DOCS_PREFIX = 'docs'; 13 | const DOCS_DIR = path.resolve(__dirname, '..'); 14 | const LATEST_ALIAS = 'latest'; 15 | const ASSETS_DIR = path.join(DOCS_DIR, 'assets'); 16 | const LANGS = ['en', 'ja', 'zh']; 17 | const DEFAULT_LANG = 'en'; 18 | 19 | module.exports = { 20 | log, 21 | DOCS_DIR, 22 | ASSETS_DIR, 23 | LANGS, 24 | DEFAULT_LANG, 25 | DOCS_BRANCH, 26 | DOCS_PREFIX, 27 | DOCS_REMOTE, 28 | LATEST_ALIAS, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/blog/index.md: -------------------------------------------------------------------------------- 1 | # Blog 2 | 3 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/cli/args.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Server CLI Args 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/cli/extensions.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Extension CLI 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/cli/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 命令行介绍 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/contributing/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 为Appium做贡献 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/ecosystem/build-docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Building Docs for Appium Extensions 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/ecosystem/build-plugins.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Building Appium Plugins 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/ecosystem/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Appium生态系统 3 | --- 4 | 5 | 6 | TODO -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/caps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Capabilities 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/config.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Appium Config File 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/context.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Context API 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/event-timing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Event Timing API 3 | --- 4 | 5 | TODO -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/execute-script-overload.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Execute Script Overloads 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/grid.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Appium and Selenium Grid 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/log-filters.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Log Filtering 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/managing-exts.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Managing Appium Drivers and Plugins 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/security.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Appium Server Security 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/guides/settings.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Settings API 3 | --- 4 | 5 | TODO 6 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - navigation 4 | - toc 5 | 6 | title: 欢迎 7 | --- 8 | 13 |
14 | 15 |
16 | 17 | 欢迎访问 Appium 文档!Appium 是一个开源项目和相关软件的生态系统。 18 | 软件,旨在促进许多应用程序平台的用户界面自动化,包括移动平台(iOS、 19 | Android、Tizen)、浏览器(Chrome、Firefox、Safari)、桌面(macOS、Windows)、电视(Roku、tvOS、 20 | Android TV、三星)等! 21 | 22 | ## 探索文档 23 | 24 |
25 | 26 | - 查看 [__介绍__](./intro/index.md) 确保您理解关键概念 27 | - 通过 [__快速入门__](./quickstart/index.md) 来进行设置并运行基本的 Android 测试 28 | - 访问 [__生态系统__](./ecosystem/index.md) 页面,查看您可能需要使用的驱动程序、客户端和插件列表 29 | - 参考 [__CLI 参考__](./cli/index.md) 用于从命令行使用 Appium 30 | - 查看 [__命令说明__](./commands/index.md) Appium 和插件公开的命令列表 31 | - 阅读不同的 [__指南__](./guides/migrating-1-to-2.md) 来获取各种说明、技巧和窍门 32 | - 查阅各种第三方的 [__资源__](./resources.md) 来探索网上的Appium运用 33 | - 为了创建您自己的Appium拓展, 请查看 [__开发者__](./developing/index.md) 文档 34 | - 对 Appium 自身的贡献,请查阅 [__贡献__](./contributing/index.md) 页面 35 | 36 |
37 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/intro/drivers.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Intro to Appium Drivers 3 | --- 4 | 5 | 6 | TODO -------------------------------------------------------------------------------- /packages/appium/docs/zh/intro/history.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Appium 项目历史 3 | --- 4 | 5 | 6 | TODO -------------------------------------------------------------------------------- /packages/appium/docs/zh/intro/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: Appium in a Nutshell 6 | --- 7 | 正如主页上提到的那样,Appium旨在支持许多不同平台(移动、网络、桌面等)的UI自动化。不仅如此,它还旨在支持用不同语言(JS、Java、Python等)编写的自动化代码。将所有这些功能结合到一个程序中是一项非常艰巨、甚至不可能的任务! 8 | 9 | 为了实现这一目标,Appium实际上被分为四个部分: 10 | 11 |
12 | - :material-image-filter-center-focus-strong: __Appium Core__ - 定义核心API 13 | - :material-car: __Drivers__ - 实现与特定平台的连接 14 | - :octicons-code-16: __Clients__ - 实现与特定平台的连接 15 | - :fontawesome-solid-plug: __Plugins__ - 更改或扩展Appium的核心功能 16 |
17 | 18 | 因此,为了开始使用Appium自动化某些内容,您需要: 19 | - 安装Appium本身 20 | - 为您的目标平台安装驱动程序 21 | - 为您的目标编程语言安装客户端库 22 | - (可选)安装一个或多个插件 23 | 24 | 这些都是基础!如果您准备好加入,请继续[快速入门](../quickstart/index.md)! 25 | 26 | 如果您想了解有关其运作方式的更多详细信息,请参阅以下页面了解背景材料: 27 | 28 | - [Appium Core](./appium.md) 29 | - [Appium Drivers](./drivers.md) 30 | - [Appium Clients](./clients.md) 31 | 32 | 最后,要了解Appium的起源,请查看[Appium项目历史](./history.md)。 33 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/quickstart/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: 快速入门介绍 6 | --- 7 | 8 | 让我们开始使用 Appium!为了成功使用本快速入门介绍, 我们建议您 9 | 首先阅读 [介绍](../intro/index.md), 以便您了解 10 | 运行 Appium 和编写 Appium 脚本所涉及的概念。 11 | 12 | 本快速入门课程的基本计划如下: 13 | 14 | 1. 安装 Appium 15 | 1. 安装 Appium 驱动及其依赖项 (我们将使用 [UiAutomator2 16 | driver](https://github.com/appium/appium-uiautomator2-driver) 对于这些例子) 17 | 1. 用你选择的语言安装 Appium 客户端库(本指南包含以下选项 18 | JavaScript、Python、Java、Ruby 和 .NET 的选项)。 19 | 1. 使用示例应用程序编写并运行简单的 Appium 自动化脚本 20 | 21 | ### 要求 22 | 23 | 开始之前,请确保您的系统满足运行 Appium 服务器的 24 | [要求](../quickstart/requirements.md).其他要求 25 | 将在安装 UiAutomator2 驱动程序时一并讨论。 本指南还假设 26 | 您已熟练掌握您的操作系统平台上的基本命令行功能,例如可以运行命令行命令, 设置 27 | 永久的系统变量, 等等... 28 | 29 | 现在你已经准备好开始了!请前往 [安装 Appium](./install.md)吧. 30 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/quickstart/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: 安装 Appium 6 | --- 7 | 8 | !!! 信息 9 | 10 | 安装前,请务必检查[系统要求](./requirements.md). 11 | 12 | 您可以使用 `npm` 在全局范围内安装 Appium: 13 | 14 | ```bash 15 | npm i -g appium 16 | ``` 17 | 18 | !!! 注意 19 | 20 | 目前不支持其他软件包管理器。 21 | 22 | 安装完成后,您应该可以从命令行运行 Appium: 23 | 24 | ``` 25 | appium 26 | ``` 27 | 28 | 你应该会看到一些输出结果,开头一行是这样的: 29 | 30 | ``` 31 | [Appium] 欢迎来到 Appium v2.4.1 32 | ``` 33 | 34 | 为了更新Appiums使用 `npm`: 35 | 36 | ```bash 37 | npm update -g appium 38 | ``` 39 | 40 | 就是这样!如果你看到这个,说明 Appium 服务器已经启动并运行。按 (Ctrl-C) 41 | 继续退出并跳转到到 [下一步](./uiauto2-driver.md), 在这里我们将安装一个用于自动运行 Android 应用程序的驱动程序. -------------------------------------------------------------------------------- /packages/appium/docs/zh/quickstart/next-steps.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: 下一步 6 | --- 7 | 8 | 既然您已经成功配置了您的系统进行Android自动化并运行了一个简单的测试, 9 | 您可能希望继续探索这份文档。特别是对于初学者, 10 | 以下是一些很好的指南和参考资料: 11 | 12 | - 在 [生态系统](../ecosystem/index.md) 页面: 浏览可用的驱动程序, 客户端, 插件, 和工具 13 | - [管理Appium驱动程序与插件](../guides/managing-exts.md) 14 | - [能力](../guides/caps.md) 15 | - [设置](../guides/settings.md) 16 | 17 | 您还会发现[Appium 监测者](https://github.com/appium/appium-inspector) 编写 Appium 测试不可或缺的工具。 18 | 是编写 Appium 测试不可或缺的工具,因为它可以对应用程序进行可视化检查,并 19 | 帮助您发现可用于测试脚本的元素定位器。 20 | 21 | 您还可以利用为您提供的众多在线 Appium 课程之一。 22 | 23 | 祝你好运,玩得开心! 24 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/quickstart/test-java.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - toc 4 | 5 | title: 编写一个测试(Java) 6 | --- 7 | 8 | Appium 团队为 Java 编程语言维护一个官方[客户端](https://github.com/appium/java-client). 9 | 它是建立在 [Selenium](https://github.com/SeleniumHQ/selenium)上的. 10 | 您也可以在 Kotlin 项目中使用该客户端. 11 | 12 | 遵循[在测试框架中添加 Appium Java 客户端](https://github.com/appium/java-client#add-appium-java-client-to-your-test-framework) 13 | 教程,以便将该库连接到测试框架源。 14 | 15 | Appium Java 客户端有专门的类来支持大多数官方 Appium 驱动程序。对于其他驱动程序 16 | 您只需使用 [AppiumDriver](https://github.com/appium/java-client/blob/master/src/main/java/io/appium/java_client/AppiumDriver.java)类 17 | 或在此基础上创建您的自定义衍生工具. 请查看 [驱动支持](https://github.com/appium/java-client#drivers-support) 18 | 文章,了解有关当前驱动程序类实现的更多信息. 19 | 20 | 请参阅 [使用实例](https://github.com/appium/java-client#usage-examples) 一文,以了解 21 | 如何从测试框架调用 Java 客户端功能。 22 | 23 | 成功运行测试后,您可以继续阅读以下内容 [下一步骤](./next-steps.md) 继续探索 24 | -------------------------------------------------------------------------------- /packages/appium/docs/zh/resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - navigation 4 | - toc 5 | 6 | title: 其他资源 7 | --- 8 | 9 | 在这里,您可以找到网络上其他 Appium 资源的链接: 10 | 11 | ## 网站 12 | 13 | - [Appium Pro](https://appiumpro.com) - 是Appium其中一名维护者乔纳森-利普斯(Jonathan Lipps)撰写的博客和时事通讯。 14 | 撰写的博客和时事通讯,其中包含大量实用指南 15 | 16 | ## 在线课程 17 | 18 | - [Appium 和 Selenium 基础](https://ui.headspin.io/university/learn/appium-selenium-fundamentals-2020) - Jonathan Lipps 编写的学习 Python、Selenium 和 Appium 的综合视频课程 19 | - [使用 Appium 实现移动测试自动化](https://testautomationu.applitools.com/appium-java-tutorial/) - 由 Moataz Nabil 制作的视频课程 20 | - [高级Appoium](https://www.linkedin.com/learning/advanced-appium) - 乔纳森-利普斯(Jonathan Lipps)的视频课程 21 | -------------------------------------------------------------------------------- /packages/appium/driver.d.ts: -------------------------------------------------------------------------------- 1 | export * from '@appium/base-driver'; 2 | -------------------------------------------------------------------------------- /packages/appium/driver.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // @ts-check 4 | 5 | /** 6 | * This module is here to re-export `@appium/base-driver` for Appium extensions. 7 | * 8 | * @see https://npm.im/@appium/base-driver 9 | * @example 10 | * const { BaseDriver, errors } = require('appium/driver'); 11 | */ 12 | 13 | /** @type {import('@appium/base-driver')} */ 14 | module.exports = require('@appium/base-driver'); 15 | -------------------------------------------------------------------------------- /packages/appium/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {asyncify} = require('asyncbox'); 4 | 5 | const appium = require('./build/lib/main.js'); 6 | 7 | if (require.main === module) { 8 | asyncify(appium.main); 9 | } 10 | 11 | module.exports = appium; 12 | -------------------------------------------------------------------------------- /packages/appium/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from '@appium/support'; 2 | 3 | export const APPIUM_LOGGER_NAME = 'Appium'; 4 | let log = logger.getLogger(APPIUM_LOGGER_NAME); 5 | 6 | export default log; 7 | -------------------------------------------------------------------------------- /packages/appium/lib/schema/index.js: -------------------------------------------------------------------------------- 1 | export * from './schema'; 2 | export * from './cli-args'; 3 | -------------------------------------------------------------------------------- /packages/appium/plugin.d.ts: -------------------------------------------------------------------------------- 1 | export * from '@appium/base-plugin'; 2 | -------------------------------------------------------------------------------- /packages/appium/plugin.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // @ts-check 4 | 5 | /** 6 | * This module is here to re-export `@appium/base-plugin` for Appium extensions. 7 | * 8 | * @see https://npm.im/@appium/base-plugin 9 | * @example 10 | * const { BasePlugin } = require('appium/plugin'); 11 | */ 12 | 13 | module.exports = require('@appium/base-plugin'); 14 | -------------------------------------------------------------------------------- /packages/appium/sample-code/appium.config.sample.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | server: { 3 | address: '127.0.0.1', 4 | 'allow-cors': true, 5 | 'allow-insecure': ['foo', 'bar'], 6 | 'base-path': '/', 7 | 'callback-address': '127.0.0.1', 8 | 'callback-port': 4723, 9 | 'debug-log-spacing': true, 10 | 'default-capabilities': { 11 | key: 'value', 12 | }, 13 | 'deny-insecure': ['baz', 'quux'], 14 | driver: { 15 | xcuitest: { 16 | key: 'value', 17 | }, 18 | }, 19 | 'keep-alive-timeout': 600, 20 | 'local-timezone': true, 21 | log: '/tmp/appium.log', 22 | 'log-level': 'info', 23 | 'log-no-colors': false, 24 | 'log-timestamp': true, 25 | 'long-stacktrace': false, 26 | 'no-perms-check': false, 27 | nodeconfig: { 28 | key: 'value', 29 | }, 30 | plugin: { 31 | images: { 32 | key: 'value', 33 | }, 34 | }, 35 | port: 4723, 36 | 'relaxed-security': false, 37 | 'session-override': false, 38 | 'strict-caps': true, 39 | tmp: '/tmp', 40 | 'trace-dir': '/tmp/appium-instruments', 41 | 'use-drivers': ['foo', 'bar'], 42 | 'use-plugins': ['baz', 'quux'], 43 | webhook: 'https://some-url.com', 44 | }, 45 | }; 46 | -------------------------------------------------------------------------------- /packages/appium/sample-code/appium.config.sample.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "address": "127.0.0.1", 4 | "allow-cors": true, 5 | "allow-insecure": ["foo", "bar"], 6 | "base-path": "/", 7 | "callback-address": "127.0.0.1", 8 | "callback-port": 4723, 9 | "debug-log-spacing": true, 10 | "default-capabilities": { 11 | "key": "value" 12 | }, 13 | "deny-insecure": ["baz", "quux"], 14 | "driver": { 15 | "xcuitest": { 16 | "key": "value" 17 | } 18 | }, 19 | "keep-alive-timeout": 600, 20 | "local-timezone": true, 21 | "log": "/tmp/appium.log", 22 | "log-level": "info", 23 | "log-no-colors": false, 24 | "log-timestamp": true, 25 | "long-stacktrace": false, 26 | "no-perms-check": false, 27 | "nodeconfig": { 28 | "key": "value" 29 | }, 30 | "plugin": { 31 | "images": { 32 | "key": "value" 33 | } 34 | }, 35 | "port": 4723, 36 | "relaxed-security": false, 37 | "session-override": false, 38 | "strict-caps": true, 39 | "tmp": "/tmp", 40 | "trace-dir": "/tmp/appium-instruments", 41 | "use-drivers": ["foo", "bar"], 42 | "use-plugins": ["baz", "quux"], 43 | "webhook": "https://some-url.com" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/appium/sample-code/appium.config.sample.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | address: 127.0.0.1 3 | allow-cors: true 4 | allow-insecure: 5 | - foo 6 | - bar 7 | base-path: / 8 | callback-address: 127.0.0.1 9 | callback-port: 4723 10 | debug-log-spacing: true 11 | default-capabilities: 12 | key: value 13 | deny-insecure: 14 | - baz 15 | - quux 16 | driver: 17 | xcuitest: 18 | key: value 19 | keep-alive-timeout: 600 20 | local-timezone: true 21 | log: /tmp/appium.log 22 | log-level: info 23 | log-no-colors: false 24 | log-timestamp: true 25 | long-stacktrace: false 26 | no-perms-check: false 27 | nodeconfig: 28 | key: value 29 | plugin: 30 | images: 31 | key: value 32 | port: 4723 33 | relaxed-security: false 34 | session-override: false 35 | strict-caps: true 36 | tmp: /tmp 37 | trace-dir: /tmp/appium-instruments 38 | use-drivers: 39 | - foo 40 | - bar 41 | use-plugins: 42 | - baz 43 | - quux 44 | webhook: https://some-url.com 45 | -------------------------------------------------------------------------------- /packages/appium/sample-code/apps/ApiDemos-debug.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/sample-code/apps/ApiDemos-debug.apk -------------------------------------------------------------------------------- /packages/appium/sample-code/apps/TestApp.app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/sample-code/apps/TestApp.app.zip -------------------------------------------------------------------------------- /packages/appium/sample-code/quickstarts/js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | -------------------------------------------------------------------------------- /packages/appium/sample-code/quickstarts/js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "webdriverio": "8.39.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/appium/sample-code/quickstarts/js/test.js: -------------------------------------------------------------------------------- 1 | const {remote} = require('webdriverio'); 2 | 3 | const capabilities = { 4 | platformName: 'Android', 5 | 'appium:automationName': 'UiAutomator2', 6 | 'appium:deviceName': 'Android', 7 | 'appium:appPackage': 'com.android.settings', 8 | 'appium:appActivity': '.Settings', 9 | }; 10 | 11 | const wdOpts = { 12 | hostname: process.env.APPIUM_HOST || 'localhost', 13 | port: parseInt(process.env.APPIUM_PORT, 10) || 4723, 14 | logLevel: 'info', 15 | capabilities, 16 | }; 17 | 18 | async function runTest() { 19 | const driver = await remote(wdOpts); 20 | try { 21 | const batteryItem = await driver.$('//*[@text="Battery"]'); 22 | await batteryItem.click(); 23 | } finally { 24 | await driver.pause(1000); 25 | await driver.deleteSession(); 26 | } 27 | } 28 | 29 | runTest().catch(console.error); 30 | -------------------------------------------------------------------------------- /packages/appium/sample-code/quickstarts/py/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from appium import webdriver 3 | from appium.options.android import UiAutomator2Options 4 | from appium.webdriver.common.appiumby import AppiumBy 5 | 6 | capabilities = dict( 7 | platformName='Android', 8 | automationName='uiautomator2', 9 | deviceName='Android', 10 | appPackage='com.android.settings', 11 | appActivity='.Settings', 12 | language='en', 13 | locale='US' 14 | ) 15 | 16 | appium_server_url = 'http://localhost:4723' 17 | 18 | class TestAppium(unittest.TestCase): 19 | def setUp(self) -> None: 20 | self.driver = webdriver.Remote(appium_server_url, options=UiAutomator2Options().load_capabilities(capabilities)) 21 | 22 | def tearDown(self) -> None: 23 | if self.driver: 24 | self.driver.quit() 25 | 26 | def test_find_battery(self) -> None: 27 | el = self.driver.find_element(by=AppiumBy.XPATH, value='//*[@text="Battery"]') 28 | el.click() 29 | 30 | if __name__ == '__main__': 31 | unittest.main() 32 | -------------------------------------------------------------------------------- /packages/appium/sample-code/quickstarts/rb/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'appium_lib_core' 4 | gem 'test-unit' 5 | 6 | -------------------------------------------------------------------------------- /packages/appium/sample-code/quickstarts/rb/test.rb: -------------------------------------------------------------------------------- 1 | require 'appium_lib_core' 2 | require 'test/unit' 3 | 4 | CAPABILITIES = { 5 | platformName: 'Android', 6 | automationName: 'uiautomator2', 7 | deviceName: 'Android', 8 | appPackage: 'com.android.settings', 9 | appActivity: '.Settings', 10 | language: 'en', 11 | locale: 'US' 12 | } 13 | 14 | SERVER_URL = 'http://localhost:4723' 15 | 16 | class AppiumTest < Test::Unit::TestCase 17 | def setup 18 | @core = ::Appium::Core.for capabilities: CAPABILITIES 19 | @driver = @core.start_driver server_url: SERVER_URL 20 | end 21 | 22 | def teardown 23 | @driver&.quit 24 | end 25 | 26 | def test_version 27 | @driver.wait { |d| d.find_element :xpath, '//*[@text="Battery"]' }.click 28 | end 29 | end 30 | 31 | -------------------------------------------------------------------------------- /packages/appium/scripts/check-npm-pack-files.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path'); 4 | const childProcess = require('child_process'); 5 | const _ = require('lodash'); 6 | 7 | const res = JSON.parse( 8 | childProcess.execSync('npm pack --dry-run --json --ignore-scripts', { 9 | cwd: path.join(__dirname, '..'), 10 | encoding: 'utf8', 11 | }) 12 | )[0]; 13 | 14 | // List of files we are testing to make sure they are included in package 15 | const testFiles = [ 16 | 'LICENSE', // Check that license is included 17 | 'build/lib/appium.js', // Sanity check that build files are being included by testing just one file 18 | ]; 19 | 20 | // Get list of files in `testFiles` that aren't in the list of packaged fileNames 21 | const missingFiles = _.without(testFiles, ..._.map(res.files, 'path')); 22 | 23 | if (!_.isEmpty(missingFiles)) { 24 | throw new Error( 25 | `Files [${missingFiles.join(', ')}] are not included in package.json "files". ` + 26 | `Please make sure these files are included before publishing.` 27 | ); 28 | } 29 | 30 | process.exit(0); 31 | -------------------------------------------------------------------------------- /packages/appium/support.d.ts: -------------------------------------------------------------------------------- 1 | export * from '@appium/support'; 2 | -------------------------------------------------------------------------------- /packages/appium/support.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // @ts-check 4 | 5 | /** 6 | * This module is here to re-export `@appium/support` for Appium extensions. 7 | * 8 | * @see https://npm.im/@appium/support 9 | * @example 10 | * const { fs, npm } = require('appium/support'); 11 | */ 12 | 13 | module.exports = require('@appium/support'); 14 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/allow-feat.txt: -------------------------------------------------------------------------------- 1 | feature1 2 | feature2 3 | 4 | feature3 5 | 6 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/caps.json: -------------------------------------------------------------------------------- 1 | { 2 | "a": "b" 3 | } 4 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/appium-dependency.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "devDependencies": { 4 | "appium": "next" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/appium-file-dependency.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "dependencies": { 4 | "appium": "file:./appium/somewhere" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/appium-old-dependency.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "dependencies": { 4 | "appium": "next" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/cli-error-output-boolean.txt: -------------------------------------------------------------------------------- 1 | main.js server: error: argument --relaxed-security: ignored explicit argument 'sheep' 2 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/cli-error-output-color.txt: -------------------------------------------------------------------------------- 1 | > 1 | "sheep" 2 |   | ^^^^^^^ 👈🏽 type must be integer 3 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/cli-error-output-unknown.txt: -------------------------------------------------------------------------------- 1 | [ERROR] Unrecognized arguments: --pigs=sheep 2 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/cli/cli-error-output.txt: -------------------------------------------------------------------------------- 1 | > 1 | "sheep" 2 | | ^^^^^^^ 👈🏽 type must be integer 3 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/allow-insecure.txt: -------------------------------------------------------------------------------- 1 | foo 2 | bar 3 | baz 4 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-bad-nodeconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "nodeconfig": "./packages/appium/test/fixtures/config/nodeconfig.json" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-bad.json: -------------------------------------------------------------------------------- 1 | { 2 | "appium-home": "foo", 3 | "server": { 4 | "address": "0.0.0.0", 5 | "allow-cors": 1, 6 | "allow-insecure": {}, 7 | "base-path": "/", 8 | "callback-address": "0.0.0.0", 9 | "callback-port": 43243234, 10 | "debug-log-spacing": false, 11 | "default-capabilities": {}, 12 | "deny-insecure": [], 13 | "keep-alive-timeout": 0, 14 | "local-timezone": false, 15 | "log": "/tmp/appium.log", 16 | "log-level": "smoosh", 17 | "log-no-colors": 1, 18 | "log-timestamp": false, 19 | "long-stacktrace": false, 20 | "no-perms-check": false, 21 | "nodeconfig": {}, 22 | "port": "31337", 23 | "relaxed-security": false, 24 | "session-override": false, 25 | "strict-caps": false, 26 | "tmp": "/tmp", 27 | "trace-dir": "/tmp/appium-instruments", 28 | "use-drivers": [], 29 | "use-plugins": ["all"], 30 | "webhook": "http://0.0.0.0/hook" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-driver-fake.json: -------------------------------------------------------------------------------- 1 | { 2 | "driver": { 3 | "fake": { 4 | "sillyWebServerPort": 1234, 5 | "sillyWebServerHost": "hey" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-ext-good.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "driver": { 4 | "fake": { 5 | "answer": 24 6 | } 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-ext-unknown-props.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "driver": { 4 | "fake": { 5 | "answer": 24, 6 | "bubb": "rubb" 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-good-normalized.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "address": "0.0.0.0", 4 | "allow-cors": false, 5 | "allow-insecure": [], 6 | "base-path": "/", 7 | "callback-address": "0.0.0.0", 8 | "callback-port": 31337, 9 | "debug-log-spacing": false, 10 | "default-capabilities": {}, 11 | "deny-insecure": [], 12 | "keep-alive-timeout": 600, 13 | "local-timezone": false, 14 | "log": "/tmp/appium.log", 15 | "log-level": "info", 16 | "log-no-colors": false, 17 | "log-timestamp": false, 18 | "long-stacktrace": false, 19 | "no-perms-check": false, 20 | "nodeconfig": { 21 | "foo": "bar" 22 | }, 23 | "port": 31337, 24 | "relaxed-security": false, 25 | "session-override": false, 26 | "strict-caps": false, 27 | "tmp": "/tmp", 28 | "trace-dir": "/tmp/appium-instruments", 29 | "use-drivers": [], 30 | "use-plugins": ["all"], 31 | "webhook": "http://0.0.0.0/hook" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-good.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | server: { 3 | address: '0.0.0.0', 4 | 'allow-cors': false, 5 | 'allow-insecure': [], 6 | 'base-path': '/', 7 | 'callback-address': '0.0.0.0', 8 | 'callback-port': 31337, 9 | 'debug-log-spacing': false, 10 | 'default-capabilities': {}, 11 | 'deny-insecure': [], 12 | 'keep-alive-timeout': 600, 13 | 'local-timezone': false, 14 | log: '/tmp/appium.log', 15 | 'log-level': 'info', 16 | 'log-no-colors': false, 17 | 'log-timestamp': false, 18 | 'long-stacktrace': false, 19 | 'no-perms-check': false, 20 | nodeconfig: { 21 | foo: 'bar' 22 | }, 23 | port: 31337, 24 | 'relaxed-security': false, 25 | 'session-override': false, 26 | 'strict-caps': false, 27 | tmp: '/tmp', 28 | 'trace-dir': '/tmp/appium-instruments', 29 | 'use-drivers': [], 30 | 'use-plugins': ['all'], 31 | webhook: 'http://0.0.0.0/hook' 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-good.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "address": "0.0.0.0", 4 | "allow-cors": false, 5 | "allow-insecure": [], 6 | "base-path": "/", 7 | "callback-address": "0.0.0.0", 8 | "callback-port": 31337, 9 | "debug-log-spacing": false, 10 | "default-capabilities": {}, 11 | "deny-insecure": [], 12 | "keep-alive-timeout": 600, 13 | "local-timezone": false, 14 | "log": "/tmp/appium.log", 15 | "log-level": "info", 16 | "log-no-colors": false, 17 | "log-timestamp": false, 18 | "long-stacktrace": false, 19 | "no-perms-check": false, 20 | "nodeconfig": { 21 | "foo": "bar" 22 | }, 23 | "port": 31337, 24 | "relaxed-security": true, 25 | "session-override": false, 26 | "strict-caps": false, 27 | "tmp": "/tmp", 28 | "trace-dir": "/tmp/appium-instruments", 29 | "use-drivers": [], 30 | "use-plugins": ["all"], 31 | "webhook": "http://0.0.0.0/hook" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-good.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | address: '0.0.0.0' 3 | port: 31337 4 | allow-cors: false 5 | allow-insecure: [] 6 | base-path: / 7 | callback-address: '0.0.0.0' 8 | callback-port: 31337 9 | log-level: info 10 | log-timestamp: false 11 | debug-log-spacing: false 12 | default-capabilities: {} 13 | deny-insecure: [] 14 | keep-alive-timeout: 600 15 | local-timezone: false 16 | log: /tmp/appium.log 17 | log-no-colors: false 18 | long-stacktrace: false 19 | no-perms-check: false 20 | nodeconfig: 21 | foo: bar 22 | relaxed-security: true # not default 23 | session-override: false 24 | strict-caps: false 25 | tmp: /tmp 26 | trace-dir: /tmp/appium-instruments 27 | use-drivers: [] 28 | use-plugins: 29 | - all 30 | webhook: 'http://0.0.0.0/hook' 31 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "address": "0.0.0.0", 4 | "port": 31337, 5 | "allow-cors": false, 6 | "allow-insecure": [], 7 | "base-path": "/", 8 | "callback-address": "0.0.0.0", 9 | "callback-port": 31337, 10 | "log-level": "info", 11 | "log-timestamp": false, 12 | "debug-log-spacing": false, 13 | "default-capabilities": {}, 14 | "deny-insecure": [], 15 | "drivers": [], 16 | "keep-alive-timeout": 600, 17 | "local-timezone": false, 18 | "log": "/tmp/appium.log", 19 | "log-no-colors": false, 20 | "long-stacktrace": false, 21 | "no-perms-check": false, 22 | "nodeconfig": {}, 23 | "plugins": "all", 24 | "relaxed-security": false, 25 | "session-override": false, 26 | "strict-caps": false, 27 | "tmp": '/tmp', 28 | "trace-dir": "/tmp/appium-instruments", 29 | "webhook": "http://0.0.0.0/hook" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-log-filters.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "log-filters": [ 4 | { 5 | "text": "foo", 6 | "replacer": "bar" 7 | }, 8 | { 9 | "pattern": "/foo/", 10 | "flags": "i" 11 | } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-plugin-fake.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/appium/test/fixtures/config/appium-config-plugin-fake.json -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-security-array.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "allow-insecure": ["foo", "bar", "baz"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-security-delimited.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "allow-insecure": "foo,bar,baz" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/appium-config-security-path.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "allow-insecure": "./packages/appium/test/fixtures/config/allow-insecure.txt" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/config/nodeconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "random": "stuff" 3 | } 4 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/default-args.js: -------------------------------------------------------------------------------- 1 | export default { 2 | address: '0.0.0.0', 3 | allowCors: false, 4 | allowInsecure: [], 5 | basePath: '', 6 | callbackPort: 4723, 7 | debugLogSpacing: false, 8 | denyInsecure: [], 9 | driversImportChunkSize: 3, 10 | keepAliveTimeout: 600, 11 | localTimezone: false, 12 | logFormat: 'text', 13 | loglevel: 'debug', 14 | logNoColors: false, 15 | logTimestamp: false, 16 | longStacktrace: false, 17 | noPermsCheck: false, 18 | pluginsImportChunkSize: 7, 19 | port: 4723, 20 | relaxedSecurityEnabled: false, 21 | sessionOverride: false, 22 | strictCaps: false, 23 | useDrivers: [], 24 | usePlugins: [] 25 | }; 26 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/deny-feat.txt: -------------------------------------------------------------------------------- 1 | 2 | nofeature1 3 | nofeature2 4 | 5 | nofeature3 6 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/driver-schema.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | required: ['answer'], 4 | properties: { 5 | answer: { 6 | type: 'number', 7 | minimum: 0, 8 | maximum: 100, 9 | default: 50, 10 | description: 11 | 'The answer to the Ultimate Question of Life, The Universe, and Everything', 12 | }, 13 | }, 14 | $id: 'driver.json' 15 | }; 16 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/log-filters.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "text": "foo", 4 | "replacer": "bar" 5 | }, 6 | { 7 | "pattern": "/foo/", 8 | "flags": "i" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/manifest/v2-empty.yaml: -------------------------------------------------------------------------------- 1 | drivers: {} 2 | plugins: {} 3 | schemaRev: 2 4 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/manifest/v2.yaml: -------------------------------------------------------------------------------- 1 | drivers: 2 | fake: 3 | automationName: Fake 4 | platformNames: 5 | - Fake 6 | mainClass: FakeDriver 7 | schema: ./build/lib/fake-driver-schema.js 8 | scripts: 9 | fake-error: ./build/lib/scripts/fake-error.js 10 | fake-success: ./build/lib/scripts/fake-success.js 11 | pkgName: '@appium/fake-driver' 12 | version: 3.0.5 13 | installType: local 14 | installSpec: /Users/alice/projects/appium/packages/fake-driver 15 | plugins: 16 | fake: 17 | mainClass: FakePlugin 18 | scripts: 19 | fake-error: ./build/lib/scripts/fake-error.js 20 | fake-success: ./build/lib/scripts/fake-success.js 21 | pkgName: '@appium/fake-plugin' 22 | version: 1.2.2 23 | installType: local 24 | installSpec: /Users/alice/projects/appium/node_modules/@appium/fake-plugin 25 | schemaRev: 2 26 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/manifest/v3.yaml: -------------------------------------------------------------------------------- 1 | drivers: 2 | fake: 3 | automationName: Fake 4 | platformNames: 5 | - Fake 6 | mainClass: FakeDriver 7 | schema: ./build/lib/fake-driver-schema.js 8 | scripts: 9 | fake-error: ./build/lib/scripts/fake-error.js 10 | fake-success: ./build/lib/scripts/fake-success.js 11 | pkgName: '@appium/fake-driver' 12 | version: 3.0.5 13 | installType: local 14 | installSpec: /Users/alice/projects/appium/packages/fake-driver 15 | installPath: /Users/alice/projects/appium/packages/fake-driver 16 | plugins: 17 | fake: 18 | mainClass: FakePlugin 19 | scripts: 20 | fake-error: ./build/lib/scripts/fake-error.js 21 | fake-success: ./build/lib/scripts/fake-success.js 22 | pkgName: '@appium/fake-plugin' 23 | version: 1.2.2 24 | installType: local 25 | installSpec: /Users/alice/projects/appium/node_modules/@appium/fake-plugin 26 | installPath: /Users/alice/projects/appium/node_modules/@appium/fake-plugin 27 | schemaRev: 3 28 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/plugin-schema.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | type: 'object', 3 | required: ['answer'], 4 | properties: { 5 | answer: { 6 | type: 'number', 7 | minimum: 0, 8 | maximum: 100, 9 | default: 50, 10 | description: 11 | 'The answer to the Ultimate Question of Life, The Universe, and Everything', 12 | }, 13 | }, 14 | $id: 'plugin.json' 15 | }; 16 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/schema-with-extensions.js: -------------------------------------------------------------------------------- 1 | // this fixture combines the base config schema and the fake-driver schema, as would happen in a real use case. 2 | import _ from 'lodash'; 3 | import { AppiumConfigJsonSchema } from '@appium/schema'; 4 | 5 | // this must be a `require` because babel will not compile `node_modules` 6 | // TOOD: consider just copying this into fixtures so we can make it ESM 7 | const {default: fakeDriverSchema} = require('@appium/fake-driver/build/lib/fake-driver-schema'); 8 | 9 | const schema = _.cloneDeep(AppiumConfigJsonSchema); 10 | _.set(schema, 'properties.driver.properties.fake', fakeDriverSchema); 11 | 12 | export default schema; 13 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/test-driver-invalid-peer-dep/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-driver-invalid-peer-dep", 3 | "version": "1.0.0", 4 | "private": true, 5 | "appium": { 6 | "driverName": "test", 7 | "automationName": "Test", 8 | "platformNames": [ 9 | "Test" 10 | ], 11 | "mainClass": "TestDriver", 12 | "schema": { 13 | "type": "object", 14 | "properties": { 15 | "mcmonkey-mcbean": { 16 | "type": "string", 17 | "appiumCliIgnored": true 18 | }, 19 | "oliver-boliver": { 20 | "type": "number", 21 | "appiumDeprecated": true, 22 | "description": "funkytelechy" 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/appium/test/fixtures/test-driver/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@appium/test-driver", 3 | "version": "1.0.0", 4 | "private": true, 5 | "peerDependencies": { 6 | "appium": "^2.0.0-beta.42" 7 | }, 8 | "devDependencies": { 9 | "appium": "next" 10 | }, 11 | "appium": { 12 | "driverName": "test", 13 | "automationName": "Test", 14 | "platformNames": [ 15 | "Test" 16 | ], 17 | "mainClass": "TestDriver", 18 | "schema": { 19 | "type": "object", 20 | "properties": { 21 | "mcmonkey-mcbean": { 22 | "type": "string", 23 | "appiumCliIgnored": true 24 | }, 25 | "oliver-boliver": { 26 | "type": "number", 27 | "appiumDeprecated": true, 28 | "description": "funkytelechy" 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/appium/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "paths": { 7 | "@appium/support": ["../support"], 8 | "@appium/base-driver": ["../base-driver"], 9 | "@appium/base-plugin": ["../base-plugin"], 10 | "@appium/types": ["../types"], 11 | "@appium/schema": ["../schema"], 12 | "appium": ["."] 13 | }, 14 | "checkJs": true 15 | }, 16 | "include": ["lib", "types"], 17 | "references": [ 18 | {"path": "../schema"}, 19 | {"path": "../types"}, 20 | {"path": "../support"}, 21 | {"path": "../base-driver"}, 22 | {"path": "../base-plugin"}, 23 | {"path": "../test-support"} 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /packages/appium/types/index.ts: -------------------------------------------------------------------------------- 1 | import {ExtensionType, DriverType, DriverClass, PluginType, PluginClass} from '@appium/types'; 2 | 3 | export * from './manifest'; 4 | export * from './cli'; 5 | 6 | /** 7 | * Known environment variables concerning Appium 8 | */ 9 | export interface AppiumEnv extends NodeJS.ProcessEnv { 10 | APPIUM_HOME?: string; 11 | } 12 | 13 | /** 14 | * Generic to get at the class of an extension. 15 | */ 16 | export type ExtClass = ExtType extends DriverType 17 | ? DriverClass 18 | : ExtType extends PluginType 19 | ? PluginClass 20 | : never; 21 | -------------------------------------------------------------------------------- /packages/appium/types/manifest/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * See `README.md` for information on how to add a new version of the schema. 3 | * @module 4 | */ 5 | 6 | import * as ManifestV2 from './base'; 7 | import * as ManifestV3 from './v3'; 8 | import * as ManifestV4 from './v4'; 9 | // add `import * as ManifestV from './v';` above 10 | 11 | export * from './v4'; 12 | // replace above line with `export * from './v';` 13 | 14 | export {ManifestV2, ManifestV3, ManifestV4}; 15 | 16 | export interface ManifestDataVersions { 17 | 2: ManifestV2.ManifestData; 18 | 3: ManifestV3.ManifestData; 19 | 4: ManifestV4.ManifestData; 20 | } 21 | // append to this interface your new version of `ManifestData` 22 | 23 | /** 24 | * One of the known versions of the `extensions.yaml` schema. 25 | * 26 | * @privateRemarks You probably don't need to edit this. 27 | */ 28 | export type AnyManifestDataVersion = ManifestDataVersions[keyof ManifestDataVersions]; 29 | -------------------------------------------------------------------------------- /packages/base-driver/README.md: -------------------------------------------------------------------------------- 1 | # @appium/base-driver 2 | 3 | > Base class for creating other Appium drivers 4 | 5 | [![NPM version](http://img.shields.io/npm/v/@appium/base-driver.svg)](https://npmjs.org/package/@appium/base-driver) 6 | [![Downloads](http://img.shields.io/npm/dm/@appium/base-driver.svg)](https://npmjs.org/package/@appium/base-driver) 7 | 8 | This is the parent class that all Appium drivers inherit from. This driver should not be installed 9 | directly as it does nothing on its own. Instead, you should extend this driver when creating your 10 | *own* Appium drivers. Check out the [Building Drivers](https://appium.io/docs/en/latest/developing/build-drivers/) 11 | documentation for more details. 12 | 13 | Each included utility is documented in its own README: 14 | 15 | * [BaseDriver](lib/basedriver) 16 | * [The Appium Express Server](lib/express) 17 | * [The Mobile JSON Wire Protocol Encapsulation](lib/mjsonwp) 18 | * [The JSONWP Proxy Library](lib/jsonwp-proxy) 19 | * [The JSONWP Status Library](lib/jsonwp-status) 20 | 21 | ## License 22 | 23 | Apache-2.0 24 | -------------------------------------------------------------------------------- /packages/base-driver/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib'); 2 | -------------------------------------------------------------------------------- /packages/base-driver/lib/basedriver/commands/bidi.ts: -------------------------------------------------------------------------------- 1 | import type {Constraints, Driver, IBidiCommands} from '@appium/types'; 2 | import type {BaseDriver} from '../driver'; 3 | import {mixin} from './mixin'; 4 | 5 | declare module '../driver' { 6 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 7 | interface BaseDriver extends IBidiCommands {} 8 | } 9 | 10 | const BidiCommands: IBidiCommands = { 11 | async bidiSubscribe( 12 | this: BaseDriver, 13 | events: string[], 14 | contexts: string[] = [''], 15 | ) { 16 | for (const event of events) { 17 | this.bidiEventSubs[event] = contexts; 18 | } 19 | }, 20 | 21 | async bidiUnsubscribe( 22 | this: BaseDriver, 23 | events: string[], 24 | contexts: string[] = [''], 25 | ) { 26 | for (const event of events) { 27 | if (this.bidiEventSubs[event]) { 28 | this.bidiEventSubs[event] = this.bidiEventSubs[event].filter((c) => !contexts.includes(c)); 29 | } 30 | if (this.bidiEventSubs[event].length === 0) { 31 | delete this.bidiEventSubs[event]; 32 | } 33 | } 34 | }, 35 | }; 36 | 37 | mixin(BidiCommands); 38 | -------------------------------------------------------------------------------- /packages/base-driver/lib/basedriver/commands/index.ts: -------------------------------------------------------------------------------- 1 | import './event'; 2 | import './find'; 3 | import './log'; 4 | import './timeout'; 5 | import './execute'; 6 | import './bidi'; 7 | -------------------------------------------------------------------------------- /packages/base-driver/lib/basedriver/commands/log.ts: -------------------------------------------------------------------------------- 1 | import type {Constraints, Driver, ILogCommands} from '@appium/types'; 2 | import _ from 'lodash'; 3 | import type {BaseDriver} from '../driver'; 4 | import {mixin} from './mixin'; 5 | 6 | declare module '../driver' { 7 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 8 | interface BaseDriver extends ILogCommands {} 9 | } 10 | 11 | const LogCommands: ILogCommands = { 12 | supportedLogTypes: {}, 13 | 14 | async getLogTypes(this: BaseDriver) { 15 | this.log.debug('Retrieving supported log types'); 16 | return Object.keys(this.supportedLogTypes); 17 | }, 18 | 19 | async getLog(this: Driver, logType: string) { 20 | this.log.debug(`Retrieving '${String(logType)}' logs`); 21 | 22 | if (!(logType in this.supportedLogTypes)) { 23 | const logsTypesWithDescriptions = _.mapValues(this.supportedLogTypes, 'description'); 24 | throw new Error( 25 | `Unsupported log type '${String(logType)}'. ` + 26 | `Supported types: ${JSON.stringify(logsTypesWithDescriptions)}` 27 | ); 28 | } 29 | 30 | return await this.supportedLogTypes[logType].getter(this); 31 | }, 32 | }; 33 | 34 | mixin(LogCommands); 35 | -------------------------------------------------------------------------------- /packages/base-driver/lib/basedriver/commands/mixin.ts: -------------------------------------------------------------------------------- 1 | import {Constraints} from '@appium/types'; 2 | import {BaseDriver} from '../driver'; 3 | 4 | /** 5 | * This function assigns a mixin `T` to the `BaseDriver` class' prototype. 6 | * While each mixin has its own interface which is (in isolation) unrelated to `BaseDriver`, the constraint 7 | * on this generic type `T` is that it must be a partial of `BaseDriver`'s interface. This enforces 8 | * that it does not conflict with the existing interface of `BaseDriver`. In that way, you can 9 | * think of it as a type guard. 10 | * @param mixin Mixin implementation 11 | */ 12 | export function mixin>>(mixin: T): void { 13 | // eslint-disable-next-line no-restricted-syntax 14 | Object.assign(BaseDriver.prototype, mixin); 15 | } 16 | -------------------------------------------------------------------------------- /packages/base-driver/lib/basedriver/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from '@appium/support'; 2 | 3 | const log = logger.getLogger('BaseDriver'); 4 | export default log; 5 | -------------------------------------------------------------------------------- /packages/base-driver/lib/constants.ts: -------------------------------------------------------------------------------- 1 | import {util} from '@appium/support'; 2 | import {Protocol} from '@appium/types'; 3 | 4 | // The default maximum length of a single log record 5 | // containing http request/response body 6 | // This value could be globally customized using the --log-filters 7 | // server feature. Example rule: 8 | // {"pattern": "(.{1,150}).*", "flags": "s", "replacer": "$1"} 9 | // ^ cuts all log records to maximum 150 chars 10 | const MAX_LOG_BODY_LENGTH = 1024; 11 | const MJSONWP_ELEMENT_KEY = 'ELEMENT'; 12 | const W3C_ELEMENT_KEY = util.W3C_WEB_ELEMENT_IDENTIFIER; 13 | const PROTOCOLS = { 14 | W3C: 'W3C', 15 | MJSONWP: 'MJSONWP', 16 | } as const satisfies Record; 17 | 18 | // Before Appium 2.0, this default value was '/wd/hub' by historical reasons. 19 | const DEFAULT_BASE_PATH = ''; 20 | 21 | export {MAX_LOG_BODY_LENGTH, MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY, PROTOCOLS, DEFAULT_BASE_PATH}; 22 | -------------------------------------------------------------------------------- /packages/base-driver/lib/express/README.md: -------------------------------------------------------------------------------- 1 | ## appium-express 2 | 3 | [Express](http://expressjs.com/) server tuned for to serve [Appium](http://appium.io/). 4 | 5 | 6 | ### Configuration 7 | 8 | The `appium-express` server comes configured with: 9 | 10 | 1. appropriate logging formats 11 | 2. service of necessary static assets 12 | 3. allowance of cross-domain requests 13 | 4. default error handling 14 | 5. fix for invalid content types sent by certain clients 15 | 16 | To configure routes, a function that takes an Express server is passed into the 17 | server. This function can add whatever routes are wanted. 18 | 19 | 20 | ### Usage 21 | 22 | ```js 23 | import { server } from 'appium-base-driver'; 24 | 25 | 26 | // configure the routes 27 | function configureRoutes (app) { 28 | app.get('/hello', (req, res) => { 29 | res.header['content-type'] = 'text/html'; 30 | res.status(200).send('Hello'); 31 | }); 32 | app.get('/world', (req, res) => { 33 | res.header['content-type'] = 'text/html'; 34 | res.status(200).send('World'); 35 | }); 36 | } 37 | 38 | const port = 5000; 39 | const host = 'localhost'; 40 | 41 | const appiumServer = await server({ 42 | routeConfiguringFunction, 43 | port, 44 | host, 45 | }); 46 | ``` 47 | 48 | 49 | ## Watch 50 | 51 | ``` 52 | npm run watch 53 | ``` 54 | 55 | ## Test 56 | 57 | ``` 58 | npm test 59 | ``` 60 | -------------------------------------------------------------------------------- /packages/base-driver/lib/express/crash.js: -------------------------------------------------------------------------------- 1 | import {errors} from '../protocol'; 2 | 3 | function produceError() { 4 | throw new errors.UnknownCommandError('Produced generic error for testing'); 5 | } 6 | 7 | function produceCrash() { 8 | throw new Error('We just tried to crash Appium!'); 9 | } 10 | 11 | export {produceError, produceCrash}; 12 | -------------------------------------------------------------------------------- /packages/base-driver/lib/express/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from '@appium/support'; 2 | 3 | const log = logger.getLogger('HTTP'); 4 | export default log; 5 | -------------------------------------------------------------------------------- /packages/base-driver/lib/helpers/session.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Extract the first 8 characters of session ID to prefix the log. 3 | * 4 | * @param sessionId session identifier 5 | */ 6 | export function calcSignature(sessionId: string): string { 7 | return sessionId.substring(0, 8); 8 | } 9 | -------------------------------------------------------------------------------- /packages/base-driver/lib/jsonwp-status/README.md: -------------------------------------------------------------------------------- 1 | ## jsonwp-status 2 | 3 | Library of status codes for the Selenium [JSON Wire Protocol](https://github.com/SeleniumHQ/mobile-spec/blob/master/spec-draft.md). 4 | 5 | 6 | ### Usage 7 | 8 | ``` 9 | import { statusCodes } from 'appium-base-driver'; 10 | 11 | statusCodes.NoSuchContext; 12 | // -> {code: 35, summary: 'No such context found'} 13 | ``` 14 | 15 | ``` 16 | import { getSummaryByCode } from 'appium-base-driver'; 17 | 18 | getSummaryByCode(0); 19 | // -> 'The command executed successfully.' 20 | ``` 21 | -------------------------------------------------------------------------------- /packages/base-driver/lib/protocol/bidi-commands.js: -------------------------------------------------------------------------------- 1 | const SUBSCRIPTION_REQUEST_PARAMS = /** @type {const} */ ({ 2 | required: ['events'], 3 | optional: ['contexts'], 4 | }); 5 | 6 | const BIDI_COMMANDS = /** @type {const} */ ({ 7 | session: { 8 | subscribe: { 9 | command: 'bidiSubscribe', 10 | params: SUBSCRIPTION_REQUEST_PARAMS, 11 | }, 12 | unsubscribe: { 13 | command: 'bidiUnsubscribe', 14 | params: SUBSCRIPTION_REQUEST_PARAMS, 15 | }, 16 | }, 17 | browsingContext: { 18 | navigate: { 19 | command: 'bidiNavigate', 20 | params: { 21 | required: ['context', 'url'], 22 | optional: ['wait'], 23 | }, 24 | }, 25 | }, 26 | }); 27 | 28 | // TODO add definitions for all bidi commands. 29 | // spec link: https://w3c.github.io/webdriver-bidi/ 30 | 31 | export {BIDI_COMMANDS}; 32 | -------------------------------------------------------------------------------- /packages/base-driver/lib/protocol/helpers.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import {duplicateKeys} from '../basedriver/helpers'; 3 | import {MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY} from '../constants'; 4 | 5 | /** 6 | * Preprocesses the resulting value for API responses, 7 | * so they have keys for both W3C and JSONWP protocols. 8 | * The argument value is NOT mutated 9 | * 10 | * @param {?Object} resValue The actual response value 11 | * @returns {?Object} Either modified value or the same one if 12 | * nothing has been modified 13 | */ 14 | function formatResponseValue(resValue) { 15 | if (_.isUndefined(resValue)) { 16 | // convert undefined to null 17 | return null; 18 | } 19 | // If the MJSONWP element key format (ELEMENT) was provided, add a duplicate key (element-6066-11e4-a52e-4f735466cecf) 20 | // If the W3C element key format (element-6066-11e4-a52e-4f735466cecf) was provided, add a duplicate key (ELEMENT) 21 | return duplicateKeys(resValue, MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY); 22 | } 23 | 24 | /** 25 | * Properly formats the status for API responses, 26 | * so they are correct for the W3C protocol. 27 | * 28 | * @param {Object} responseBody 29 | * @returns {Object} The fixed response body 30 | */ 31 | function formatStatus(responseBody) { 32 | return _.isPlainObject(responseBody) ? _.omit(responseBody, ['status']) : responseBody; 33 | } 34 | 35 | export {MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY, formatResponseValue, formatStatus}; 36 | -------------------------------------------------------------------------------- /packages/base-driver/lib/protocol/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | isSessionCommand, 3 | routeConfiguringFunction, 4 | determineProtocol, 5 | CREATE_SESSION_COMMAND, 6 | DELETE_SESSION_COMMAND, 7 | GET_STATUS_COMMAND, 8 | makeArgs, 9 | checkParams, 10 | validateExecuteMethodParams, 11 | } from './protocol'; 12 | import {NO_SESSION_ID_COMMANDS, ALL_COMMANDS, METHOD_MAP, routeToCommandName} from './routes'; 13 | import {errors, isErrorType, errorFromMJSONWPStatusCode, errorFromW3CJsonCode} from './errors'; 14 | 15 | export { 16 | routeConfiguringFunction, 17 | errors, 18 | isErrorType, 19 | makeArgs, 20 | checkParams, 21 | validateExecuteMethodParams, 22 | errorFromMJSONWPStatusCode, 23 | errorFromW3CJsonCode, 24 | ALL_COMMANDS, 25 | METHOD_MAP, 26 | routeToCommandName, 27 | NO_SESSION_ID_COMMANDS, 28 | isSessionCommand, 29 | determineProtocol, 30 | CREATE_SESSION_COMMAND, 31 | DELETE_SESSION_COMMAND, 32 | GET_STATUS_COMMAND, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/base-driver/lib/protocol/validators.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | 3 | function isNumber(o) { 4 | return _.isNumber(o) || !_.isNaN(parseInt(o, 10)) || !_.isNaN(parseFloat(o)); 5 | } 6 | 7 | function msValidator(ms) { 8 | if (!_.isNumber(ms) || ms < 0) { 9 | throw new Error('Wait ms must be a number equal to 0 or greater'); 10 | } 11 | } 12 | 13 | const validators = { 14 | setUrl: (url) => { 15 | // either an `xyz://`, `about:`, or `data:` scheme is allowed 16 | if (!url || !url.match(/^([a-zA-Z0-9_+.-]+:\/\/)|(about:)|(data:)/)) { 17 | throw new Error('Url or Uri must start with ://'); 18 | } 19 | }, 20 | implicitWait: (ms) => { 21 | msValidator(ms); 22 | }, 23 | asyncScriptTimeout: (ms) => { 24 | msValidator(ms); 25 | }, 26 | clickCurrent: (button) => { 27 | if (!(isNumber(button) || _.isUndefined(button)) || button < 0 || button > 2) { 28 | throw new Error('Click button must be 0, 1, or 2'); 29 | } 30 | }, 31 | setNetworkConnection: (type) => { 32 | if (!isNumber(type) || [0, 1, 2, 4, 6].indexOf(type) === -1) { 33 | throw new Error('Network type must be one of 0, 1, 2, 4, 6'); 34 | } 35 | }, 36 | }; 37 | 38 | export {validators}; 39 | -------------------------------------------------------------------------------- /packages/base-driver/static/appium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/base-driver/static/appium.png -------------------------------------------------------------------------------- /packages/base-driver/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/base-driver/static/favicon.ico -------------------------------------------------------------------------------- /packages/base-driver/static/test/frameset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Frameset guinea pig 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/guinea-pig2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | I am another page title 5 | 6 | 7 | I am some other page content 8 |
I am another div
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/guinea-pig3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Another Page: page 3 5 | 6 | 7 | Page 3: I am some other page content 8 |
I am another div
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/guinea-pig4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Another Page: page 4 5 | 6 | 7 | Page 4: I am some other page content 8 |
I am another div
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/guinea-pig5.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Another Page: page 5 5 | 6 | 7 | Page 5: I am some other page content 8 |
I am another div
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/iframes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Iframe guinea pig 4 | 5 | 6 |

This is a page full o iframes!

7 | 9 | 11 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/shadow-dom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | I am a page title with shadow content 5 | 6 | 7 | 8 |

This page is a Shadow DOM sandbox

9 | 10 | 11 | 12 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/subframe1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sub frame 1 4 | 5 | 6 |

Sub frame 1

7 | Open a named window 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/subframe2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sub frame 2 4 | 5 | 6 |

Sub frame 2

7 | 8 | 9 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/subframe3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Sub frame 3 4 | 5 | 6 |

Sub frame 3

7 |

I have a child iframe

8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/base-driver/static/test/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Appium/welcome 5 | 6 | 7 | 8 |

<%= message %>

9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/basedriver/driver.e2e.spec.js: -------------------------------------------------------------------------------- 1 | import {BaseDriver} from '../../../lib'; 2 | import {driverE2ETestSuite} from '@appium/driver-test-support'; 3 | 4 | const DEFAULT_CAPS = { 5 | platformName: 'iOS', 6 | 'appium:deviceName': 'Delorean', 7 | }; 8 | 9 | driverE2ETestSuite(BaseDriver, DEFAULT_CAPS); 10 | 11 | /** 12 | * @typedef {import('@appium/driver-test-support').SessionHelpers} SessionHelpers 13 | */ 14 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/BadZippedApp.zip: -------------------------------------------------------------------------------- 1 | zip zip zip 2 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/FakeAndroidApp.apk: -------------------------------------------------------------------------------- 1 | this is not really an apk 2 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/FakeAndroidApp.asd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/base-driver/test/e2e/fixtures/FakeAndroidApp.asd -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/FakeIOSApp.app: -------------------------------------------------------------------------------- 1 | this is not really an app 2 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/FakeIOSApp.app.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/base-driver/test/e2e/fixtures/FakeIOSApp.app.zip -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/FakeIOSApp.ipa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/base-driver/test/e2e/fixtures/FakeIOSApp.ipa -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/custom-element-finder-bad.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | notFind: function () { // eslint-disable-line object-shorthand 3 | return []; 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/fixtures/custom-element-finder.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | find: function (driver, logger, selector, multiple) { // eslint-disable-line object-shorthand 3 | if (!driver || !driver.opts) { 4 | throw new Error('Expected driver object'); 5 | } 6 | 7 | if (!logger || !logger.info) { 8 | throw new Error('Expected logger object'); 9 | } 10 | 11 | if (selector === 'foo') { 12 | return ['bar']; 13 | } 14 | 15 | if (selector === 'foos') { 16 | if (multiple) { 17 | return ['baz1', 'baz2']; 18 | } 19 | 20 | return ['bar1', 'bar2']; 21 | } 22 | 23 | if (selector === 'error') { 24 | throw new Error('This is a plugin error'); 25 | } 26 | 27 | return []; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /packages/base-driver/test/e2e/protocol/helpers.js: -------------------------------------------------------------------------------- 1 | import Express from 'express'; 2 | import bodyParser from 'body-parser'; 3 | 4 | export function createProxyServer(sessionId, port) { 5 | // Start an express server for proxying 6 | let app = new Express(); 7 | app.use(bodyParser.json()); 8 | let server = app.listen(port); 9 | return {app, server}; 10 | } 11 | -------------------------------------------------------------------------------- /packages/base-driver/test/types/basedriver/driver.test-d.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable} from 'tsd'; 2 | // NOTE: this pulls in the distfiles 3 | import {BaseDriver} from '.../../..'; 4 | import {BaseDriverCapConstraints, ExternalDriver, Driver, DriverOpts} from '@appium/types'; 5 | 6 | expectAssignable>( 7 | new BaseDriver({} as DriverOpts) 8 | ); 9 | expectAssignable>( 10 | new BaseDriver({} as DriverOpts) 11 | ); 12 | -------------------------------------------------------------------------------- /packages/base-driver/test/unit/basedriver/driver.spec.js: -------------------------------------------------------------------------------- 1 | import BaseDriver from '../../../lib'; 2 | import {driverUnitTestSuite} from '@appium/driver-test-support'; 3 | 4 | const {expect} = chai; 5 | 6 | driverUnitTestSuite(BaseDriver, { 7 | platformName: 'iOS', 8 | 'appium:deviceName': 'Delorean', 9 | }); 10 | 11 | describe('BaseDriver', function () { 12 | describe('constructor', function () { 13 | it('should initialize "opts"', function () { 14 | const driver = new BaseDriver(); 15 | expect(driver.opts).to.exist; 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/base-driver/test/unit/express/static.spec.js: -------------------------------------------------------------------------------- 1 | // transpile:mocha 2 | 3 | import {welcome} from '../../../lib/express/static'; 4 | import {createSandbox} from 'sinon'; 5 | 6 | describe('welcome', function () { 7 | let sandbox; 8 | 9 | beforeEach(function () { 10 | sandbox = createSandbox(); 11 | }); 12 | 13 | afterEach(function () { 14 | sandbox.restore(); 15 | }); 16 | 17 | it('should fill the template', async function () { 18 | let res = { 19 | send: sandbox.stub(), 20 | }; 21 | await welcome({}, res); 22 | 23 | res.send.calledOnce.should.be.true; 24 | res.send.args[0][0].should.include("Let's browse!"); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/base-driver/test/unit/jsonwp-proxy/mock-request.js: -------------------------------------------------------------------------------- 1 | function resFixture(url, method) { 2 | if (/\/status$/.test(url)) { 3 | return [200, {status: 0, value: {foo: 'bar'}}]; 4 | } 5 | if (/\/element\/bad\/text$/.test(url)) { 6 | return [500, {status: 11, value: {message: 'Invisible element'}}]; 7 | } 8 | if (/\/element\/200\/text$/.test(url)) { 9 | return [200, {status: 11, value: {message: 'Invisible element'}}]; 10 | } 11 | if (/\/element\/200\/value$/.test(url)) { 12 | return [200, {status: 0, sessionId: 'innersessionid', value: 'foobar'}]; 13 | } 14 | if (/\/session$/.test(url) && method === 'POST') { 15 | return [200, {status: 0, sessionId: '123', value: {browserName: 'boo'}}]; 16 | } 17 | if (/\/nochrome$/.test(url)) { 18 | return [100, {status: 0, value: {message: 'chrome not reachable'}}]; 19 | } 20 | throw new Error("Can't handle url " + url); 21 | } 22 | 23 | // eslint-disable-next-line require-await 24 | async function request(opts) { 25 | const {url, method, json} = opts; 26 | if (/badurl$/.test(url)) { 27 | throw new Error('noworky'); 28 | } 29 | 30 | const [status, data] = resFixture(url, method, json); 31 | return { 32 | status, 33 | headers: {'content-type': 'application/json; charset=utf-8'}, 34 | data, 35 | }; 36 | } 37 | 38 | export default request; 39 | -------------------------------------------------------------------------------- /packages/base-driver/test/unit/jsonwp-status/status.spec.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import {statusCodes, getSummaryByCode} from '../../../lib'; 3 | 4 | describe('jsonwp-status', function () { 5 | describe('codes', function () { 6 | it('should export code numbers and summaries', function () { 7 | for (let obj of _.values(statusCodes)) { 8 | should.exist(obj.code); 9 | obj.code.should.be.a('number'); 10 | should.exist(obj.summary); 11 | obj.summary.should.be.a('string'); 12 | } 13 | }); 14 | }); 15 | describe('getSummaryByCode', function () { 16 | it('should get the summary for a code', function () { 17 | getSummaryByCode(0).should.equal('The command executed successfully.'); 18 | }); 19 | it('should convert codes to ints', function () { 20 | getSummaryByCode('0').should.equal('The command executed successfully.'); 21 | }); 22 | it('should return an error string for unknown code', function () { 23 | getSummaryByCode(1000).should.equal('An error occurred'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/base-driver/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "paths": { 6 | "@appium/support": ["../support"], 7 | "@appium/types": ["../types"], 8 | "@appium/driver-test-support": ["../driver-test-support"] 9 | }, 10 | "checkJs": true 11 | }, 12 | "include": ["lib"], 13 | "references": [{"path": "../support"}, {"path": "../types"}] 14 | } 15 | -------------------------------------------------------------------------------- /packages/base-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @appium/base-plugin 2 | 3 | > Base class for creating other Appium plugins 4 | 5 | [![NPM version](http://img.shields.io/npm/v/@appium/base-plugin.svg)](https://npmjs.org/package/@appium/base-plugin) 6 | [![Downloads](http://img.shields.io/npm/dm/@appium/base-plugin.svg)](https://npmjs.org/package/@appium/base-plugin) 7 | 8 | This is the parent class that all Appium plugins inherit from. This plugin should not be installed 9 | directly as it does nothing on its own. Instead, you should extend this plugin when creating your 10 | *own* Appium plugins. Check out the [Building Plugins](https://appium.io/docs/en/latest/developing/build-plugins/) 11 | documentation for more details. 12 | 13 | ## License 14 | 15 | Apache-2.0 16 | -------------------------------------------------------------------------------- /packages/base-plugin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/plugin'); 2 | -------------------------------------------------------------------------------- /packages/base-plugin/test/unit/plugin.spec.js: -------------------------------------------------------------------------------- 1 | import BasePlugin from '../../lib/plugin'; 2 | 3 | describe('base plugin', function () { 4 | it('should exist', function () { 5 | should.exist(BasePlugin); 6 | }); 7 | it('should define its name', function () { 8 | const p = new BasePlugin('foo'); 9 | p.name.should.eql('foo'); 10 | }); 11 | it('should create a logger', function () { 12 | const p = new BasePlugin('foo'); 13 | should.exist(p.logger); 14 | }); 15 | it('should define a default list of no new methods', function () { 16 | BasePlugin.newMethodMap.should.eql({}); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/base-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "checkJs": true, 6 | "strict": true, 7 | "paths": { 8 | "@appium/support": ["../support"], 9 | "@appium/types": ["../types"] 10 | } 11 | }, 12 | "include": ["lib"], 13 | "references": [{"path": "../support"}, {"path": "../types"}] 14 | } 15 | -------------------------------------------------------------------------------- /packages/doctor/README.md: -------------------------------------------------------------------------------- 1 | # @appium/doctor 2 | 3 | > Attempts to diagnose and fix common Appium configuration issues 4 | 5 | [![NPM version](http://img.shields.io/npm/v/@appium/doctor.svg)](https://npmjs.org/package/@appium/doctor) 6 | [![Downloads](http://img.shields.io/npm/dm/@appium/doctor.svg)](https://npmjs.org/package/@appium/doctor) 7 | 8 | > [!WARNING] 9 | > This package has been deprecated since the Appium server v 2.4.0 and will be removed in the future. 10 | > Use doctor checks (if any exist) integrated into your installed driver or plugin by 11 | > running `appium driver doctor ` or `appium plugin doctor `. 12 | 13 | ### Install 14 | 15 | ``` 16 | npm install @appium/doctor -g 17 | ``` 18 | 19 | ### Usage 20 | 21 | ``` 22 | ➜ appium-doctor -h 23 | 24 | Usage: appium-doctor.js [options, defaults: --ios --android] 25 | 26 | Options: 27 | --ios Check iOS setup [boolean] 28 | --android Check Android setup [boolean] 29 | --dev Check dev setup [boolean] 30 | --debug Show debug messages [boolean] 31 | --yes Always respond yes [boolean] 32 | --no Always respond no [boolean] 33 | --demo Run appium-doctor demo (for dev). [boolean] 34 | -h, --help Show help [boolean] 35 | ``` 36 | 37 | ## License 38 | 39 | Apache-2.0 40 | -------------------------------------------------------------------------------- /packages/doctor/appium-doctor.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | module.exports = require('./build/bin/appium-doctor'); 4 | -------------------------------------------------------------------------------- /packages/doctor/bin/appium-doctor.js: -------------------------------------------------------------------------------- 1 | // transpile:main 2 | 3 | import yargs from 'yargs'; 4 | import newDoctor from '../lib/factory'; 5 | import {configureBinaryLog} from '../lib/utils'; 6 | import {configure as configurePrompt} from '../lib/prompt'; 7 | import {system} from '@appium/support'; 8 | 9 | yargs 10 | .strict() 11 | .usage('Usage: $0 [options, defaults: --ios --android]') 12 | .boolean('ios') 13 | .describe('ios', 'Check iOS setup') 14 | .boolean('android') 15 | .describe('android', 'Check Android setup') 16 | .boolean('dev') 17 | .describe('dev', 'Check dev setup') 18 | .boolean('debug') 19 | .describe('debug', 'Show debug messages') 20 | .boolean('yes') 21 | .describe('yes', 'Always respond yes') 22 | .boolean('no') 23 | .describe('no', 'Always respond no') 24 | .boolean('demo') 25 | .describe('demo', 'Run appium-doctor demo (for dev).') 26 | .help('h') 27 | .alias('h', 'help') 28 | .check(function (argv) { 29 | if (!argv.ios && !argv.android && !argv.demo) { 30 | argv.ios = system.isMac(); 31 | argv.android = true; 32 | } 33 | return true; 34 | }); 35 | 36 | // make sure we use the general checks for every test 37 | let opts = Object.assign( 38 | { 39 | general: true, 40 | }, 41 | yargs.argv 42 | ); 43 | 44 | configurePrompt(opts); 45 | configureBinaryLog(opts); 46 | newDoctor(opts) 47 | .run() 48 | .catch(function (e) { 49 | console.error(e); // eslint-disable-line no-console 50 | process.exit(1); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/doctor/lib/factory.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash'; 2 | import {Doctor} from './doctor'; 3 | import generalChecks from './general'; 4 | import iosChecks from './ios'; 5 | import androidChecks from './android'; 6 | import devChecks from './dev'; 7 | import demoChecks from './demo'; 8 | 9 | /** 10 | * @type {DoctorGroup} 11 | */ 12 | const checks = {generalChecks, iosChecks, androidChecks, devChecks, demoChecks}; 13 | 14 | const newDoctor = (/** @type {Record} */ opts) => { 15 | const doctor = new Doctor(); 16 | for (const [k, v] of _.toPairs(opts)) { 17 | if (v) { 18 | doctor.register(checks[`${k}Checks`] || []); 19 | } 20 | } 21 | return doctor; 22 | }; 23 | 24 | export default newDoctor; 25 | 26 | /** 27 | * @typedef {import('./doctor').DoctorCheck[]} DoctorCheckList 28 | */ 29 | 30 | /** 31 | * @typedef DoctorGroup - Contain a group of Doctors 32 | * @property {DoctorCheckList} generalChecks - Check AppiumHome, NodeBinary, NodeVersion, ffmpeg, mjpeg-consumer 33 | * @property {DoctorCheckList} iosChecks - Check if iOS toolchains are installed 34 | * @property {DoctorCheckList} androidChecks - Check if Android toolchains are installed 35 | * @property {DoctorCheckList} devChecks - Check Path Binary and Android SDKs 36 | * @property {DoctorCheckList} demoChecks - Check /tmp/appium-doctor/demo/* 37 | */ 38 | -------------------------------------------------------------------------------- /packages/doctor/lib/index.js: -------------------------------------------------------------------------------- 1 | import newDoctor from './factory'; 2 | import {Doctor, DoctorCheck} from './doctor'; 3 | 4 | export {newDoctor, Doctor, DoctorCheck}; 5 | -------------------------------------------------------------------------------- /packages/doctor/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from '@appium/support'; 2 | 3 | const log = logger.getLogger('AppiumDoctor'); 4 | 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/doctor/lib/node-detector.js: -------------------------------------------------------------------------------- 1 | import log from './logger'; 2 | import {resolveExecutablePath} from './utils'; 3 | 4 | // Look for node 5 | export class NodeDetector { 6 | /** 7 | * @returns {Promise} 8 | */ 9 | static async retrieveUsingSystemCall() { 10 | const nodePath = await resolveExecutablePath('node'); 11 | 12 | if (!nodePath) { 13 | log.debug(`Node binary not found in PATH: ${process.env.PATH}`); 14 | return null; 15 | } 16 | 17 | log.debug(`Node binary found at: ${nodePath}`); 18 | return nodePath; 19 | } 20 | 21 | /** 22 | * @returns {Promise} 23 | */ 24 | static async detect() { 25 | const nodePath = await NodeDetector.retrieveUsingSystemCall(); 26 | if (nodePath) { 27 | return nodePath; 28 | } else { 29 | log.warn('The node binary could not be found.'); 30 | return null; 31 | } 32 | } 33 | } 34 | 35 | export default NodeDetector; 36 | -------------------------------------------------------------------------------- /packages/doctor/lib/prompt.js: -------------------------------------------------------------------------------- 1 | import { prompt } from './utils'; 2 | 3 | /** 4 | * @type {string|undefined} 5 | */ 6 | let persistentResponse; 7 | 8 | const fixItQuestion = { 9 | type: 'list', 10 | name: 'confirmation', 11 | message: 'Fix it:', 12 | choices: ['yes', 'no', 'always', 'never'], 13 | /** 14 | * 15 | * @param {string} val 16 | * @returns {string} 17 | */ 18 | filter(val) { 19 | return val.toLowerCase(); 20 | }, 21 | }; 22 | 23 | /** 24 | * @param {Record} opts 25 | */ 26 | export function configure(opts) { 27 | if (opts.yes) { 28 | persistentResponse = 'yes'; 29 | } 30 | if (opts.no) { 31 | persistentResponse = 'no'; 32 | } 33 | } 34 | 35 | /** 36 | * @returns {void} 37 | */ 38 | export function clear() { 39 | persistentResponse = undefined; 40 | } 41 | 42 | /** 43 | * @returns {Promise} 44 | */ 45 | export async function fixIt() { 46 | if (persistentResponse) { 47 | return persistentResponse; 48 | } 49 | const resp = await prompt(fixItQuestion); 50 | persistentResponse = resp.confirmation === 'always' ? 'yes' : persistentResponse; 51 | persistentResponse = resp.confirmation === 'never' ? 'no' : persistentResponse; 52 | return persistentResponse || resp.confirmation; 53 | } 54 | -------------------------------------------------------------------------------- /packages/doctor/test/unit/factory.spec.js: -------------------------------------------------------------------------------- 1 | import newDoctor from '../../lib/factory'; 2 | 3 | describe('factory', function () { 4 | function getTest(config) { 5 | return function runTest() { 6 | let doctor = newDoctor(config); 7 | doctor.should.exist; 8 | doctor.checks.should.have.length.above(0); 9 | }; 10 | } 11 | for (let config of [{ios: true}, {android: true}, {dev: true}]) { 12 | it(`should work for ${config}`, getTest(config)); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /packages/doctor/test/unit/fixtures/wow.txt: -------------------------------------------------------------------------------- 1 | WOW 2 | -------------------------------------------------------------------------------- /packages/doctor/test/unit/helper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Remove ANSI colors/styles 3 | * 4 | * @param {string} text 5 | * @returns {string} The text which has no ANSI colors/styles 6 | */ 7 | function removeColors(text) { 8 | // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings 9 | return text.replace( 10 | // eslint-disable-next-line no-control-regex 11 | /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, 12 | '' 13 | ); 14 | } 15 | 16 | export {removeColors}; 17 | -------------------------------------------------------------------------------- /packages/doctor/test/unit/index.spec.js: -------------------------------------------------------------------------------- 1 | import {newDoctor, Doctor, DoctorCheck} from '../../lib'; 2 | 3 | describe('index', function () { 4 | it('should work', function () { 5 | newDoctor.should.exist; 6 | Doctor.should.exist; 7 | DoctorCheck.should.exist; 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/doctor/test/unit/prompt.spec.js: -------------------------------------------------------------------------------- 1 | import {fixIt, clear} from '../../lib/prompt'; 2 | import * as utils from '../../lib/utils'; 3 | import {withMocks} from '@appium/test-support'; 4 | import B from 'bluebird'; 5 | 6 | describe( 7 | 'prompt', 8 | withMocks({utils}, (mocks) => { 9 | it('fixit - yes', async function () { 10 | clear(); 11 | mocks.utils 12 | .expects('prompt') 13 | .once() 14 | .returns(B.resolve({confirmation: 'yes'})); 15 | (await fixIt()).should.equal('yes'); 16 | mocks.verify(); 17 | }); 18 | 19 | it('fixit always ', async function () { 20 | clear(); 21 | mocks.utils 22 | .expects('prompt') 23 | .once() 24 | .returns(B.resolve({confirmation: 'always'})); 25 | (await fixIt()).should.equal('yes'); 26 | (await fixIt()).should.equal('yes'); 27 | (await fixIt()).should.equal('yes'); 28 | mocks.verify(); 29 | }); 30 | 31 | it('fixit never ', async function () { 32 | clear(); 33 | mocks.utils 34 | .expects('prompt') 35 | .once() 36 | .returns(B.resolve({confirmation: 'never'})); 37 | (await fixIt()).should.equal('no'); 38 | (await fixIt()).should.equal('no'); 39 | (await fixIt()).should.equal('no'); 40 | mocks.verify(); 41 | }); 42 | }) 43 | ); 44 | -------------------------------------------------------------------------------- /packages/doctor/test/unit/util.spec.js: -------------------------------------------------------------------------------- 1 | import {configureBinaryLog, resetLog} from '../../lib/utils'; 2 | import {fs} from '@appium/support'; 3 | import path from 'path'; 4 | import {Doctor} from '../../lib/doctor'; 5 | 6 | describe('utils', function () { 7 | it('fs.readFile', async function () { 8 | (await fs.readFile(path.resolve(__dirname, 'fixtures', 'wow.txt'), 'utf8')).should.include( 9 | 'WOW' 10 | ); 11 | }); 12 | 13 | it('fs.exists', async function () { 14 | (await fs.exists(path.resolve(__dirname, 'fixtures', 'wow.txt'))).should.be.ok; 15 | (await fs.exists(path.resolve(__dirname, 'fixtures', 'notwow.txt'))).should.not.be.ok; 16 | }); 17 | 18 | it('Should handle logs through onLogMessage callback', async function () { 19 | function onLogMessage(level, prefix, msg) { 20 | `${level} ${prefix} ${msg}`.should.include('AppiumDoctor'); 21 | } 22 | 23 | configureBinaryLog({onLogMessage}); 24 | let doctor = new Doctor(); 25 | try { 26 | await doctor.run(); 27 | } finally { 28 | resetLog(); 29 | } 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/doctor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "checkJs": true, 6 | "paths": { 7 | "@appium/support": ["../support"] 8 | } 9 | }, 10 | "include": ["lib", "bin"], 11 | "references": [{"path": "../support"}] 12 | } 13 | -------------------------------------------------------------------------------- /packages/docutils/README.md: -------------------------------------------------------------------------------- 1 | # @appium/docutils 2 | 3 | > Documentation-building utilities for Appium 4 | 5 | [![NPM version](http://img.shields.io/npm/v/@appium/docutils.svg)](https://npmjs.org/package/@appium/docutils) 6 | [![Downloads](http://img.shields.io/npm/dm/@appium/docutils.svg)](https://npmjs.org/package/@appium/docutils) 7 | 8 | ## Setup for Main Appium Docs 9 | 10 | This package does not need to be run directly. Instead, execute this command in the 11 | `packages/appium` directory: 12 | 13 | ```bash 14 | npm run dev:docs:en 15 | ``` 16 | 17 | > [!NOTE] 18 | > This will only build the English docs. See the `scripts` field of the `package.json` for 19 | other languages. 20 | 21 | ## Setup for Appium Drivers and Plugins 22 | 23 | See the Appium docs on [Building Documentation](http://appium.io/docs/en/latest/developing/build-docs/). 24 | 25 | ## License 26 | 27 | Apache-2.0 28 | -------------------------------------------------------------------------------- /packages/docutils/bin/appium-docs.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // @ts-check 3 | 4 | 'use strict'; 5 | require('source-map-support').install(); 6 | 7 | const {main} = require('../build/lib/cli'); 8 | const {getLogger} = require('../build/lib/logger'); 9 | 10 | const log = getLogger('cli'); 11 | 12 | // eslint-disable-next-line promise/prefer-await-to-then, promise/prefer-await-to-callbacks 13 | main().catch((err) => { 14 | log.error('Caught otherwise-unhandled rejection (this is probably a bug):', err); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/docutils/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib'); 2 | -------------------------------------------------------------------------------- /packages/docutils/lib/builder/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exports APIs used by build-related commands 3 | * @module 4 | */ 5 | 6 | export * from './deploy'; 7 | export * from './site'; 8 | -------------------------------------------------------------------------------- /packages/docutils/lib/cli/command/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Exports all command modules 3 | * @module 4 | */ 5 | 6 | export {default as init} from './init'; 7 | export {default as validate} from './validate'; 8 | export {default as build} from './build'; 9 | -------------------------------------------------------------------------------- /packages/docutils/lib/error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A custom error class. This exists so we can use `instanceof` to differentiate between "expected" 3 | * exceptions and unexpected ones. 4 | * @module 5 | */ 6 | 7 | export class DocutilsError extends Error {} 8 | -------------------------------------------------------------------------------- /packages/docutils/lib/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Main entry point 3 | * @module 4 | */ 5 | 6 | export * from './builder'; 7 | export * from './validate'; 8 | export * from './scaffold'; 9 | export * from './constants'; 10 | export * from './logger'; 11 | -------------------------------------------------------------------------------- /packages/docutils/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs==1.6.0 2 | mkdocs-git-revision-date-localized-plugin==1.2.6 3 | mkdocs-material==9.5.27 4 | mkdocs-redirects==1.2.1 5 | mike==2.1.1 6 | -------------------------------------------------------------------------------- /packages/docutils/test/unit/util.spec.js: -------------------------------------------------------------------------------- 1 | import {argify} from '../../lib/util'; 2 | import _ from 'lodash'; 3 | 4 | describe('argify', function () { 5 | it('should create args from params', function () { 6 | // deploy example 7 | const version = '2.0'; 8 | const mikeOpts = { 9 | 'config-file': '/path/to/yml', 10 | push: true, 11 | remote: 'origin', 12 | branch: 'gh-pages', 13 | 'deploy-prefix': '2.0', 14 | message: 'docs: a thing', 15 | port: 8100, 16 | host: 'localhost', 17 | }; 18 | const mikeArgs = [ 19 | ...argify( 20 | _.omitBy( 21 | mikeOpts, 22 | (value, key) => _.includes(['port', 'host'], key) || (!_.isNumber(value) && !value), 23 | ), 24 | ), 25 | version, 26 | ]; 27 | mikeArgs.should.eql([ 28 | '--config-file', 29 | '/path/to/yml', 30 | '--push', 31 | '--remote', 32 | 'origin', 33 | '--branch', 34 | 'gh-pages', 35 | '--deploy-prefix', 36 | '2.0', 37 | '--message', 38 | 'docs: a thing', 39 | '2.0', 40 | ]); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /packages/docutils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "checkJs": true, 7 | "paths": { 8 | "@appium/support": ["../support"], 9 | "@appium/types": ["../types"] 10 | }, 11 | "strict": true 12 | }, 13 | "include": ["./lib/**/*"], 14 | "references": [{"path": "../types"}, {"path": "../support"}] 15 | } 16 | -------------------------------------------------------------------------------- /packages/driver-test-support/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib'); 2 | -------------------------------------------------------------------------------- /packages/driver-test-support/lib/index.js: -------------------------------------------------------------------------------- 1 | export {createSessionHelpers, driverE2ETestSuite} from './e2e-suite'; 2 | export * from './unit-suite'; 3 | export * from './helpers'; 4 | 5 | // eslint-disable-next-line import/no-unresolved 6 | export * from './stoppable'; 7 | 8 | /** 9 | * @typedef {import('@appium/types').DriverClass} DriverClass 10 | * @typedef {import('@appium/types').BaseNSCapabilities} BaseNSCapabilities 11 | */ 12 | 13 | /** 14 | * @template {import('@appium/types').Constraints} C 15 | * @typedef {import('@appium/types').W3CCapabilities} W3CCapabilities 16 | */ 17 | -------------------------------------------------------------------------------- /packages/driver-test-support/test/e2e/stoppable.e2e.spec.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import B from 'bluebird'; 3 | import {Agent} from 'node:http'; 4 | import {startStoppableAppium, TestAppiumServer} from '../../lib'; 5 | import getPort from 'get-port'; 6 | 7 | const {expect} = chai; 8 | 9 | describe('startStoppableAppium()', function () { 10 | it('should start an Appium server', async function () { 11 | let server: TestAppiumServer | undefined; 12 | try { 13 | server = await startStoppableAppium({port: await getPort()}); 14 | expect(server, 'to be an object'); 15 | } finally { 16 | if (server) { 17 | await expect(server.stop()).to.be.fulfilled; 18 | } 19 | } 20 | }); 21 | 22 | describe('when the server has connections', function () { 23 | it('should stop the server and resolve with a boolean', async function () { 24 | const port = await getPort(); 25 | const server = await startStoppableAppium({port}); 26 | const getConnections = B.promisify(server.getConnections, {context: server}); 27 | await axios.get(`http://127.0.0.1:${port}/status`, { 28 | httpAgent: new Agent({keepAlive: true}), 29 | }); 30 | try { 31 | await expect(getConnections()).to.eventually.be.greaterThan(0); 32 | } finally { 33 | await expect(server.stop()).to.eventually.be.a('boolean'); 34 | } 35 | }); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /packages/driver-test-support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "checkJs": true, 7 | "paths": { 8 | "@appium/types": ["../types"], 9 | "appium/driver": ["../base-driver"] 10 | }, 11 | "types": ["mocha", "chai", "chai-as-promised"] 12 | }, 13 | "include": ["lib", "test"], 14 | "references": [{"path": "../types"}, {"path": "../base-driver"}] 15 | } 16 | -------------------------------------------------------------------------------- /packages/eslint-config-appium/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@appium/eslint-config-appium", 3 | "version": "8.0.5", 4 | "description": "Shared ESLint config for Appium projects", 5 | "keywords": [ 6 | "eslint", 7 | "eslintconfig", 8 | "appium" 9 | ], 10 | "homepage": "https://appium.io", 11 | "bugs": { 12 | "url": "https://github.com/appium/appium/issues" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/appium/appium.git", 17 | "directory": "packages/eslint-config-appium" 18 | }, 19 | "license": "Apache-2.0", 20 | "author": "https://github.com/appium", 21 | "main": "index.js", 22 | "files": [ 23 | "index.js" 24 | ], 25 | "scripts": { 26 | "eslint:find:all-rules": "eslint-find-rules -a", 27 | "eslint:find:current-rules": "eslint-find-rules -c", 28 | "eslint:find:plugin-rules": "eslint-find-rules -p", 29 | "eslint:find:unused-rules": "eslint-find-rules -u -n", 30 | "test:smoke": "node ./index.js" 31 | }, 32 | "peerDependencies": { 33 | "eslint": "^8.21.0", 34 | "eslint-config-prettier": "^8.5.0 || ^9.0.0", 35 | "eslint-plugin-import": "^2.26.0", 36 | "eslint-plugin-mocha": "^10.1.0", 37 | "eslint-plugin-promise": "^6.0.0" 38 | }, 39 | "engines": { 40 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0", 41 | "npm": ">=8" 42 | }, 43 | "publishConfig": { 44 | "access": "public" 45 | }, 46 | "gitHead": "8480a85ce2fa466360e0fb1a7f66628331907f02" 47 | } 48 | -------------------------------------------------------------------------------- /packages/execute-driver-plugin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/plugin'); 2 | -------------------------------------------------------------------------------- /packages/execute-driver-plugin/test/unit/plugin.spec.js: -------------------------------------------------------------------------------- 1 | import {ExecuteDriverPlugin} from '../../lib/plugin'; 2 | 3 | describe('execute driver plugin', function () { 4 | it('should exist', function () { 5 | should.exist(ExecuteDriverPlugin); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/execute-driver-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "checkJs": true, 5 | "module": "commonjs", 6 | "moduleResolution": "node10" 7 | }, 8 | "extends": "@appium/tsconfig/tsconfig.plugin.json", 9 | "include": ["lib"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/fake-driver/README.md: -------------------------------------------------------------------------------- 1 | # @appium/fake-driver 2 | 3 | > Fake Appium driver for internal testing 4 | 5 | ## License 6 | 7 | Apache-2.0 8 | -------------------------------------------------------------------------------- /packages/fake-driver/doctor/common.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const {doctor} = require('appium/support'); 3 | 4 | /** @satisfies {import('@appium/types').IDoctorCheck} */ 5 | class EnvVarAndPathCheck { 6 | /** 7 | * @param {string} varName 8 | */ 9 | constructor(varName) { 10 | this.varName = varName; 11 | } 12 | 13 | async diagnose() { 14 | return doctor.ok(`${this.varName} environment variable is always set because it's fake`); 15 | } 16 | 17 | async fix() { 18 | return ( 19 | `Make sure the environment variable ${this.varName} is properly configured for the Appium server process` 20 | ); 21 | } 22 | 23 | hasAutofix() { 24 | return false; 25 | } 26 | 27 | isOptional() { 28 | return false; 29 | } 30 | } 31 | 32 | module.exports = {EnvVarAndPathCheck}; 33 | -------------------------------------------------------------------------------- /packages/fake-driver/doctor/fake1.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const {EnvVarAndPathCheck} = require('./common'); 3 | 4 | const fakeCheck1 = new EnvVarAndPathCheck('FAKE1'); 5 | 6 | module.exports = {fakeCheck1}; 7 | -------------------------------------------------------------------------------- /packages/fake-driver/doctor/fake2.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const {EnvVarAndPathCheck} = require('./common'); 3 | 4 | const fakeCheck2 = new EnvVarAndPathCheck('FAKE2'); 5 | 6 | module.exports = {fakeCheck2}; 7 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/commands/mixin.ts: -------------------------------------------------------------------------------- 1 | import {FakeDriver} from '../driver'; 2 | 3 | /** 4 | * This function assigns a mixin `T` to the `FakeDriver` class' prototype. 5 | * While each mixin has its own interface which is (in isolation) unrelated to `FakeDriver`, the constraint 6 | * on this generic type `T` is that it must be a partial of `FakeDriver`'s interface. This enforces 7 | * that it does not conflict with the existing interface of `FakeDriver`. In that way, you can 8 | * think of it as a type guard. 9 | * @param mixin Mixin implementation 10 | */ 11 | export function mixin>(mixin: T): void { 12 | Object.assign(FakeDriver.prototype, mixin); 13 | } 14 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/fake-driver-schema.js: -------------------------------------------------------------------------------- 1 | // this could be a .json file, too: 2 | // { 3 | // "$schema": "http://json-schema.org/draft-07/schema", 4 | // "$id": "https://appium.io/fake-driver.json", 5 | // "type": "object", 6 | // "title": "Fake Driver Configuration", 7 | // "description": "A schema for Fake Driver arguments", 8 | // "properties": { 9 | // "answer": { 10 | // "type": "number", 11 | // "description": "The answer to life, the universe, and everything", 12 | // "default": 42 13 | // } 14 | // } 15 | // } 16 | 17 | export default { 18 | type: 'object', 19 | title: 'Fake Driver Configuration', 20 | description: 'A schema for Fake Driver arguments', 21 | properties: { 22 | // the prop name will always be normalized to camelCase 23 | 'silly-web-server-port': { 24 | type: 'integer', 25 | minimum: 1, 26 | maximum: 65535, 27 | description: 'The port to use for the fake web server', 28 | }, 29 | sillyWebServerHost: { 30 | type: 'string', 31 | description: 'The host to use for the fake web server', 32 | default: 'sillyhost', 33 | }, 34 | }, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/index.js: -------------------------------------------------------------------------------- 1 | import {FakeDriver} from './driver'; 2 | import {startServer} from './server'; 3 | 4 | const DEFAULT_HOST = 'localhost'; 5 | const DEFAULT_PORT = 4774; 6 | 7 | async function main() { 8 | const getArgValue = (/** @type {string} */ argName) => { 9 | const argIndex = process.argv.indexOf(argName); 10 | return argIndex > 0 ? process.argv[argIndex + 1] : null; 11 | }; 12 | const port = parseInt(String(getArgValue('--port')), 10) || DEFAULT_PORT; 13 | const host = getArgValue('--host') || DEFAULT_HOST; 14 | return await startServer(port, host); 15 | } 16 | 17 | export {FakeDriver, startServer, main}; 18 | 19 | /** 20 | * @typedef {import('./types').W3CFakeDriverCaps} W3CFakeDriverCaps 21 | * @typedef {import('./types').FakeDriverCaps} FakeDriverCaps 22 | */ 23 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from 'appium/support'; 2 | 3 | const log = logger.getLogger('FakeDriver'); 4 | 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/fake-driver/lib/screen.png -------------------------------------------------------------------------------- /packages/fake-driver/lib/scripts/fake-error.js: -------------------------------------------------------------------------------- 1 | import ora from 'ora'; 2 | 3 | const spinner = ora('Running fake-error...').start(); 4 | 5 | setTimeout(() => { 6 | spinner.fail('Oh nooooooo!'); 7 | throw Error('Unsuccessfully ran the script'); 8 | }, 1000); 9 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/scripts/fake-stdin.js: -------------------------------------------------------------------------------- 1 | import readline from 'node:readline'; 2 | 3 | const rl = readline.createInterface({input: process.stdin, output: process.stderr}); 4 | 5 | rl.question('Press ENTER to continue: ', () => { 6 | rl.close(); 7 | // eslint-disable-next-line no-console 8 | console.error('You did it!'); 9 | }); 10 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/scripts/fake-success.js: -------------------------------------------------------------------------------- 1 | import log from '../logger'; 2 | 3 | log.info('Successfully ran the script'); 4 | 5 | log.info(process.argv.slice(2).join(' ')); 6 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/server.js: -------------------------------------------------------------------------------- 1 | import log from './logger'; 2 | import {server as baseServer, routeConfiguringFunction} from 'appium/driver'; 3 | import {FakeDriver} from './driver'; 4 | 5 | async function startServer(port, hostname) { 6 | const d = new FakeDriver(); 7 | const server = await baseServer({ 8 | routeConfiguringFunction: routeConfiguringFunction(d), 9 | port, 10 | hostname, 11 | }); 12 | log.info(`FakeDriver server listening on http://${hostname}:${port}`); 13 | return server; 14 | } 15 | 16 | export {startServer}; 17 | -------------------------------------------------------------------------------- /packages/fake-driver/lib/types.ts: -------------------------------------------------------------------------------- 1 | import type {DriverCaps, W3CDriverCaps} from '@appium/types'; 2 | import type {FakeDriverConstraints} from './driver'; 3 | 4 | /** 5 | * W3C-style caps for {@link FakeDriver} 6 | * @public 7 | */ 8 | export type W3CFakeDriverCaps = W3CDriverCaps; 9 | 10 | /** 11 | * Capabilities for {@link FakeDriver} 12 | * @public 13 | */ 14 | export type FakeDriverCaps = DriverCaps; 15 | -------------------------------------------------------------------------------- /packages/fake-driver/test/helpers.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import {remote as wdio} from 'webdriverio'; 3 | 4 | const TEST_HOST = '127.0.0.1'; 5 | const TEST_PORT = 4774; 6 | 7 | const TEST_APP = path.join(__dirname, 'fixtures', 'app.xml'); 8 | 9 | const BASE_CAPS = { 10 | platformName: 'Fake', 11 | deviceName: 'Commodore 64', 12 | app: TEST_APP, 13 | address: TEST_HOST, 14 | port: 8181, 15 | }; 16 | 17 | const W3C_PREFIXED_CAPS = { 18 | 'appium:deviceName': BASE_CAPS.deviceName, 19 | 'appium:app': BASE_CAPS.app, 20 | 'appium:address': BASE_CAPS.address, 21 | 'appium:port': BASE_CAPS.port, 22 | platformName: BASE_CAPS.platformName, 23 | }; 24 | 25 | const W3C_CAPS = { 26 | alwaysMatch: {...W3C_PREFIXED_CAPS}, 27 | firstMatch: [{}], 28 | }; 29 | 30 | const WD_OPTS = { 31 | hostname: TEST_HOST, 32 | port: TEST_PORT, 33 | connectionRetryCount: 0, 34 | logLevel: 'error', 35 | }; 36 | 37 | async function initSession(w3cPrefixedCaps) { 38 | return await wdio({...WD_OPTS, capabilities: w3cPrefixedCaps}); 39 | } 40 | 41 | async function deleteSession(driver) { 42 | try { 43 | await driver.deleteSession(); 44 | } catch (ign) {} 45 | } 46 | 47 | export { 48 | initSession, 49 | deleteSession, 50 | TEST_APP, 51 | TEST_HOST, 52 | TEST_PORT, 53 | BASE_CAPS, 54 | W3C_CAPS, 55 | W3C_PREFIXED_CAPS, 56 | WD_OPTS, 57 | }; 58 | -------------------------------------------------------------------------------- /packages/fake-driver/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "checkJs": true, 6 | "paths": { 7 | "@appium/types": ["../types"], 8 | "appium/support": ["../appium/support"], 9 | "appium/driver": ["../appium/driver"], 10 | "@appium/driver-test-support": ["../driver-test-support"] 11 | } 12 | }, 13 | "include": ["lib"], 14 | "references": [ 15 | {"path": "../types"}, 16 | {"path": "../support"}, 17 | {"path": "../driver-test-support"}, 18 | {"path": "../base-driver"} 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /packages/fake-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @appium/fake-plugin 2 | 3 | > Fake Appium plugin for internal testing 4 | 5 | ## License 6 | 7 | Apache-2.0 8 | -------------------------------------------------------------------------------- /packages/fake-plugin/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = require('./build/lib/plugin'); 4 | -------------------------------------------------------------------------------- /packages/fake-plugin/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from 'appium/support'; 2 | 3 | const log = logger.getLogger('FakePlugin'); 4 | 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/fake-plugin/lib/scripts/fake-error.js: -------------------------------------------------------------------------------- 1 | throw Error('Unsuccessfully ran the script'); 2 | -------------------------------------------------------------------------------- /packages/fake-plugin/lib/scripts/fake-success.js: -------------------------------------------------------------------------------- 1 | import log from '../logger'; 2 | 3 | log.info('Successfully ran the script'); 4 | -------------------------------------------------------------------------------- /packages/fake-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "checkJs": true 5 | }, 6 | "extends": "@appium/tsconfig/tsconfig.plugin.json", 7 | "include": ["lib"], 8 | "references": [ 9 | { 10 | "path": "../base-plugin" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /packages/images-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @appium/images-plugin 2 | 3 | > Appium plugin for image comparison, visual testing, and image-based functional testing 4 | 5 | [![NPM version](http://img.shields.io/npm/v/@appium/images-plugin.svg)](https://npmjs.org/package/@appium/images-plugin) 6 | [![Downloads](http://img.shields.io/npm/dm/@appium/images-plugin.svg)](https://npmjs.org/package/@appium/images-plugin) 7 | 8 | ## Features 9 | 10 | 1. **Image Comparison** ([docs](./docs/image-comparison.md)) - A new Appium command and route that allows sending in two different images and comparing them in various ways. 11 | 2. **Finding Elements by Image** ([docs](./docs/find-by-image.md)) - Using a template image, find a matching screen region of an app and interact with it via standard Appium element semantics. 12 | 13 | ## Installation 14 | 15 | ``` 16 | appium plugin install images 17 | ``` 18 | 19 | The plugin must be explicitly activated when launching the Appium server: 20 | 21 | ``` 22 | appium --use-plugins=images 23 | ``` 24 | 25 | ## License 26 | 27 | Apache-2.0 28 | -------------------------------------------------------------------------------- /packages/images-plugin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/plugin'); 2 | -------------------------------------------------------------------------------- /packages/images-plugin/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from 'appium/support'; 2 | 3 | const log = logger.getLogger('ImageElementPlugin'); 4 | 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/images-plugin/test/fixtures/appstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/images-plugin/test/fixtures/appstore.png -------------------------------------------------------------------------------- /packages/images-plugin/test/fixtures/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/images-plugin/test/fixtures/img1.png -------------------------------------------------------------------------------- /packages/images-plugin/test/fixtures/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/images-plugin/test/fixtures/img2.png -------------------------------------------------------------------------------- /packages/images-plugin/test/fixtures/img2_part.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/images-plugin/test/fixtures/img2_part.png -------------------------------------------------------------------------------- /packages/images-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "checkJs": true, 5 | "esModuleInterop": true, 6 | "strict": false, 7 | "paths": { 8 | "@appium/opencv": ["../opencv"] 9 | } 10 | }, 11 | "extends": "@appium/tsconfig/tsconfig.plugin.json", 12 | "include": ["lib"], 13 | "references": [ 14 | { 15 | "path": "../opencv" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /packages/logger/LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright npm, Inc. 4 | 5 | Permission to use, copy, modify, and/or distribute this 6 | software for any purpose with or without fee is hereby 7 | granted, provided that the above copyright notice and this 8 | permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND NPM DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 12 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO 13 | EVENT SHALL NPM BE LIABLE FOR ANY SPECIAL, DIRECT, 14 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 17 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE 18 | USE OR PERFORMANCE OF THIS SOFTWARE. 19 | -------------------------------------------------------------------------------- /packages/logger/README.md: -------------------------------------------------------------------------------- 1 | # @appium/logger 2 | 3 | > Appium's logging functionality 4 | 5 | ## Installation 6 | 7 | ```console 8 | npm install @appium/logger --save 9 | ``` 10 | 11 | ## Basic Usage 12 | 13 | ```js 14 | import log from '@appium/logger'; 15 | 16 | // additional stuff ---------------------------+ 17 | // message ----------+ | 18 | // prefix ----+ | | 19 | // level -+ | | | 20 | // v v v v 21 | log.info('fyi', 'I have a kitty cat: %j', myKittyCat); 22 | ``` 23 | 24 | ## History 25 | 26 | This module is forked from [npmlog](https://github.com/npm/npmlog) under ISC License because the original project has been archived. 27 | Please check [the npmlog changelog](https://github.com/npm/npmlog/blob/main/CHANGELOG.md) to see the list of former module updates before it was forked. 28 | 29 | ## License 30 | 31 | ISC License 32 | -------------------------------------------------------------------------------- /packages/logger/index.ts: -------------------------------------------------------------------------------- 1 | import log from './lib/log'; 2 | export type * from './lib/types'; 3 | 4 | export {log}; 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/logger/lib/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This function is necessary to workaround unexpected memory leaks 3 | * caused by NodeJS string interning 4 | * behavior described in https://bugs.chromium.org/p/v8/issues/detail?id=2869 5 | * 6 | * @param {any} s - The string to unleak 7 | * @return {string} Either the unleaked string or the original object converted to string 8 | */ 9 | export function unleakString(s: any): string { 10 | return ` ${s}`.substring(1); 11 | } 12 | -------------------------------------------------------------------------------- /packages/logger/test/unit/display-specs.js: -------------------------------------------------------------------------------- 1 | import { Log } from '../../lib/log'; 2 | import { waitForCondition } from 'asyncbox'; 3 | 4 | describe('display', function () { 5 | let log; 6 | 7 | describe('explicitly set new log level display to empty string', function () { 8 | let actual; 9 | 10 | beforeEach(function () { 11 | actual = ''; 12 | log = new Log(); 13 | log.write = (msg) => { 14 | actual += msg; 15 | }; 16 | }); 17 | 18 | it('explicitly set new log level display to empty string', async function () { 19 | log.addLevel('explicitNoLevelDisplayed', 20000, {}, ''); 20 | log.explicitNoLevelDisplayed('1', '2'); 21 | await waitForCondition(() => actual.trim() === '1 2', {waitMs: 1000, intervalMs: 50}); 22 | 23 | actual = ''; 24 | log.explicitNoLevelDisplayed('', '1'); 25 | await waitForCondition(() => actual.trim() === '1', {waitMs: 1000, intervalMs: 50}); 26 | }); 27 | 28 | it('explicitly set new log level display to 0', async function () { 29 | log.addLevel('explicitNoLevelDisplayed', 20000, {}, 0); 30 | log.explicitNoLevelDisplayed('', '1'); 31 | await waitForCondition(() => actual.trim() === '0 1', {waitMs: 1000, intervalMs: 50}); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /packages/logger/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "esModuleInterop": true, 5 | "strict": false, 6 | "outDir": "build", 7 | "types": ["node"], 8 | "checkJs": true 9 | }, 10 | "include": [ 11 | "index.ts", 12 | "lib" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /packages/opencv/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib'); 2 | -------------------------------------------------------------------------------- /packages/opencv/lib/autorelease-pool.js: -------------------------------------------------------------------------------- 1 | export class OpenCvAutoreleasePool { 2 | /** 3 | * @type {Set} 4 | */ 5 | _items; 6 | 7 | constructor() { 8 | this._items = new Set(); 9 | } 10 | 11 | /** 12 | * @template T 13 | * @param {...T} items 14 | * @return {T[]|T} the same items 15 | */ 16 | add(...items) { 17 | for (const item of items) { 18 | this._items.add(item); 19 | } 20 | return items.length === 1 ? items[0] : items; 21 | } 22 | 23 | drain() { 24 | for (const item of this._items) { 25 | try { 26 | item.delete(); 27 | } catch (ign) {} 28 | } 29 | this._items.clear(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/appium-diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/appium-diagram.jpg -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/cc1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/cc1.png -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/cc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/cc2.png -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/cc_rotated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/cc_rotated.png -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/findwaldo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/findwaldo.jpg -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/number5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/number5.png -------------------------------------------------------------------------------- /packages/opencv/test/e2e/images/waldo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/opencv/test/e2e/images/waldo.jpg -------------------------------------------------------------------------------- /packages/opencv/test/unit/opencv.spec.js: -------------------------------------------------------------------------------- 1 | import {initOpenCv} from '../../lib'; 2 | 3 | describe('OpenCV', function () { 4 | it('should initialize opencv library', async function () { 5 | this.timeout('10s'); 6 | await initOpenCv(); 7 | const buildInfo = require('opencv-bindings').getBuildInformation(); 8 | buildInfo.should.include('OpenCV'); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /packages/opencv/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "checkJs": true 6 | }, 7 | "include": ["lib"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/plugin-test-support/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/harness'); 2 | -------------------------------------------------------------------------------- /packages/plugin-test-support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "checkJs": true, 7 | "paths": { 8 | "@appium/types": ["../types"], 9 | "appium/support": ["../appium/support"] 10 | }, 11 | "types": ["mocha", "chai", "sinon", "sinon-chai", "chai-as-promised"] 12 | }, 13 | "include": ["./lib"], 14 | "references": [{"path": "../types"}, {"path": "../support"}] 15 | } 16 | -------------------------------------------------------------------------------- /packages/relaxed-caps-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @appium/relaxed-caps-plugin 2 | 3 | > Appium plugin for handling extension capabilities with no prefix 4 | 5 | [![NPM version](http://img.shields.io/npm/v/@appium/relaxed-caps-plugin.svg)](https://npmjs.org/package/@appium/relaxed-caps-plugin) 6 | [![Downloads](http://img.shields.io/npm/dm/@appium/relaxed-caps-plugin.svg)](https://npmjs.org/package/@appium/relaxed-caps-plugin) 7 | 8 | Appium conforms to the W3C WebDriver Protocol [requirements for capabilities](https://www.w3.org/TR/webdriver/#capabilities), 9 | which means that all non-standard (extension) capabilities used with Appium must have a prefix 10 | (usually this prefix is `appium:`). Any non-standard capabilities without a prefix are rejected. 11 | 12 | This plugin can be used to automatically add the `appium:` prefix to non-standard capabilities that 13 | do not have a prefix. 14 | 15 | ## Motivation 16 | 17 | There are a lot of test scripts out there that don't conform to the W3C capability requirements, 18 | so this plugin is designed to make it easy to keep running these scripts even with the stricter 19 | capability requirements in Appium 2. 20 | 21 | ## Installation 22 | 23 | ``` 24 | appium plugin install relaxed-caps 25 | ``` 26 | 27 | The plugin must be explicitly activated when launching the Appium server: 28 | 29 | ``` 30 | appium --use-plugins=relaxed-caps 31 | ``` 32 | 33 | ## License 34 | 35 | Apache-2.0 36 | -------------------------------------------------------------------------------- /packages/relaxed-caps-plugin/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/plugin'); 2 | -------------------------------------------------------------------------------- /packages/relaxed-caps-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "checkJs": true 5 | }, 6 | "extends": "@appium/tsconfig/tsconfig.plugin.json", 7 | "include": ["lib"] 8 | } 9 | -------------------------------------------------------------------------------- /packages/schema/README.md: -------------------------------------------------------------------------------- 1 | # @appium/schema 2 | 3 | > JSON schema for [Appium](https://github.com/appium/appium) configuration files 4 | 5 | ## Description 6 | 7 | This package is used internally by Appium, but can also be used to validate Appium configuration files in other contexts. 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm i @appium/schema 13 | ``` 14 | 15 | ## Usage 16 | 17 | The schema is exported as a JS object: 18 | 19 | ```js 20 | const { AppiumConfigJsonSchema } = require('@appium/schema'); 21 | ``` 22 | 23 | It is also provided as a JSON file (since this is a JSON schema, after all): 24 | 25 | ```js 26 | const schema = require('@appium/schema/lib/appium-config.schema.json'); 27 | ``` 28 | 29 | ## See Also 30 | 31 | [@appium/types](https://npm.im/@appium/types) exports a TypeScript type `AppiumConfig` (generated from this package) for typesafe configuration objects; this may be useful if your Appium configuration is written in JS (e.g., `.appiumrc.js`). Example: 32 | 33 | ```js 34 | // @ts-check 35 | /** @type {import('@appium/types').AppiumConfig} */ 36 | module.exports = { 37 | server: { 38 | port: 1234, 39 | host: '127.0.0.1' 40 | } 41 | } 42 | ``` 43 | 44 | ## Notes 45 | 46 | `lib/appium-config.schema.json` is generated by this package from `lib/appium-config-schema.js` (the single source of truth), but is under version control to avoid chicken-or-egg build problems. 47 | 48 | ## License 49 | 50 | Apache-2.0 51 | -------------------------------------------------------------------------------- /packages/schema/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/index.js'); 2 | -------------------------------------------------------------------------------- /packages/schema/lib/index.js: -------------------------------------------------------------------------------- 1 | export * from './appium-config-schema'; 2 | -------------------------------------------------------------------------------- /packages/schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@appium/schema", 3 | "version": "0.6.1", 4 | "description": "Appium Configuration Schema", 5 | "keywords": [ 6 | "automation", 7 | "javascript", 8 | "selenium", 9 | "webdriver", 10 | "ios", 11 | "android", 12 | "firefoxos", 13 | "testing" 14 | ], 15 | "homepage": "https://appium.io", 16 | "bugs": { 17 | "url": "https://github.com/appium/appium/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/appium/appium.git", 22 | "directory": "schema" 23 | }, 24 | "license": "Apache-2.0", 25 | "author": "https://github.com/appium", 26 | "types": "./build/lib/index.d.ts", 27 | "files": [ 28 | "build", 29 | "lib", 30 | "index.js", 31 | "tsconfig.json", 32 | "!build/tsconfig.tsbuildinfo" 33 | ], 34 | "scripts": { 35 | "build": "node ./scripts/generate-schema-json.js", 36 | "clean": "git checkout -- ./lib/appium-config.schema.json || true", 37 | "test:smoke": "node ./index.js", 38 | "test": "exit 0" 39 | }, 40 | "dependencies": { 41 | "@types/json-schema": "7.0.15", 42 | "json-schema": "0.4.0", 43 | "source-map-support": "0.5.21" 44 | }, 45 | "engines": { 46 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0", 47 | "npm": ">=8" 48 | }, 49 | "publishConfig": { 50 | "access": "public" 51 | }, 52 | "gitHead": "84b211330dc84849af9cc1f3a5c5ad32e15b2e72" 53 | } 54 | -------------------------------------------------------------------------------- /packages/schema/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "checkJs": true, 7 | "strict": true 8 | }, 9 | "include": ["lib"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/strongbox/lib/util.ts: -------------------------------------------------------------------------------- 1 | import slug from 'slugify'; 2 | 3 | export function slugify(value: string) { 4 | return slug(value); 5 | } 6 | -------------------------------------------------------------------------------- /packages/strongbox/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@appium/strongbox", 3 | "version": "0.3.2", 4 | "description": "Persistent storage for Appium extensions", 5 | "keywords": [ 6 | "automation", 7 | "javascript", 8 | "selenium", 9 | "webdriver", 10 | "ios", 11 | "android", 12 | "firefoxos", 13 | "testing" 14 | ], 15 | "homepage": "https://appium.io", 16 | "bugs": { 17 | "url": "https://github.com/appium/appium/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/appium/appium.git", 22 | "directory": "packages/strongbox" 23 | }, 24 | "license": "Apache-2.0", 25 | "author": "https://github.com/appium", 26 | "main": "./build/lib/index.js", 27 | "types": "./build/lib/index.d.ts", 28 | "directories": { 29 | "lib": "lib" 30 | }, 31 | "files": [ 32 | "build/lib", 33 | "lib", 34 | "tsconfig.json" 35 | ], 36 | "scripts": { 37 | "test": "run-p test:unit test:types", 38 | "test:smoke": "node .", 39 | "test:unit": "mocha \"test/unit/**/*.spec.ts\"", 40 | "test:types": "tsd", 41 | "test:e2e": "mocha -t 20s \"test/e2e/**/*.e2e.spec.ts\"" 42 | }, 43 | "dependencies": { 44 | "env-paths": "2.2.1", 45 | "slugify": "1.6.6" 46 | }, 47 | "engines": { 48 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0", 49 | "npm": ">=8" 50 | }, 51 | "publishConfig": { 52 | "access": "public" 53 | }, 54 | "tsd": { 55 | "directory": "test/types" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /packages/strongbox/test/types/strongbox.test-d.ts: -------------------------------------------------------------------------------- 1 | import {expectAssignable, expectNotAssignable} from 'tsd'; 2 | import {Item, BaseItem, Value, strongbox} from '../..'; 3 | 4 | expectAssignable>(new BaseItem('foo', strongbox('foo'))); 5 | 6 | expectNotAssignable(1); 7 | expectAssignable('foo'); 8 | expectNotAssignable(true); 9 | expectAssignable(Buffer.from('foo')); 10 | expectNotAssignable({}); 11 | -------------------------------------------------------------------------------- /packages/strongbox/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "strict": true, 7 | "checkJs": true, 8 | "types": ["node", "mocha", "chai", "chai-as-promised", "sinon-chai"] 9 | }, 10 | "include": ["lib", "test"], 11 | "exclude": ["**/*.test-d.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /packages/support/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib'); 2 | -------------------------------------------------------------------------------- /packages/support/lib/doctor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A shortcut for a successful required doctor check 3 | * 4 | * @param {string} message 5 | * @returns {DoctorCheckResult} 6 | */ 7 | export function ok(message) { 8 | return {ok: true, optional: false, message}; 9 | } 10 | 11 | /** 12 | * A shortcut for an unsuccessful required doctor check 13 | * 14 | * @param {string} message 15 | * @returns {DoctorCheckResult} 16 | */ 17 | export function nok(message) { 18 | return {ok: false, optional: false, message}; 19 | } 20 | 21 | /** 22 | * A shortcut for a successful optional doctor check 23 | * 24 | * @param {string} message 25 | * @returns {DoctorCheckResult} 26 | */ 27 | export function okOptional(message) { 28 | return {ok: true, optional: true, message}; 29 | } 30 | 31 | /** 32 | * A shortcut for an unsuccessful optional doctor check 33 | * 34 | * @param {string} message 35 | * @returns {DoctorCheckResult} 36 | */ 37 | export function nokOptional(message) { 38 | return {ok: false, optional: true, message}; 39 | } 40 | 41 | /** 42 | * Throw this exception in the fix() method 43 | * of your doctor check to skip the actual fix if hasAutofix() is true 44 | */ 45 | export class FixSkippedError extends Error {} 46 | 47 | /** 48 | * @typedef {import('@appium/types').DoctorCheckResult} DoctorCheckResult 49 | */ 50 | -------------------------------------------------------------------------------- /packages/support/lib/image-util.js: -------------------------------------------------------------------------------- 1 | let _sharp; 2 | 3 | /** 4 | * @returns {import('sharp')} 5 | */ 6 | export function requireSharp() { 7 | if (!_sharp) { 8 | try { 9 | _sharp = require('sharp'); 10 | } catch (err) { 11 | throw new Error( 12 | `Cannot load the 'sharp' module needed for images processing. ` + 13 | `Consider visiting https://sharp.pixelplumbing.com/install ` + 14 | `for troubleshooting. Original error: ${err.message}` 15 | ); 16 | } 17 | } 18 | return _sharp; 19 | } 20 | 21 | /** 22 | * Crop the image by given rectangle (use base64 string as input and output) 23 | * 24 | * @param {string} base64Image The string with base64 encoded image. 25 | * Supports all image formats natively supported by Sharp library. 26 | * @param {import('sharp').Region} rect The selected region of image 27 | * @return {Promise} base64 encoded string of cropped image 28 | */ 29 | export async function cropBase64Image(base64Image, rect) { 30 | const buf = await requireSharp()(Buffer.from(base64Image, 'base64')).extract(rect).toBuffer(); 31 | return buf.toString('base64'); 32 | } 33 | -------------------------------------------------------------------------------- /packages/support/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {getLogger} from './logging'; 2 | 3 | let log = getLogger('Support'); 4 | 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/support/lib/mkdirp.js: -------------------------------------------------------------------------------- 1 | import {fs} from './fs'; 2 | /** 3 | * @deprecated Use `fs.mkdirp` instead. 4 | */ 5 | const {mkdirp} = fs; 6 | export {mkdirp}; 7 | -------------------------------------------------------------------------------- /packages/support/lib/process.js: -------------------------------------------------------------------------------- 1 | import {exec} from 'teen_process'; 2 | 3 | /* 4 | * Exit Status for pgrep and pkill (`man pkill`) 5 | * 0. One or more processes matched the criteria. 6 | * 1. No processes matched. 7 | * 2. Syntax error in the command line. 8 | * 3. Fatal error: out of memory etc. 9 | */ 10 | 11 | async function getProcessIds(appName) { 12 | let pids; 13 | try { 14 | let {stdout} = await exec('pgrep', ['-x', appName]); 15 | pids = stdout 16 | .trim() 17 | .split('\n') 18 | .map((pid) => parseInt(pid, 10)); 19 | } catch (err) { 20 | if (parseInt(err.code, 10) !== 1) { 21 | throw new Error(`Error getting process ids for app '${appName}': ${err.message}`); 22 | } 23 | pids = []; 24 | } 25 | return pids; 26 | } 27 | 28 | async function killProcess(appName, force = false) { 29 | let pids = await getProcessIds(appName); 30 | if (pids.length === 0) { 31 | // the process is not running 32 | return; 33 | } 34 | 35 | try { 36 | let args = force ? ['-9'] : []; 37 | args.push('-x', appName); 38 | await exec('pkill', args); 39 | } catch (err) { 40 | if (parseInt(err.code, 10) !== 1) { 41 | throw new Error(`Error killing app '${appName}' with pkill: ${err.message}`); 42 | } 43 | } 44 | } 45 | 46 | export {getProcessIds, killProcess}; 47 | -------------------------------------------------------------------------------- /packages/support/lib/system.js: -------------------------------------------------------------------------------- 1 | import {exec} from 'teen_process'; 2 | import _ from 'lodash'; 3 | import os from 'os'; 4 | 5 | const VERSION_PATTERN = /^(\d+\.\d+)/m; 6 | 7 | function isWindows() { 8 | return os.type() === 'Windows_NT'; 9 | } 10 | 11 | function isMac() { 12 | return os.type() === 'Darwin'; 13 | } 14 | 15 | function isLinux() { 16 | return !isWindows() && !isMac(); 17 | } 18 | 19 | function isOSWin64() { 20 | return process.arch === 'x64' || _.has(process.env, 'PROCESSOR_ARCHITEW6432'); 21 | } 22 | 23 | async function arch() { 24 | if (isLinux() || isMac()) { 25 | let {stdout} = await exec('uname', ['-m']); 26 | return stdout.trim() === 'i686' ? '32' : '64'; 27 | } else if (isWindows()) { 28 | let is64 = this.isOSWin64(); 29 | return is64 ? '64' : '32'; 30 | } 31 | } 32 | 33 | async function macOsxVersion() { 34 | let stdout; 35 | try { 36 | stdout = (await exec('sw_vers', ['-productVersion'])).stdout.trim(); 37 | } catch (err) { 38 | throw new Error(`Could not detect Mac OS X Version: ${err}`); 39 | } 40 | 41 | const versionMatch = VERSION_PATTERN.exec(stdout); 42 | if (!versionMatch) { 43 | throw new Error(`Could not detect Mac OS X Version from sw_vers output: '${stdout}'`); 44 | } 45 | return versionMatch[1]; 46 | } 47 | 48 | export {isWindows, isMac, isLinux, isOSWin64, arch, macOsxVersion}; 49 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fixture/appium-v1-dependency.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "dependencies": { 4 | "appium": "1.0.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fixture/appium-v1-package/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'totally appium'; 2 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fixture/appium-v1-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phony-appium", 3 | "version": "1.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fixture/appium-v2-dependency.package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "dependencies": { 4 | "appium": "2.0.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fixture/appium-v2-package/index.js: -------------------------------------------------------------------------------- 1 | module.exports = 'totally appium'; 2 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fixture/appium-v2-package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phony-appium", 3 | "version": "2.0.0", 4 | "private": true 5 | } 6 | -------------------------------------------------------------------------------- /packages/support/test/e2e/fs.e2e.spec.js: -------------------------------------------------------------------------------- 1 | import fs from '../../lib/fs'; 2 | import {isWindows} from '../../lib/system'; 3 | 4 | describe('isExecutable()', function () { 5 | describe('when the path does not exist', function () { 6 | it('should return `false`', async function () { 7 | await fs.isExecutable('/path/to/nowhere').should.eventually.be.false; 8 | }); 9 | }); 10 | 11 | describe('when the path exists', function () { 12 | beforeEach(function () { 13 | if (isWindows()) { 14 | return this.skip(); 15 | } 16 | }); 17 | 18 | describe('when the path is not executable', function () { 19 | it('should return `false`', async function () { 20 | await fs.isExecutable(__filename).should.eventually.be.false; 21 | }); 22 | }); 23 | 24 | describe('when the path is executable', function () { 25 | it('should return `true`', async function () { 26 | await fs.isExecutable('/bin/bash').should.eventually.be.true; 27 | }); 28 | }); 29 | }); 30 | 31 | describe('when the parameter is not a path', function () { 32 | it('should return `false`', async function () { 33 | await fs.isExecutable().should.eventually.be.false; 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/support/test/e2e/image-util.e2e.spec.js: -------------------------------------------------------------------------------- 1 | import {cropBase64Image} from '../../lib/image-util'; 2 | import path from 'path'; 3 | import sharp from 'sharp'; 4 | import {fs} from '../../lib'; 5 | 6 | const FIXTURES_ROOT = path.resolve(__dirname, 'fixture', 'images'); 7 | 8 | async function getImage(name) { 9 | const imagePath = path.resolve(FIXTURES_ROOT, name); 10 | return await fs.readFile(imagePath, 'utf8'); 11 | } 12 | 13 | describe('image-util', function () { 14 | describe('cropBase64Image', function () { 15 | let originalImageB64 = null; 16 | 17 | before(async function () { 18 | originalImageB64 = await getImage('full-image.b64'); 19 | }); 20 | 21 | it('should verify that an image is cropped correctly', async function () { 22 | const croppedImageB64 = await cropBase64Image(originalImageB64, { 23 | left: 35, 24 | top: 107, 25 | width: 323, 26 | height: 485, 27 | }); 28 | 29 | const croppedImage = sharp(Buffer.from(croppedImageB64, 'base64')); 30 | const {width, height} = await croppedImage.metadata(); 31 | // verify cropped image size, it should be less than original image according to crop region 32 | width.should.be.equal(323, 'unexpected width'); 33 | height.should.be.equal(485, 'unexpected height'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/support/test/e2e/net.e2e.spec.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import {downloadFile} from '../../lib/net'; 3 | import {tempDir, fs} from '../../lib/index'; 4 | 5 | describe('#net', function () { 6 | let tmpRoot; 7 | 8 | beforeEach(async function () { 9 | tmpRoot = await tempDir.openDir(); 10 | }); 11 | 12 | afterEach(async function () { 13 | await fs.rimraf(tmpRoot); 14 | }); 15 | 16 | describe('downloadFile()', function () { 17 | it('should download file into the target folder', async function () { 18 | const dstPath = path.join(tmpRoot, 'download.tmp'); 19 | await downloadFile( 20 | 'https://appium.io/docs/en/2.0/assets/images/appium-logo-white.png', 21 | dstPath 22 | ); 23 | await fs.exists(dstPath).should.eventually.be.true; 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /packages/support/test/e2e/node.e2e.spec.js: -------------------------------------------------------------------------------- 1 | import {node} from '../../lib'; 2 | 3 | describe('node utilities', function () { 4 | describe('requirePackage', function () { 5 | it('should be able to require a local package', async function () { 6 | await node.requirePackage('chai').should.not.be.rejected; 7 | }); 8 | // XXX: see #15951 9 | it.skip('should be able to require a global package', async function () { 10 | await node.requirePackage('npm').should.not.be.rejected; 11 | }); 12 | it('should fail to find uninstalled package', async function () { 13 | await node 14 | .requirePackage('appium-foo-driver') 15 | .should.eventually.be.rejectedWith(/Unable to load package/); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/support/test/helpers.js: -------------------------------------------------------------------------------- 1 | import {EventEmitter} from 'events'; 2 | import rewiremock, {addPlugin, overrideEntryPoint, plugins} from 'rewiremock'; 3 | 4 | overrideEntryPoint(module); 5 | addPlugin(plugins.nodejs); 6 | class MockReadWriteStream extends EventEmitter { 7 | resume() {} 8 | 9 | pause() {} 10 | 11 | setEncoding() {} 12 | 13 | flush() {} 14 | 15 | write(msg) { 16 | this.emit('data', msg); 17 | } 18 | 19 | end() { 20 | this.emit('end'); 21 | this.emit('finish'); 22 | } 23 | } 24 | 25 | export {MockReadWriteStream, rewiremock}; 26 | -------------------------------------------------------------------------------- /packages/support/test/unit/assets/sample_binary.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/support/test/unit/assets/sample_binary.plist -------------------------------------------------------------------------------- /packages/support/test/unit/assets/sample_text.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.locationd.bundle-/System/Library/PrivateFrameworks/Parsec.framework 6 | 7 | Whitelisted 8 | 9 | Executable 10 | 11 | BundlePath 12 | /System/Library/PrivateFrameworks/Parsec.framework 13 | Registered 14 | 15 | 16 | com.apple.locationd.bundle-/System/Library/PrivateFrameworks/WirelessDiagnostics.framework 17 | 18 | Whitelisted 19 | 20 | Executable 21 | 22 | BundlePath 23 | /System/Library/PrivateFrameworks/WirelessDiagnostics.framework 24 | Registered 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /packages/support/test/unit/index.spec.js: -------------------------------------------------------------------------------- 1 | import AppiumSupport from '../../lib/'; 2 | 3 | let {system, tempDir, util} = AppiumSupport; 4 | 5 | describe('index', function () { 6 | describe('default', function () { 7 | it('should expose an object', function () { 8 | AppiumSupport.should.exist; 9 | AppiumSupport.should.be.an.instanceof(Object); 10 | }); 11 | it('should expose system object', function () { 12 | AppiumSupport.system.should.exist; 13 | AppiumSupport.system.should.be.an.instanceof(Object); 14 | }); 15 | it('should expose tempDir object', function () { 16 | AppiumSupport.tempDir.should.exist; 17 | AppiumSupport.tempDir.should.be.an.instanceof(Object); 18 | }); 19 | it('should expose util object', function () { 20 | AppiumSupport.util.should.exist; 21 | AppiumSupport.util.should.be.an.instanceof(Object); 22 | }); 23 | }); 24 | 25 | it('should expose an object as "system" ', function () { 26 | system.should.be.an.instanceof(Object); 27 | }); 28 | 29 | it('should expose an object as "tempDir" ', function () { 30 | tempDir.should.be.an.instanceof(Object); 31 | }); 32 | 33 | it('should expose an object as "util" ', function () { 34 | util.should.be.an.instanceof(Object); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/support/test/unit/logger/logger-force.spec.js: -------------------------------------------------------------------------------- 1 | import {getDynamicLogger, restoreWriters, setupWriters, assertOutputContains} from './helpers'; 2 | 3 | describe('logger with force log', function () { 4 | let writers, log; 5 | before(function () { 6 | writers = setupWriters(); 7 | log = getDynamicLogger(true, true); 8 | log.level = 'silly'; 9 | }); 10 | 11 | after(function () { 12 | restoreWriters(writers); 13 | }); 14 | 15 | it('should not rewrite log levels even during testing', function () { 16 | log.silly('silly'); 17 | assertOutputContains(writers, 'silly'); 18 | log.verbose('verbose'); 19 | assertOutputContains(writers, 'verbose'); 20 | log.verbose('debug'); 21 | assertOutputContains(writers, 'debug'); 22 | log.info('info'); 23 | assertOutputContains(writers, 'info'); 24 | log.http('http'); 25 | assertOutputContains(writers, 'http'); 26 | log.warn('warn'); 27 | assertOutputContains(writers, 'warn'); 28 | log.error('error'); 29 | assertOutputContains(writers, 'error'); 30 | (() => { 31 | throw log.errorWithException('msg'); 32 | }).should.throw('msg'); 33 | assertOutputContains(writers, 'error'); 34 | assertOutputContains(writers, 'msg'); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/support/test/unit/logger/logger-test.spec.js: -------------------------------------------------------------------------------- 1 | // transpile:mocha 2 | 3 | import {getDynamicLogger, restoreWriters, setupWriters, assertOutputDoesntContain} from './helpers'; 4 | 5 | describe('test logger', function () { 6 | let writers, log; 7 | before(function () { 8 | writers = setupWriters(); 9 | log = getDynamicLogger(true); 10 | }); 11 | 12 | after(function () { 13 | restoreWriters(writers); 14 | }); 15 | 16 | it('should contains levels', function () { 17 | log.levels.should.have.length.above(3); 18 | log.levels[2].should.equal('debug'); 19 | }); 20 | 21 | it('should unwrap', function () { 22 | log.unwrap.should.exist; 23 | log.unwrap().should.exist; 24 | }); 25 | 26 | it('should rewrite npmlog levels during testing', function () { 27 | const text = 'hi'; 28 | log.silly(text); 29 | log.verbose(text); 30 | log.info(text); 31 | log.http(text); 32 | log.warn(text); 33 | log.error(text); 34 | (() => { 35 | throw log.errorWithException(text); 36 | }).should.throw(text); 37 | assertOutputDoesntContain(writers, text); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /packages/support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "paths": { 6 | "@appium/types": ["../types"] 7 | }, 8 | "checkJs": true 9 | }, 10 | "include": ["lib"], 11 | "references": [{"path": "../types"}] 12 | } 13 | -------------------------------------------------------------------------------- /packages/test-support/bin/android-emu-travis-pre.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ev 4 | 5 | if [ ${START_EMU} = "1" ]; then 6 | emuTag="" 7 | if [ -n "$ANDROID_EMU_TAG" ]; then 8 | emuTag="--tag $ANDROID_EMU_TAG" 9 | fi 10 | echo no | android create avd --force -n ${ANDROID_EMU_NAME} -t ${ANDROID_EMU_TARGET} --abi ${ANDROID_EMU_ABI} ${tag} 11 | emulator -avd ${ANDROID_EMU_NAME} -no-audio -no-window & 12 | fi 13 | 14 | exit 0; 15 | -------------------------------------------------------------------------------- /packages/test-support/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib'); 2 | -------------------------------------------------------------------------------- /packages/test-support/lib/env-utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates Mocha "before each" and "after each" hooks to restore `process.env` after every test. 3 | */ 4 | function stubEnv() { 5 | /** @type {NodeJS.ProcessEnv} */ 6 | let envBackup; 7 | beforeEach(function beforeEach() { 8 | envBackup = process.env; 9 | process.env = {...process.env}; 10 | }); 11 | afterEach(function afterEach() { 12 | process.env = envBackup; 13 | }); 14 | } 15 | 16 | export {stubEnv}; 17 | -------------------------------------------------------------------------------- /packages/test-support/lib/index.js: -------------------------------------------------------------------------------- 1 | // this just needs to be imported, for the functionality to be injected 2 | import './unhandled-rejection'; 3 | 4 | export {stubEnv} from './env-utils'; 5 | export {stubLog} from './log-utils'; 6 | export {fakeTime} from './time-utils'; 7 | export {withMocks, verifyMocks} from './mock-utils'; 8 | export {withSandbox, verifySandbox} from './sandbox-utils'; 9 | -------------------------------------------------------------------------------- /packages/test-support/lib/log-utils.js: -------------------------------------------------------------------------------- 1 | import '@colors/colors'; 2 | 3 | class LogStub { 4 | /** 5 | * 6 | * @param {LogStubOptions} [opts] 7 | */ 8 | constructor(opts = {}) { 9 | this.output = ''; 10 | this.stripColors = Boolean(opts.stripColors); 11 | } 12 | /** 13 | * 14 | * @param {string} level 15 | * @param {any} message 16 | */ 17 | log(level, message) { 18 | if (this.stripColors) { 19 | message = message.stripColors; 20 | } 21 | if (this.output.length > 0) { 22 | this.output += '\n'; 23 | } 24 | this.output = `${this.output}${level}: ${message}`; 25 | } 26 | } 27 | 28 | /** 29 | * Instantiates a {@linkcode LogStub} object 30 | * @param {import('sinon').SinonSandbox} sandbox 31 | * @param {import('@appium/types').AppiumLogger} log 32 | * @param {LogStubOptions} [opts] 33 | * @returns {LogStub} 34 | */ 35 | function stubLog(sandbox, log, opts = {}) { 36 | let logStub = new LogStub(opts); 37 | for (let l of log.levels) { 38 | sandbox.stub(log, l).callsFake(function doLogging(mess) { 39 | logStub.log(l, mess); 40 | }); 41 | } 42 | return logStub; 43 | } 44 | 45 | export {stubLog}; 46 | 47 | /** 48 | * Options for {@linkcode LogStub} constructor 49 | * @typedef LogStubOptions 50 | * @property {boolean} [stripColors] - If `true`, strip ANSI colors from output 51 | */ 52 | -------------------------------------------------------------------------------- /packages/test-support/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from '@appium/support'; 2 | 3 | const log = logger.getLogger('AppiumTestSupport'); 4 | 5 | export default log; 6 | -------------------------------------------------------------------------------- /packages/test-support/lib/time-utils.js: -------------------------------------------------------------------------------- 1 | /** @deprecated */ 2 | function fakeTime(sandbox) { 3 | let clock = sandbox.useFakeTimers(); 4 | return new TimeLord(clock); 5 | } 6 | 7 | class TimeLord { 8 | constructor(clock) { 9 | this.clock = clock; 10 | } 11 | 12 | speedup(interval, times) { 13 | let tick = (n) => { 14 | if (n === 0) return; // eslint-disable-line curly 15 | process.nextTick(() => { 16 | this.clock.tick(interval); 17 | n--; 18 | tick(n); 19 | }); 20 | }; 21 | tick(times); 22 | } 23 | } 24 | export {fakeTime}; 25 | -------------------------------------------------------------------------------- /packages/test-support/lib/unhandled-rejection.js: -------------------------------------------------------------------------------- 1 | import loudRejection from 'loud-rejection'; 2 | 3 | // in a testing environment (environment variable is set in `appium-gulp-utils`) 4 | // make sure unhandled promise rejections are made visible 5 | if (process.env._TESTING) { 6 | loudRejection(); 7 | } 8 | -------------------------------------------------------------------------------- /packages/test-support/test/unit/env-utils.spec.js: -------------------------------------------------------------------------------- 1 | import {stubEnv} from '../../lib'; 2 | 3 | const expect = chai.expect; 4 | 5 | describe('env-utils', function () { 6 | describe('stubEnv', function () { 7 | stubEnv(); 8 | 9 | it('setting env variable', function () { 10 | process.env.ABC = 'abc'; 11 | }); 12 | 13 | it('env varible should not be set', function () { 14 | expect(process.env.ABC).not.to.exist; 15 | }); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/test-support/test/unit/fixtures/ContactManager.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emilycodestar/appium/b6de85359d9eb7654f99eb77d90fc9e2ed14c671/packages/test-support/test/unit/fixtures/ContactManager.apk -------------------------------------------------------------------------------- /packages/test-support/test/unit/log-utils.spec.js: -------------------------------------------------------------------------------- 1 | import {stubLog} from '../../lib'; 2 | import log from '../../lib/logger'; 3 | import sinon from 'sinon'; 4 | import '@colors/colors'; 5 | 6 | describe('log-utils', function () { 7 | describe('stubLog', function () { 8 | let sandbox; 9 | beforeEach(function () { 10 | sandbox = sinon.createSandbox(); 11 | }); 12 | afterEach(function () { 13 | sandbox.restore(); 14 | }); 15 | it('should stub log', function () { 16 | let logStub = stubLog(sandbox, log); 17 | log.info('Hello World!'); 18 | log.warn(`The ${'sun'.yellow} is shining!`); 19 | logStub.output.should.equals( 20 | ['info: Hello World!', `warn: The ${'sun'.yellow} is shining!`].join('\n') 21 | ); 22 | }); 23 | it('should stub log and strip colors', function () { 24 | let logStub = stubLog(sandbox, log, {stripColors: true}); 25 | log.info('Hello World!'); 26 | log.warn(`The ${'sun'.yellow} is shining!`); 27 | logStub.output.should.equals(['info: Hello World!', 'warn: The sun is shining!'].join('\n')); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/test-support/test/unit/sandbox-utils.spec.js: -------------------------------------------------------------------------------- 1 | import {withSandbox, verifySandbox} from '../../lib'; 2 | 3 | const expect = chai.expect; 4 | 5 | let funcs = { 6 | abc: () => 'abc', 7 | }; 8 | 9 | describe('sandbox-utils', function () { 10 | describe( 11 | 'withSandbox', 12 | withSandbox({mocks: {funcs}}, (S) => { 13 | it('should create a sandbox and mocks', function () { 14 | expect(S.sandbox).to.exist; 15 | expect(S.mocks.funcs).to.exist; 16 | funcs.abc().should.equal('abc'); 17 | S.mocks.funcs.expects('abc').once().returns('efg'); 18 | funcs.abc().should.equal('efg'); 19 | S.sandbox.verify(); 20 | }); 21 | 22 | it('should be back to normal', function () { 23 | funcs.abc().should.equal('abc'); 24 | }); 25 | 26 | it('S.verify', function () { 27 | expect(S.sandbox).to.exist; 28 | expect(S.mocks.funcs).to.exist; 29 | S.mocks.funcs.expects('abc').once().returns('efg'); 30 | funcs.abc().should.equal('efg'); 31 | S.verify(); 32 | }); 33 | 34 | it('verifySandbox', function () { 35 | expect(S.sandbox).to.exist; 36 | expect(S.mocks.funcs).to.exist; 37 | S.mocks.funcs.expects('abc').once().returns('efg'); 38 | funcs.abc().should.equal('efg'); 39 | verifySandbox(S); 40 | }); 41 | }) 42 | ); 43 | }); 44 | -------------------------------------------------------------------------------- /packages/test-support/test/unit/time-utils.spec.js: -------------------------------------------------------------------------------- 1 | import {fakeTime} from '../../lib'; 2 | 3 | import sinon from 'sinon'; 4 | import B from 'bluebird'; 5 | 6 | function doSomething() { 7 | return new B.Promise((resolve) => { 8 | let ret = ''; 9 | function appendOneByOne() { 10 | if (ret.length >= 10) { 11 | return resolve(ret); 12 | } 13 | setTimeout(() => { 14 | ret = ret + ret.length; 15 | appendOneByOne(); 16 | }, 1000); 17 | } 18 | appendOneByOne(); 19 | }); 20 | } 21 | 22 | describe('time-utils', function () { 23 | describe('fakeTime', function () { 24 | let sandbox; 25 | beforeEach(function () { 26 | sandbox = sinon.createSandbox(); 27 | }); 28 | afterEach(function () { 29 | sandbox.restore(); 30 | }); 31 | it('should fake time', async function () { 32 | let timeLord = fakeTime(sandbox); 33 | let p = doSomething(); 34 | timeLord.speedup(200, 60); 35 | (await p).should.equals('0123456789'); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /packages/test-support/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "outDir": "build", 6 | "checkJs": true, 7 | "paths": { 8 | "@appium/types": ["../types"], 9 | "@appium/support": ["../support"] 10 | }, 11 | "types": ["mocha", "chai", "sinon", "sinon-chai", "chai-as-promised"] 12 | }, 13 | "include": ["./lib"], 14 | "references": [{"path": "../types"}, {"path": "../support"}] 15 | } 16 | -------------------------------------------------------------------------------- /packages/tsconfig/README.md: -------------------------------------------------------------------------------- 1 | # @appium/tsconfig 2 | 3 | > Shared TypeScript Config for Appium 4 | 5 | ## Motivation 6 | 7 | Appium projects and extensions are encouraged to use these settings. 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm install appium @appium/tsconfig -D 13 | ``` 14 | 15 | [appium](https://npm.im/appium) is a peer dependency of this package. 16 | 17 | ## License 18 | 19 | Apache-2.0 20 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@appium/tsconfig", 3 | "version": "0.3.3", 4 | "description": "Shared TypeScript Config for Appium", 5 | "main": "tsconfig.json", 6 | "keywords": [ 7 | "automation", 8 | "javascript", 9 | "selenium", 10 | "webdriver", 11 | "ios", 12 | "android", 13 | "firefoxos", 14 | "testing" 15 | ], 16 | "license": "Apache-2.0", 17 | "files": [ 18 | "tsconfig*.json" 19 | ], 20 | "author": "https://github.com/appium", 21 | "bugs": { 22 | "url": "https://github.com/appium/appium/issues" 23 | }, 24 | "homepage": "https://appium.io", 25 | "engines": { 26 | "node": "^14.17.0 || ^16.13.0 || >=18.0.0", 27 | "npm": ">=8" 28 | }, 29 | "dependencies": { 30 | "@tsconfig/node14": "14.1.2" 31 | }, 32 | "scripts": { 33 | "test:smoke": "node tsconfig.json && node tsconfig.plugin.json" 34 | }, 35 | "publishConfig": { 36 | "access": "public" 37 | }, 38 | "gitHead": "84b211330dc84849af9cc1f3a5c5ad32e15b2e72" 39 | } 40 | -------------------------------------------------------------------------------- /packages/tsconfig/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@tsconfig/node14/tsconfig.json", 4 | "ts-node": { 5 | "transpileOnly": true 6 | }, 7 | "compilerOptions": { 8 | "allowJs": true, 9 | "allowSyntheticDefaultImports": true, 10 | "composite": true, 11 | "declaration": true, 12 | "declarationMap": true, 13 | "resolveJsonModule": true, 14 | "strictNullChecks": true, 15 | "stripInternal": true, 16 | "sourceMap": true, 17 | "removeComments": false, 18 | "strict": false, 19 | "types": ["node"], 20 | "lib": ["es2020", "ES2021.WeakRef"] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/tsconfig/tsconfig.plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "appium": ["../packages/appium"], 5 | "appium/plugin": ["../packages/appium/plugin"], 6 | "@appium/plugin-test-support": ["../packages/plugin-test-support"] 7 | }, 8 | "types": ["webdriverio"] 9 | }, 10 | "extends": "./tsconfig.json", 11 | "references": [ 12 | {"path": "../packages/appium"}, 13 | {"path": "../packages/base-plugin"}, 14 | {"path": "../packages/types"}, 15 | {"path": "../packages/plugin-test-support"} 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /packages/types/README.md: -------------------------------------------------------------------------------- 1 | # @appium/types 2 | 3 | > A collection of TypeScript type declarations used across various [Appium](https://github.com/appium/appium) packages. 4 | 5 | > [!WARNING] 6 | > This is a work-in-progress; expect breaking changes! 7 | 8 | ## Install 9 | 10 | ```bash 11 | npm install @appium/types -D 12 | ``` 13 | 14 | > [!NOTE] 15 | > - The sources are `.ts` files, _not_ `.d.ts` files. This allows other packages in the root TypeScript "project" to define a dependency upon this one, and enables incremental builds an "watch" mode. 16 | > - If there is a way to switch to `.d.ts` files and configure this package to work in our "project" context _without_ needing to actually "emit" anything, then we should do that instead. Help accepted! 17 | > - `lib/appium-config.ts` is generated by this package from `@appium/schema`, but is under version control to avoid chicken-or-egg build problems. 18 | 19 | ## License 20 | 21 | Apache-2.0 22 | -------------------------------------------------------------------------------- /packages/types/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./build/lib/constraints'); 2 | -------------------------------------------------------------------------------- /packages/types/lib/constraints.ts: -------------------------------------------------------------------------------- 1 | import type {Constraints} from './driver'; 2 | 3 | export const BASE_DESIRED_CAP_CONSTRAINTS = { 4 | platformName: { 5 | presence: true, 6 | isString: true, 7 | }, 8 | app: { 9 | isString: true, 10 | }, 11 | platformVersion: { 12 | isString: true, 13 | }, 14 | webSocketUrl: { 15 | isBoolean: true, 16 | }, 17 | newCommandTimeout: { 18 | isNumber: true, 19 | }, 20 | automationName: { 21 | isString: true, 22 | }, 23 | autoLaunch: { 24 | isBoolean: true, 25 | }, 26 | udid: { 27 | isString: true, 28 | }, 29 | orientation: { 30 | inclusion: ['LANDSCAPE', 'PORTRAIT'], 31 | }, 32 | autoWebview: { 33 | isBoolean: true, 34 | }, 35 | noReset: { 36 | isBoolean: true, 37 | }, 38 | fullReset: { 39 | isBoolean: true, 40 | }, 41 | language: { 42 | isString: true, 43 | }, 44 | locale: { 45 | isString: true, 46 | }, 47 | eventTimings: { 48 | isBoolean: true, 49 | }, 50 | printPageSourceOnFindFailure: { 51 | isBoolean: true, 52 | }, 53 | } as const satisfies Constraints; 54 | 55 | export type BaseDriverCapConstraints = typeof BASE_DESIRED_CAP_CONSTRAINTS; 56 | -------------------------------------------------------------------------------- /packages/types/lib/doctor.ts: -------------------------------------------------------------------------------- 1 | import type { AppiumLogger } from './logger'; 2 | 3 | /** 4 | * The object with below properties is expected 5 | * to be returned from the Doctor Check's {@link diagnose} method 6 | */ 7 | export interface DoctorCheckResult { 8 | /** 9 | * Whether the diagnosis found no issues 10 | */ 11 | ok: boolean; 12 | /** 13 | * Whether the diagnosed issue is safe to ignore 14 | */ 15 | optional: boolean; 16 | /** 17 | * The text message describing the diagnostic result 18 | */ 19 | message: string; 20 | } 21 | 22 | /** 23 | * Each compatible Doctor Check class must implement this interface 24 | */ 25 | export interface IDoctorCheck { 26 | /** 27 | * This property will be assigned by the server automatically if unset 28 | */ 29 | log: AppiumLogger; 30 | 31 | /** 32 | * Diagnoses the actual problem 33 | */ 34 | diagnose(): Promise; 35 | /** 36 | * Either fixes the actual problem 37 | * if hasAutofix returns true or 38 | * returns a string description for possible manual fixes 39 | */ 40 | fix(): Promise; 41 | /** 42 | * Whether calling {@link fix()} would resolve the found issue 43 | */ 44 | hasAutofix(): boolean; 45 | /** 46 | * Whether the found issue can be ignored and is not a showstopper 47 | */ 48 | isOptional(): boolean; 49 | } 50 | -------------------------------------------------------------------------------- /packages/types/lib/http.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An object of HTTP headers. 3 | */ 4 | export type HTTPHeaders = Record; 5 | 6 | /** 7 | * Possible HTTP methods, as stolen from `axios`. 8 | * 9 | * @see https://npm.im/axios 10 | */ 11 | export type HTTPMethod = 12 | | 'get' 13 | | 'GET' 14 | | 'delete' 15 | | 'DELETE' 16 | | 'head' 17 | | 'HEAD' 18 | | 'options' 19 | | 'OPTIONS' 20 | | 'post' 21 | | 'POST' 22 | | 'put' 23 | | 'PUT' 24 | | 'patch' 25 | | 'PATCH' 26 | | 'purge' 27 | | 'PURGE' 28 | | 'link' 29 | | 'LINK' 30 | | 'unlink' 31 | | 'UNLINK'; 32 | -------------------------------------------------------------------------------- /packages/types/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './command'; 2 | export * from './action'; 3 | export * from './appium-config'; 4 | export * from './capabilities'; 5 | export * from './config'; 6 | export * from './constraints'; 7 | export * from './driver'; 8 | export * from './plugin'; 9 | export * from './http'; 10 | export * from './util'; 11 | export * from './server'; 12 | export * from './logger'; 13 | export * from './doctor'; 14 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@appium/tsconfig/tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "build", 5 | "checkJs": true, 6 | "strict": true, 7 | "paths": { 8 | "@appium/schema": ["../schema"] 9 | } 10 | }, 11 | "include": ["lib"], 12 | "references": [{"path": "../schema"}] 13 | } 14 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/index.js: -------------------------------------------------------------------------------- 1 | const {main, UniversalXMLPlugin} = require('./build/lib'); 2 | 3 | if (require.main === module) { 4 | main(); 5 | } 6 | 7 | module.exports = {UniversalXMLPlugin}; 8 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/lib/attr-map.js: -------------------------------------------------------------------------------- 1 | // uses the same format as NODE_MAP in node-map.js 2 | const ATTR_MAP = { 3 | x: {ios: 'x', android: 'x'}, 4 | y: {ios: 'y', android: 'y'}, 5 | width: {ios: 'width', android: 'width'}, 6 | height: {ios: 'height', android: 'height'}, 7 | enabled: {ios: 'enabled', android: 'enabled'}, 8 | axId: {ios: 'name', android: 'content-desc'}, 9 | id: {android: 'resource-id'}, 10 | text: {ios: 'label', android: 'text'}, 11 | visible: {ios: 'visible', android: 'displayed'}, 12 | value: {ios: 'value'}, 13 | }; 14 | 15 | // these attributes shouldn't be mapped and should instead just be removed 16 | const REMOVE_ATTRS = [ 17 | 'index', 18 | 'type', 19 | 'package', 20 | 'class', 21 | 'checkable', 22 | 'checked', 23 | 'clickable', 24 | 'enabled', 25 | 'focusable', 26 | 'focused', 27 | 'long-clickable', 28 | 'password', 29 | 'scrollable', 30 | 'selected', 31 | 'bounds', 32 | 'rotation', 33 | ]; 34 | 35 | export {ATTR_MAP, REMOVE_ATTRS}; 36 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/lib/index.js: -------------------------------------------------------------------------------- 1 | import UniversalXMLPlugin from './plugin'; 2 | import {transformSourceXml} from './source'; 3 | import fs from 'fs'; 4 | export default UniversalXMLPlugin; 5 | export {UniversalXMLPlugin}; 6 | 7 | export function main() { 8 | const [, , xmlDataPath, platform, optsJson] = process.argv; 9 | const xmlData = fs.readFileSync(xmlDataPath, 'utf8'); 10 | let opts = {}; 11 | if (optsJson) { 12 | opts = JSON.parse(optsJson); 13 | } 14 | const {xml, unknowns} = transformSourceXml(xmlData, platform, opts); 15 | console.log(xml); // eslint-disable-line no-console 16 | if (unknowns.nodes.length || unknowns.attrs.length) { 17 | console.error(unknowns); // eslint-disable-line no-console 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/lib/logger.js: -------------------------------------------------------------------------------- 1 | import {logger} from 'appium/support'; 2 | const log = logger.getLogger('UniversalXMLPlugin'); 3 | export default log; 4 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/lib/transformers.js: -------------------------------------------------------------------------------- 1 | import {ATTR_PREFIX} from './source'; 2 | 3 | function ios(nodeObj /*, metadata*/) { 4 | return nodeObj; 5 | } 6 | 7 | function android(nodeObj, metadata) { 8 | // strip android:id from front of id 9 | const resId = nodeObj[`${ATTR_PREFIX}resource-id`]; 10 | if (resId && metadata.appPackage) { 11 | nodeObj[`${ATTR_PREFIX}resource-id`] = resId.replace(`${metadata.appPackage}:id/`, ''); 12 | } 13 | 14 | // turn bounds attr into rect-based attrs 15 | if (nodeObj[`${ATTR_PREFIX}bounds`]) { 16 | const boundsArray = nodeObj[`${ATTR_PREFIX}bounds`] 17 | .split(/\[|\]|,/) 18 | .filter((str) => str !== ''); 19 | const [x, y, x2, y2] = boundsArray; 20 | const width = x2 - x; 21 | const height = y2 - y; 22 | nodeObj[`${ATTR_PREFIX}x`] = x; 23 | nodeObj[`${ATTR_PREFIX}y`] = y; 24 | nodeObj[`${ATTR_PREFIX}width`] = width; 25 | nodeObj[`${ATTR_PREFIX}height`] = height; 26 | } 27 | } 28 | 29 | export default { 30 | ios, 31 | android, 32 | }; 33 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/test/fixtures/index.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const XML_IOS = fs.readFileSync(path.resolve(__dirname, 'ios.xml'), 'utf8').trim(); 5 | const XML_IOS_TRANSFORMED = fs.readFileSync(path.resolve(__dirname, 'ios-transformed.xml'), 'utf8').trim(); 6 | const XML_IOS_TRANSFORMED_INDEX_PATH = fs.readFileSync(path.resolve(__dirname, 'ios-transformed-path.xml'), 'utf8').trim(); 7 | const XML_IOS_EDGE = fs.readFileSync(path.resolve(__dirname, 'ios-edge.xml'), 'utf8').trim(); 8 | const XML_IOS_EDGE_TRANSFORMED = fs.readFileSync(path.resolve(__dirname, 'ios-transformed-edge.xml'), 'utf8').trim(); 9 | const XML_ANDROID = fs.readFileSync(path.resolve(__dirname, 'android.xml'), 'utf8').trim(); 10 | const XML_ANDROID_TRANSFORMED = fs.readFileSync(path.resolve(__dirname, 'android-transformed.xml'), 'utf8').trim(); 11 | const XML_ANDROID_TRANSFORMED_INDEX_PATH = fs.readFileSync(path.resolve(__dirname, 'android-transformed-path.xml'), 'utf8').trim(); 12 | const XML_WEBVIEW = fs.readFileSync(path.resolve(__dirname, 'web-view.xml'), 'utf8').trim(); 13 | 14 | export { XML_IOS, XML_ANDROID, XML_IOS_TRANSFORMED, XML_ANDROID_TRANSFORMED, XML_IOS_TRANSFORMED_INDEX_PATH, 15 | XML_ANDROID_TRANSFORMED_INDEX_PATH, XML_IOS_EDGE, XML_IOS_EDGE_TRANSFORMED, XML_WEBVIEW }; 16 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/test/fixtures/ios-transformed-edge.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/test/fixtures/web-view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Section 1
4 |
Section 2
5 | 6 | -------------------------------------------------------------------------------- /packages/universal-xml-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build", 4 | "checkJs": true 5 | }, 6 | "extends": "@appium/tsconfig/tsconfig.plugin.json", 7 | "include": ["lib"] 8 | } 9 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Mocha will load this file to configure the environment to use Chai as the 5 | * assertion library. Since Chai is a singleton, we can run into problems when 6 | * running files individually, if we have not carefully configured Chai in every 7 | * single test file. This file means less boilerplate and less random test 8 | * failures when running single test files. 9 | * 10 | * For simplicity, this file is _not_ transpiled. If it were, Mocha would need 11 | * to load different versions of this file depending on the test context (are we 12 | * running tests against the distfiles, or the source files?). 13 | * 14 | */ 15 | 16 | require('ts-node').register(); 17 | 18 | const chai = require('chai'); 19 | const chaiAsPromised = require('chai-as-promised'); 20 | const sinonChai = require('sinon-chai'); 21 | 22 | // The `chai` global is set if a test needs something special. 23 | // Most tests won't need this. 24 | global.chai = chai.use(chaiAsPromised).use(sinonChai); 25 | 26 | // `should()` is only necessary when working with some `null` or `undefined` values. 27 | global.should = chai.should(); 28 | -------------------------------------------------------------------------------- /triagers.json: -------------------------------------------------------------------------------- 1 | { 2 | "repos": [{ 3 | "user": "appium", 4 | "repo": "appium", 5 | "triagers": ["SrinivasanTarget", "jlipps", "vikramvi"], 6 | "autoLabels": ["NeedsTriage"], 7 | "validLabels": ["1.5-beta","Android","Bug","Cannot Reproduce","Documentation","DriverIssue","Enhancement","GUI","Hybrid Apps","iOS","Linux","LogRequested","Mac","Mobile Chrome","Mobile Safari","NeedsTriage","NoActivity","NotABug","OnGoing","Question","Real Devices","ReproNeeded","Robots","SemVer:Breaking","TechDebt","ThirdpartyIssue","Windows"] 8 | }], 9 | "bot": "triager" 10 | } 11 | --------------------------------------------------------------------------------