Compare commits

...
Author SHA1 Message Date
Christopher Hiller 8a6a4ba7e7 chore: publish
- appium@2.0.0-rc.5
 - @appium/base-driver@9.3.15
 - @appium/base-plugin@2.2.15
 - @appium/doctor@2.0.21
 - @appium/docutils@0.4.4
 - @appium/driver-test-support@0.4.10
 - @appium/eslint-config-appium@8.0.4
 - @appium/fake-driver@5.2.6
 - @appium/fake-plugin@3.1.19
 - @appium/plugin-test-support@0.3.18
 - @appium/schema@0.3.1
 - @appium/support@4.1.2
 - @appium/test-support@3.0.20
 - @appium/types@0.13.2
2023-07-03 13:18:58 -07:00
Christopher Hiller 52b646c65b chore(typedoc-plugin-appium): update snapshots 2023-07-03 13:10:51 -07:00
Christopher Hiller 2b351705d4 fix(base-driver): pass thru all type args to ExternalDriver
- `core` module is now TS
- `constants` module is now TS
- export missing `W3C_ELEMENT_KEY`
- `JWProxy.command` returns `Promise<unknown>`.  deal with it
- updated some type tests
2023-07-03 13:10:51 -07:00
Christopher Hiller c13333bb97 fix(appium): type fixes for ts v5 2023-07-03 13:10:51 -07:00
Christopher Hiller 62f4244753 fix(types): update some more types to reflect reality
- added a `Protocol` type for some string constants
- `proxyRouteIsAvoided` optionally accepts a `body` arg
- `Core<C>.driverForSession` does not actually return something using type `C`.
- `ExternalDriver` passes thru all type args to `Driver`. this should be non-breaking
- `StringRecord` is more appropriate type for default driver settings
- `Constraints` should be readonly
2023-07-03 13:10:51 -07:00
Christopher Hiller 26eb766a79 chore(base-driver): actually run type tests 2023-07-03 13:10:51 -07:00
Christopher Hiller 679865ed2f fix(eslint-config-appium): remove prototype assignment warning 2023-07-03 13:10:51 -07:00
Christopher Hiller f1d768c62c chore: upgrade typescript
we need to stay at v5.0.x for now
2023-07-03 13:10:51 -07:00
Christopher Hiller d6cca5175c fix(types): separate the type of opts from initialOpts
A thing we know about the `opts` object passed in to a driver constructor is that it does _not_ contain caps.  This value is retained in `this.initialOpts`.  Upon session creation, it _does_ contain caps.

This adds a new type, `InitialOpts`, which is the first parameter to a driver.  In the future, it may accept a type argument, as currently any extension-specific options (defined via schema in `package.json`) are not typed.

Because `DriverOpts` is a superset of `InitialOpts`, this should not be a breaking change.
2023-07-03 13:10:51 -07:00
Christopher Hiller 3d614d6d41 fix(base-driver): allow subclass to define shape of settings object
The mixin approach prevented subclasses of BaseDriver from narrowing the type of `this.settings`.  Now, that's possible.

Also:
- removed an unused type argument from BD constructor
- and did some typedef aliasing
- remove unneeded type assertion from log mixin
2023-07-03 13:10:51 -07:00
Christopher Hiller ee9b2a30de fix(types): ensure return type of deleteSession can always be void
a type aliased to `void` is not the same as returning `void`, apparently.  this might be fixed in newer TS versions
2023-07-03 13:10:51 -07:00
Christopher Hiller e30ad0e88c chore(appium,types): remove some useless type arguments 2023-07-03 13:10:51 -07:00
Christopher Hiller 01061b2919 fix(types,base-driver): remove deviceName from base constraints
This cap was conflicting with constraints defined in subclasses.  Currently, it isn't possible to cleanly "override" constraints.  Given `deviceName` is not used anywhere within base-driver's source, this seems like a decent workaround for now.
2023-07-03 13:10:51 -07:00
Christopher Hiller 24e7aeca7d fix(types): fix signature of updateSettings 2023-07-03 13:10:51 -07:00
renovate[bot] ed1856d720 fix(support): update dependency semver to v7.5.3 2023-07-01 16:31:57 +00:00
renovate[bot] 757fbaaf42 chore(deps): update dependency @types/node to v18.16.19 2023-07-01 12:54:42 +00:00
renovate[bot] fdd2ac098d fix(doctor): update dependency appium-adb to v9.11.7 2023-07-01 10:44:15 +00:00
renovate[bot] 3906f7d1ca chore(docutils): update dependency mkdocs-material to v9.1.17 2023-06-30 22:01:32 +00:00
Christopher Hiller f09fbb64dc fix(appium): restrict address to ipv6/hostname (#18824)
This resolves #18716.

For whatever reason, `json-schema-to-typescript` needed `"type": "string"` in there.  See https://github.com/bcherny/json-schema-to-typescript/issues/528 for further discussion
2023-06-30 13:33:34 -07:00
Mykola Mokhnach 97fe159276 chore(appium): Improve the error message on config load error (#18829) 2023-06-30 20:35:17 +02:00
renovate[bot] cb145875c0 chore(deps): update commitlint monorepo to v17.6.6 2023-06-30 17:38:50 +00:00
renovate[bot] d75338f1c8 chore(deps): update eslint-related packages to v5.59.11 2023-06-30 13:19:49 +00:00
renovate[bot] 67e7140216 chore(deps): update dependency lint-staged to v13.2.3 2023-06-30 11:39:58 +00:00
Christopher Hiller 81256f8f90 chore(types): fix clean command 2023-06-29 17:09:25 -07:00
Christopher Hiller 0bbb25dfb4 chore(base-driver,driver-test-support): do not run basedriver e2e tests in parallel
also: rewrote event timings tests due to it being unclear which assertion actually failed. one assertion per test.
2023-06-29 17:09:25 -07:00
renovate[bot] 5263366744 chore(deps): update dependency json-schema-to-typescript to v13.0.2 2023-06-29 20:56:36 +00:00
Christopher Hiller 4ab2780978 chore: run eslint --fix on ts files before commit 2023-06-29 13:14:37 -07:00
Jonathan Lipps 58b71869d1 chore: publish
- appium@2.0.0-rc.4
 - @appium/base-driver@9.3.14
 - @appium/base-plugin@2.2.14
 - @appium/doctor@2.0.20
 - @appium/docutils@0.4.3
 - @appium/driver-test-support@0.4.9
 - @appium/fake-plugin@3.1.18
 - @appium/plugin-test-support@0.3.17
 - @appium/support@4.1.1
 - @appium/test-support@3.0.19
 - @appium/types@0.13.1
2023-06-29 11:17:15 -07:00
renovate[bot] 08a1f1be94 chore(deps): update definitelytyped 2023-06-28 10:12:12 +00:00
renovate[bot] b7cecfffb5 chore(workflows): update actions/checkout digest to c85c95e 2023-06-28 08:20:51 +00:00
Kazuaki Matsuo 52a755a913 fix(docutils): docs versioning and update latest (#18804)
* fix: docs versioning and update latest

* fix lint
2023-06-27 21:13:12 -07:00
Mykola Mokhnach 35e0012d8a chore(appium): Use the actual server address in the logged URL (#18809) 2023-06-27 12:59:55 +02:00
Jonathan Lipps ed20e00754 chore: publish
- appium@2.0.0-rc.3
 - @appium/base-driver@9.3.13
 - @appium/base-plugin@2.2.13
 - @appium/doctor@2.0.19
 - @appium/docutils@0.4.2
 - @appium/driver-test-support@0.4.8
 - @appium/fake-driver@5.2.5
 - @appium/fake-plugin@3.1.17
 - @appium/plugin-test-support@0.3.16
 - @appium/support@4.1.0
 - @appium/test-support@3.0.18
 - @appium/types@0.13.0
2023-06-15 13:57:59 -07:00
Christopher Hiller 9e60e80b13 chore(renovate): add get-stream to major upgrade ban list 2023-06-15 11:57:15 -07:00
Jonathan LippsandChristopher Hiller 1a5b8cca2d fix(docutils): set mike version to major.minor by default (#18770)
* fix(docutils): set mike version to major.minor by default

* Update packages/docutils/lib/builder/deploy.ts

Co-authored-by: Christopher Hiller <boneskull@boneskull.com>

---------

Co-authored-by: Christopher Hiller <boneskull@boneskull.com>
2023-06-15 10:19:05 -07:00
Mykola Mokhnach ced560206b ci: Update bug report template (#18766) 2023-06-15 06:30:23 +02:00
Christopher Hiller 7dbf6f2cd3 chore(typedoc-plugin-appium): update snapshots 2023-06-14 16:21:51 -07:00
Christopher Hiller 84c49b1529 chore(support): fix module paths in some tests 2023-06-14 16:21:51 -07:00
Christopher Hiller 81bc527be6 fix(types): various fixes for reality
- because BaseDriver implements `findElOrEls` (technically), any subclasses must not inherit an interface containing a non-identical `findElOrEls` -- not even narrowing.  so to avoid this, we will need to make it as broad as possible; no more generic `Ctx`
- likewise with `getLog`
- fix return type of `setGeoLocation`
- fix return type of `getPerformanceData`
- fix return type of `removeApp`
- fix return type of `hideKeyboard`
- fix return type of `getSystemBars`
- fix signature of various `touch*` methods
- fix return type of `setNetworkConnection`

alternatively, the deprecated methods mentioned here should be removed.
2023-06-14 16:21:51 -07:00
Christopher Hiller 2e909478e9 chore(types): remove unused interfaces
Both `StartScreenRecordOptions` and `StopScreenRecordOptions` are for deprecated core APIs and are defined individually by drivers
2023-06-14 16:21:51 -07:00
Christopher Hiller 00ae1625d7 fix(types,fake-driver): getLog doesn't necessarily return an array 2023-06-14 16:21:51 -07:00
Christopher Hiller 88293f8039 feat(support): export a bunch of types that weren't properly exported 2023-06-14 16:21:51 -07:00
Christopher Hiller 54a4a8da03 fix(base-driver): update def of findElOrEls and findElOrElsWithProcessing
- adds some types to the `Proxy` class
- make `isErrorType()` a proper type guard
2023-06-14 16:21:51 -07:00
Christopher Hiller 598b255e8d fix(fake-driver): update definition of findElOrEls 2023-06-14 16:21:51 -07:00
Christopher Hiller 63a5c22127 fix(types)!: use overload instead of broken conditional type in findElOrEls
BREAKING CHANGE: This changes the def of `findElOrEls` and `findElOrElsWithProcessing` in `ExternalDriver` in a breaking manner.

So conditional types don't work like how I was using them.  They work in capital-T-types (`type`), but they don't work as return values.  Instead, what we should have is an overload.  in this case, the function can be called one of two ways, and it returns a different thing depending on those values.  _unfortunately_ this is a little hinky to implement, given that overloads are unsupported in _object literals_.  they are supported in function statements or expressions, and they are supported in class definitions, but not object literals.  you'll see what needs to happen to make this work in `FakeDriver` and `BaseDriver` in subsequent changesets; essentially the function must be defined outside of the object literal and then stuffed in there.

The other change here changes an `object` type (which does not allow arbitrary properties) to a `StringRecord` type within the type which converts `Constraints` to `Capabilities`.  This affects caps for some drivers such as `chromedriverArgs` in `appium-android-driver`; anywhere where the constraint has `isObject: true`.
2023-06-14 16:21:51 -07:00
Jonathan Lipps af066eb5c3 chore: publish
- appium@2.0.0-rc.2
 - @appium/base-driver@9.3.12
 - @appium/base-plugin@2.2.12
 - @appium/doctor@2.0.18
 - @appium/docutils@0.4.1
 - @appium/fake-plugin@3.1.16
 - @appium/support@4.0.3
 - @appium/test-support@3.0.17
2023-06-14 11:21:00 -07:00
Christopher Hiller e5254b5b45 fix(docutils): fixes some weirdness with default behavior and the "implies" keyword (#18751)
* fix(docutils): fixes some weirdness with default behavior and the "implies" keyword

- also squashes an errrant warning
- fix some diff logic when writing to `mkdocs.yml`

* fix(docutils): more robust mike-finding
2023-06-14 11:18:55 -07:00
Mykola Mokhnach c18eb03261 fix(support): Require the optional sharp dep properly from mjpeg module (#18769) 2023-06-14 20:17:25 +02:00
Jonathan Lipps d0ebbaa0c6 chore: publish
- appium@2.0.0-rc.1
 - @appium/base-driver@9.3.11
 - @appium/base-plugin@2.2.11
 - @appium/doctor@2.0.17
 - @appium/docutils@0.4.0
 - @appium/driver-test-support@0.4.7
 - @appium/execute-driver-plugin@3.0.14
 - @appium/fake-driver@5.2.4
 - @appium/fake-plugin@3.1.15
 - @appium/images-plugin@2.1.2
 - @appium/opencv@2.1.1
 - @appium/plugin-test-support@0.3.15
 - @appium/schema@0.3.0
 - @appium/strongbox@0.3.1
 - @appium/support@4.0.2
 - @appium/test-support@3.0.16
 - @appium/typedoc-plugin-appium@0.6.5
 - @appium/types@0.12.0
 - @appium/universal-xml-plugin@1.0.9
2023-06-14 10:34:52 -07:00
Jonathan Lipps 1629f858f8 chore(appium): bump appium version to rc 2023-06-14 10:32:01 -07:00
Jonathan Lipps 2d124323c5 test(appium): attempt to make windows CI pass 2023-06-13 14:32:28 -07:00
Jonathan Lipps 901ac711c4 fix: ensure logServerAddress gets an actual URL (#18754) 2023-06-12 16:40:42 -07:00
Mykola Mokhnach 7982747677 chore(appium): Make sharp to an optional dependency (#18733) 2023-06-06 08:43:26 +02:00
Christopher Hiller 26bb0f95b9 chore(support): punt some windows fs tests 2023-06-05 15:34:01 -07:00
Christopher Hiller c161ec2bad chore(fake-driver): bump timeout of tests 2023-06-05 15:34:01 -07:00
Christopher Hiller 1c4737c8b4 chore(appium): fix test for win 2023-06-05 15:34:01 -07:00
Christopher Hiller 6b2f5668e9 chore(doctor): fix tests for win 2023-06-05 15:34:01 -07:00
Christopher Hiller d904477b4c chore(ci): remove mac from build matrix
I have a feeling we are bumping up against memory limitations.
2023-06-05 15:34:01 -07:00
Christopher Hiller da1da3fe13 chore(doctor): attempt to fix tests for win 2023-06-05 15:34:01 -07:00
Christopher Hiller 307c81a24b chore(ci): fix invalid workflow 2023-06-05 15:34:01 -07:00
renovate[bot] f4c37985eb chore(deps): update dependency eslint to v8.42.0 2023-06-03 09:01:42 +00:00
renovate[bot] 78c6794253 fix(doctor): update dependency appium-adb to v9.11.5 2023-06-02 13:33:41 +00:00
Christopher Hiller c416a86120 chore(appium): fix arg parser test (temp fix) (#18718) 2023-06-01 11:17:13 -07:00
Mykola Mokhnach 38f86900da fix: Do not use additional checks on opencv objects (#18714) 2023-06-01 06:31:30 +02:00
Christopher Hiller 40fc097eda chore(ci): avoid build on macos 14.x due to unexplained failures 2023-05-31 14:17:09 -07:00
Christopher Hiller 0097c8e52e chore(support): fix fs.walkdir test for win compat 2023-05-31 14:17:09 -07:00
Christopher Hiller 1ccf857a4b fix(strongbox): do not force-lowercase slugged containers 2023-05-31 14:17:09 -07:00
Christopher Hiller c34f2eb897 chore: use glob mode w/ rimraf 2023-05-31 14:17:09 -07:00
Christopher Hiller 1d7c58ff26 chore: fix/skip tests on windows 2023-05-31 14:17:09 -07:00
Christopher Hiller d78ca63e60 chore(strongbox): tests do not assume posix paths 2023-05-31 14:17:09 -07:00
Jonathan Lipps 4f171ee92f ci: add windows and macos to ci matrix 2023-05-31 14:17:09 -07:00
Christopher Hiller fd2c72fd88 fix(types): fix broken appium config schema types, temporarily
This appears to be a bug in `json-schema-to-typescript`, where the `ipv6` type converts to `{[key: string]: unknown}` instead of `string`.

For now, we need to drop all formatting requirements from the `address` prop until this is fixed.

This did not break CI because the `build` script of `@appium/types` was run either _after_ building the rest of the project, or not at all, and the resulting change to `appium-config.ts` was not committed.
2023-05-31 14:02:28 -07:00
renovate[bot] 3bf406015e fix(universal-xml-plugin): update dependency @xmldom/xmldom to v0.8.8 2023-05-31 12:15:12 +00:00
renovate[bot] 56499eb997 fix(types): update dependency type-fest to v3.11.1 2023-05-31 09:01:09 +00:00
renovate[bot] bd10bffec4 chore(docutils): update dependency mkdocs-material to v9.1.15 2023-05-30 13:02:24 +00:00
renovate[bot] b1ee41cfc7 chore(deps): update commitlint monorepo to v17.6.5 2023-05-30 10:54:49 +00:00
renovate[bot] cc7f5f4a52 chore(deps): update eslint-related packages to v5.59.8 2023-05-30 09:05:58 +00:00
renovate[bot] a98d9c33c7 fix(appium): update dependency winston to v3.9.0 2023-05-27 16:32:08 +00:00
renovate[bot] 1a2260bb15 fix(docutils): update dependency yaml to v2.3.1 2023-05-27 12:35:19 +00:00
renovate[bot] 4c52fdf1d0 chore(deps): update dependency @types/node to v18.16.16 2023-05-27 09:34:42 +00:00
Christian Bromann a959eec9cf docs(appium): Add contributing documentation (#18691) 2023-05-26 22:57:01 -07:00
renovate[bot] e96724077e fix(schema): update definitelytyped 2023-05-26 10:39:52 +00:00
Mykola Mokhnach dfae94e7f3 ci: Remove v14 from the test matrix and add v20 (#18679) 2023-05-25 22:46:03 +02:00
Mykola Mokhnach 0497090a90 fix(images-plugin): Make sure the template is always a buffer (#18696) 2023-05-25 19:16:03 +02:00
renovate[bot] 19277f6e14 fix(types): update dependency type-fest to v3.11.0 2023-05-25 09:05:01 +00:00
renovate[bot] 39a59c8bb3 chore(deps): update dependency json-schema-to-typescript to v13 2023-05-24 14:27:42 -07:00
Christopher Hiller 2766dcca4c feat(docutils): init: scafffold package.json
This makes `appium-docs init` add a needed prop (`typedoc.entryPoint`) to `package.json` of an extension. It will write to `package.json` _by default_, even if it exists--which is different than the default behavior of other scaffolding tasks.

Note that `appium-docs init` will now _fail_ if `--entry-point <some-file>` (or `-e`) is not provided; there's no way for us to guess what it is (this is the _source_ entry point; not necessarily the `main` file). I guess that's technically breaking, but `appium-docs init` should not be part of anyone's workflow.
2023-05-24 14:26:07 -07:00
Christopher Hiller f5b112bcca docs(appium): fix weird sentence 2023-05-24 13:15:56 -07:00
Christopher HillerandJonathan Lipps 5d6458b611 Update packages/appium/docs/en/ecosystem/build-docs.md
Co-authored-by: Jonathan Lipps <jlipps@gmail.com>
2023-05-24 13:15:56 -07:00
Christopher Hiller d47c805468 docs(appium): update build-docs.md 2023-05-24 13:15:56 -07:00
renovate[bot] 4c61d533b8 fix(test-support): update dependency sinon to v15.1.0 2023-05-24 13:51:21 +00:00
renovate[bot] 325d237207 fix(docutils): update dependency yaml to v2.3.0 2023-05-24 10:47:01 +00:00
Mykola Mokhnach 8f63dabb5d feat(appium): Allow to provide ipv6 addresses and handle broadcast addresses in logs (#18674) 2023-05-24 08:26:10 +02:00
renovate[bot] 5a555090c5 fix(support): update dependency semver to v7.5.1 2023-05-23 14:20:57 +00:00
renovate[bot] 81ed72f19d chore(deps): update eslint-related packages to v5.59.7 2023-05-23 11:07:31 +00:00
renovate[bot] f34c75c650 fix(doctor): update dependency appium-adb to v9.11.4 2023-05-22 13:47:30 +00:00
renovate[bot] 39d68f17a2 chore(deps): update dependency @types/node to v18.16.14 2023-05-22 08:11:22 +00:00
renovate[bot] 528dcfe324 fix(support): update dependency @types/semver to v7.5.0 2023-05-21 13:12:03 +00:00
renovate[bot] 4d7f39b5f8 chore(deps): update dependency eslint to v8.41.0 2023-05-21 10:36:29 +00:00
renovate[bot] 8594d13bf6 fix(execute-driver-plugin): update dependency vm2 to v3.9.19 2023-05-21 08:10:32 +00:00
renovate[bot] a0b434dff7 fix(doctor): update dependency appium-adb to v9.11.3 2023-05-20 13:03:53 +00:00
renovate[bot] 19a5ae4537 chore(docutils): update dependency mkdocs-material to v9.1.14 2023-05-20 11:23:01 +00:00
renovate[bot] fc831d2475 chore(deps): update eslint-related packages to v5.59.6 2023-05-20 11:19:08 +00:00
renovate[bot] cb1a97fc8e chore(deps): update dependency midnight-smoker to v2.0.4 2023-05-20 08:02:34 +00:00
Christopher Hiller a506a0a357 chore: publish
- appium@2.0.0-beta.71
 - @appium/base-driver@9.3.10
 - @appium/base-plugin@2.2.10
 - @appium/fake-plugin@3.1.14
2023-05-19 16:47:14 -07:00
Christopher Hiller 63a429c53b fix(base-driver): add missing @types/lodash
- the lack of this module was causing base-driver to fail to compile when installed as a production module.  generally, nobody is going to do such a thing--consumers will use the generated `.d.ts` files--UNLESS they happen to be running typedoc against their extension.

will need to figure out a way to test this to make sure it doesnt happen again.
2023-05-19 16:45:54 -07:00
124 changed files with 3067 additions and 3387 deletions
+21 -9
View File
@@ -6,6 +6,20 @@ body:
- type: markdown
attributes:
value: ':stop_sign: _For support questions, please visit [http://discuss.appium.io](http://discuss.appium.io) instead._'
- type: checkboxes
attributes:
label: 'Do I have the most recent component updates?'
description: 'Appium 1-related issues are going to be ignored as this version has reached its end of life.'
options:
- label: 'I use the most recent available driver/plugin and server versions'
required: true
- type: checkboxes
attributes:
label: 'Is the component officially supported by the Appium team?'
description: 'Make sure you report an issue for a component which is officially supported/maintained by the Appium team.'
options:
- label: 'I have verified the component repository is present under the Appium organization in GitHub'
required: true
- type: checkboxes
attributes:
label: 'Is there an existing issue for this?'
@@ -34,7 +48,7 @@ body:
An easy way to get started is to copy [sample code](https://github.com/appium/appium/tree/master/packages/appium/sample-code) and modify it accordingly.
If the result is more than a screenful of text _or_ requires multiple files, please:
- _Attach_ (do not paste) it to this textarea, _or_
- Put it in a [Gist](https://gist.github.com) and paste the link, _or_
- Provide a link to a new or existing public repository exhibiting the issue
@@ -46,14 +60,12 @@ body:
description: 'Please provide the following information about your environment; feel free to remove any items which are not relevant.'
value: |
- Operating system:
- If running via `appium` CLI...
- Appium CLI version (output of `appium --version`):
- Node.js version (output of `node --version`):
- `npm` version (output of `npm --version`):
- Last `appium` version which did _not_ exhibit the problem:
- If running Appium Desktop, its version:
- Appium server version (output of `appium --version`):
- Appium driver(s) and their version(s):
- Appium plugin(s) and their version(s):
- Node.js version (output of `node --version`):
- `npm` version (output of `npm --version`):
- Last component(s) version which did _not_ exhibit the problem:
- Platform and version under test:
- Real device or emulator/simulator:
validations:
@@ -63,9 +75,9 @@ body:
label: 'Link to Appium Logs'
description: |
Create a [Gist](https://gist.github.com)—which contains your _full_ Appium logs—and link it here. Alternatively, you can attach a logfile to this issue (drag it into the "Further Information" field below).
:warning: _Remember to redact or remove any sensitive information!_
placeholder: 'https://gist.github.com/...'
placeholder: 'https://gist.github.com/...'
- type: textarea
attributes:
label: Futher Information
+18 -6
View File
@@ -44,19 +44,31 @@ permissions:
jobs:
test:
name: Tests
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x, 20.x]
os:
- ubuntu-latest
- windows-latest
# TODO: Enable below envs after all tests have been verified green
# - macos-latest
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install latest npm
- name: Install latest npm (win)
if: ${{ matrix.os == 'windows-latest' }}
# for some reason there's a bug in upgrading npm on win, have to install a specific version first
# https://github.com/actions/setup-node/issues/623
run: |
npm install -g npm@8.3.1
npm install -g npm@latest
- name: Install latest npm (non-win)
if: ${{ matrix.os != 'windows-latest' }}
run: npm install -g npm
- name: Install dependencies
uses: bahmutov/npm-install@1a235c31658a322a3b024444759650ee6345c26d # tag=v1
@@ -70,7 +82,7 @@ jobs:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
- name: Install dependencies
uses: bahmutov/npm-install@1a235c31658a322a3b024444759650ee6345c26d # tag=v1
with:
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
- name: Cleanup
run: |
+1 -2
View File
@@ -14,8 +14,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
# node-version: [14.x, 16.x, 18.x]
node-version: [16.x, 18.x]
node-version: [16.x, 18.x, 20.x]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Use Node.js ${{ matrix.node-version }}
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
- name: Install dependencies (Node.js)
uses: bahmutov/npm-install@1a235c31658a322a3b024444759650ee6345c26d # tag=v1
with:
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
name: Run FOSSA Analysis
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
with:
ref: master
- name: Install dependencies
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
name: Check Renovate Configs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3
- name: Validate Renovate Config
uses: rinchsan/renovate-config-validator@1ea1e8514f6a33fdd71c40b0a5fa3512b9e7b936 # tag=v0
with:
+1 -2
View File
@@ -1,6 +1,5 @@
module.exports = {
'*.js': ['eslint --fix', 'prettier --write'],
'*.ts': ['prettier --write'],
'*.(js|ts)': ['eslint --fix', 'prettier --write'],
'appium-config-schema.js': () => [
'npm run --workspace=./packages/schema build',
'git add -A packages/schema/lib/appium-config.schema.json',
+5
View File
@@ -0,0 +1,5 @@
{
"recommendations": [
"stateful.runme"
]
}
+949 -2502
View File
File diff suppressed because it is too large Load Diff
+15 -14
View File
@@ -34,7 +34,7 @@
"clean": "run-s clean:distfiles clean:workspaces",
"clean:dangerously": "run-s clean:workspaces clean:force",
"clean:deps": "lerna clean --yes",
"clean:distfiles": "npx rimraf \"./packages/*/build\" \"./packages/*/**/.tsconfig.tsbuildinfo\"; npx nx reset",
"clean:distfiles": "npx rimraf --glob \"./packages/*/build\" \"./packages/*/**/.tsconfig.tsbuildinfo\"; npx nx reset",
"clean:interactive": "git clean -d -x -i",
"clean:force": "git clean -d -x -f",
"clean:workspaces": "lerna run clean || true",
@@ -85,29 +85,30 @@
},
"devDependencies": {
"@colors/colors": "1.5.0",
"@commitlint/cli": "17.6.3",
"@commitlint/config-conventional": "17.6.3",
"@commitlint/cli": "17.6.6",
"@commitlint/config-conventional": "17.6.6",
"@tsconfig/node14": "1.0.3",
"@types/chai": "4.3.5",
"@types/chai-as-promised": "7.1.5",
"@types/diff": "5.0.3",
"@types/mocha": "10.0.1",
"@types/node": "18.16.13",
"@types/semver": "7.3.13",
"@types/node": "18.16.19",
"@types/semver": "7.5.0",
"@types/sinon": "10.0.15",
"@types/sinon-chai": "3.2.9",
"@types/source-map-support": "0.5.6",
"@types/teen_process": "2.0.0",
"@types/ws": "8.5.4",
"@types/ws": "8.5.5",
"@types/yargs": "17.0.24",
"@typescript-eslint/eslint-plugin": "5.59.2",
"@typescript-eslint/parser": "5.59.2",
"@typescript-eslint/eslint-plugin": "5.59.11",
"@typescript-eslint/parser": "5.59.11",
"asyncbox": "2.9.4",
"chai": "4.3.7",
"chai-as-promised": "7.1.1",
"chai-webdriverio-async": "2.7.0",
"conventional-changelog-conventionalcommits": "5.0.0",
"cpy-cli": "4.2.0",
"eslint": "8.40.0",
"eslint": "8.42.0",
"eslint-config-prettier": "8.8.0",
"eslint-find-rules": "4.1.0",
"eslint-import-resolver-typescript": "3.5.5",
@@ -119,11 +120,11 @@
"get-port": "5.1.1",
"handlebars": "4.7.7",
"husky": "8.0.3",
"json-schema-to-typescript": "12.0.0",
"json-schema-to-typescript": "13.0.2",
"lerna": "6.6.2",
"lint-staged": "13.2.2",
"lint-staged": "13.2.3",
"log-symbols": "4.1.0",
"midnight-smoker": "2.0.3",
"midnight-smoker": "2.0.4",
"mjpeg-consumer": "2.0.0",
"mjpeg-server": "0.3.1",
"mocha": "10.2.0",
@@ -133,7 +134,7 @@
"rewiremock": "3.14.5",
"rimraf": "5.0.1",
"serve-static": "1.15.0",
"sinon": "15.0.4",
"sinon": "15.1.0",
"sinon-chai": "3.7.0",
"sync-monorepo-packages": "1.0.2",
"through2": "4.0.2",
@@ -142,7 +143,7 @@
"typedoc": "0.23.28",
"typedoc-plugin-markdown": "3.14.0",
"typedoc-plugin-resolve-crossmodule-references": "0.3.3",
"typescript": "4.9.5",
"typescript": "5.0.4",
"validate.js": "0.13.1",
"webdriverio": "7.31.1",
"ws": "8.13.0",
+65
View File
@@ -3,6 +3,71 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.0.0-rc.5](https://github.com/appium/appium/compare/appium@2.0.0-rc.4...appium@2.0.0-rc.5) (2023-07-03)
### Bug Fixes
* **appium:** restrict address to ipv6/hostname ([#18824](https://github.com/appium/appium/issues/18824)) ([f09fbb6](https://github.com/appium/appium/commit/f09fbb64dce0e179a007f3a0ae9800e61fbe90eb)), closes [#18716](https://github.com/appium/appium/issues/18716)
* **appium:** type fixes for ts v5 ([c13333b](https://github.com/appium/appium/commit/c13333bb979ee3409e6a62a5800c629781553b42))
* **support:** update dependency semver to v7.5.3 ([ed1856d](https://github.com/appium/appium/commit/ed1856d720c442842d5c3fe5f6caeccb8229b5f1))
## [2.0.0-rc.4](https://github.com/appium/appium/compare/appium@2.0.0-rc.3...appium@2.0.0-rc.4) (2023-06-29)
**Note:** Version bump only for package appium
## [2.0.0-rc.3](https://github.com/appium/appium/compare/appium@2.0.0-rc.2...appium@2.0.0-rc.3) (2023-06-15)
**Note:** Version bump only for package appium
## [2.0.0-rc.2](https://github.com/appium/appium/compare/appium@2.0.0-rc.1...appium@2.0.0-rc.2) (2023-06-14)
**Note:** Version bump only for package appium
## [2.0.0-rc.1](https://github.com/appium/appium/compare/appium@2.0.0-beta.71...appium@2.0.0-rc.1) (2023-06-14)
### Features
* **appium:** Allow to provide ipv6 addresses and handle broadcast addresses in logs ([#18674](https://github.com/appium/appium/issues/18674)) ([8f63dab](https://github.com/appium/appium/commit/8f63dabb5dca882e522026f243e3445f5418874b))
### Bug Fixes
* **appium:** update dependency winston to v3.9.0 ([a98d9c3](https://github.com/appium/appium/commit/a98d9c33c761af6870411721cc0d6c4491b43940))
* **docutils:** update dependency yaml to v2.3.0 ([325d237](https://github.com/appium/appium/commit/325d2372078ee01e8fb58613ffd13417f2527151))
* **docutils:** update dependency yaml to v2.3.1 ([1a2260b](https://github.com/appium/appium/commit/1a2260bb15d75e4f4c23551dc61529108a7b0d88))
* ensure logServerAddress gets an actual URL ([#18754](https://github.com/appium/appium/issues/18754)) ([901ac71](https://github.com/appium/appium/commit/901ac711c4e2207f30d93ad177a332b7d6f23c38))
* **support:** update dependency @types/semver to v7.5.0 ([528dcfe](https://github.com/appium/appium/commit/528dcfe324e75e48471cdb694448c2a09ec2d828))
* **support:** update dependency semver to v7.5.1 ([5a55509](https://github.com/appium/appium/commit/5a555090c5322318f845415d743ee5c645fb94a0))
* **types:** fix broken appium config schema types, temporarily ([fd2c72f](https://github.com/appium/appium/commit/fd2c72fd886ecaaf0f6588a328ff8a268a54fb55))
* **types:** update dependency type-fest to v3.11.0 ([19277f6](https://github.com/appium/appium/commit/19277f6e14a56e52b4669d633e148ad4a3da2c7a))
* **types:** update dependency type-fest to v3.11.1 ([56499eb](https://github.com/appium/appium/commit/56499eb997b551739bed628f057de7987674ea7f))
## [2.0.0-beta.71](https://github.com/appium/appium/compare/appium@2.0.0-beta.70...appium@2.0.0-beta.71) (2023-05-19)
**Note:** Version bump only for package appium
## [2.0.0-beta.70](https://github.com/appium/appium/compare/appium@2.0.0-beta.69...appium@2.0.0-beta.70) (2023-05-19)
**Note:** Version bump only for package appium
+100 -2
View File
@@ -1,5 +1,6 @@
---
title: Contributing to Appium
cwd: ../../../../../
---
This section of Appium's documentation is for those who are interested in contributing to Appium's
@@ -16,8 +17,105 @@ already.
## Ways to Contribute
TODO
The project offers a variety of ways to contribute, e.g.:
- contributing code
- improving documentation
- creating educational content (blog posts, tutorials, videos, etc.)
- spreading the good word about the project (e.g. via Twitter)
- filing bugs if you discover them while using Appium
- making feature requests if you are missing something in the project or help triaging bugs
- supporting users in the [Appium forum](https://discuss.appium.io/)
## The Appium Development Process
TODO
To contribute to the Appium code base make sure to check out the Git repository.
!!! info
If you are VS Code user you can easily check out the project using [Runme](https://runme.dev/api/runme?repository=https%3A%2F%2Fgithub.com%2Fappium%2Fappium.git&fileToOpen=packages%2Fappium%2Fdocs%2Fen%2Fcontributing%2Findex.md).
It is advised to [fork](https://github.com/appium/appium/fork) before cloning it to your system.
```sh { name=clone }
export GITHUB_USERNAME=<your-username>
git clone git@github.com:$GITHUB_USERNAME/appium.git
```
After cloning you can go ahead and install the project dependencies:
```sh { name=install }
npm install
```
From here on there are several things you can do.
### Watch Files
When developing Appium code we have to watch all JavaScript and TypeScript files to re-compile them after every change. You can run this watch process via:
```sh { name=watch, background=true }
npm run dev
```
### Start Appium in Dev-Mode
To test your changes you can run Appium in dev mode via:
```sh { name=start }
npm start
```
### Run Tests
The project maintains a set of different test variations you can run to verify the quality of the code.
#### Linting
Appium uses EsLint for static code analysis and linting. You can run these checks via:
```sh { name=test-linting }
npm run lint
```
#### Unit
Run via:
```sh { name=test-unit }
npm run test:unit
```
You can also run tests for specific workspaces, e.g.:
```sh { name=test-workspace }
export APPIUM_WORKSPACE=@appium/doctor
npm run test:unit -w $APPIUM_WORKSPACE
```
#### Smoke and e2e Tests
Run via:
```sh { name=test-slow }
npm run test:slow
```
### Deploy Docs Locally
Our documentation system uses [MKDocs](https://www.mkdocs.org/) and therefor requires [Python](https://www.python.org/) to be installed on your system. You can run the docs by:
```sh
# installing needed Python dependencies
pip install -r packages/docutils/requirements.txt
# build the project
npm run build
# run dev server
npm run dev:docs
```
You should be able to view the page at `http://127.0.0.1:8000/docs/en`:
```sh
open http://127.0.0.1:8000/docs/en
```
+184 -36
View File
@@ -2,11 +2,11 @@
title: Building Docs for Appium Extensions
---
Once you've [built a driver](./build-drivers.md) or [built a plugin](./build-plugins.md) for
Appium, you will hopefully want to document how that extension works for your users. The most basic
way of doing this is to write up a quick README and keep it in the root of your project's
repository. However, this can involve a lot of duplication of effort, especially when documenting
things like Appium commands.
Once you've [built a driver](./build-drivers.md) or [built a plugin](./build-plugins.md) for Appium,
you will hopefully want to document how that extension works for your users. The most basic way of
doing this is to write up a quick `README.md` and keep it in the root of your project's repository.
However, this can involve a lot of duplication of effort, especially when documenting things like
Appium commands.
Let's say your driver implements ~25 of the standard WebDriver protocol commands. You could write
up a description of these commands, how they map to the protocol, what parameters they take, and
@@ -16,16 +16,16 @@ this information in two places creates an opportunity for the docs to get out of
reality of the code. Wouldn't it be nice to generate command reference documentation straight from
the code?
Another problem with the basic single file README approach is that many extensions might want
a whole set of documents including longer prose guides (like this one). It might be nice to have
code examples where you can toggle between different programming languages. It might be nice to be
able to add a project-specific logo. And so on.
Another problem with the basic single file `README.md` approach is that many extensions might want a
whole set of documents including longer prose guides (like this one). It might be nice to have code
examples where you can toggle between different programming languages. It might be nice to be able
to add a project-specific logo. And so on.
Luckily, the Appium project has built tools to do all these things, and we've packaged up these
tools so our ecosystem developers building drivers and plugins can _also_ use them. The best way to
get going with these tools is probably to look at an existing Appium driver repo to see how it's
done, for example the [XCUITest driver repo](https://github.com/appium/appium-xcuitest-driver). But
this guide will outline the basic approach.
The Appium project has built tools to do all these things, and we've packaged up these tools so our
ecosystem developers building drivers and plugins can _also_ use them. The best way to get going
with these tools is probably to look at an existing Appium driver repo to see how it's done, for
example the [XCUITest driver repo](https://github.com/appium/appium-xcuitest-driver). But this guide
will outline the basic approach.
### Conceptual architecture
@@ -42,32 +42,180 @@ The other main piece is automatic documentation generation from your code files.
a plugin for [TypeDoc](https://typedoc.org/). This plugin is incorporated into our doc utility.
When you give it an entrypoint for you driver or plugin, it will scan and parse all your code files
looking for Appium command implementations. A set of Markdown reference files will be generated for these
commands, which can then be included in your docs site.
commands, which will then be included in your docs site.
> Note: Implementing an extension in TypeScript is _not_ a requirement for generating documentation,
> but for automated doc generation to work, you will need to apply TypeScript-supported JSDoc-style
> docstrings to your JS codebase. See ["JS Projects Utilizing TypeScript"](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html) for more information.
In order to make different versions of your docs available (one for each minor release of your
extension, typically), we also bundle [Mike](https://github.com/jimporter/mike).
### Setup
### Prerequisites
To take advantage of Appium's documentation utilities, you'll need to make sure of the following:
To take advantage of Appium's documentation utilities, you'll need to install:
- Python 3 is available
- The appropriate Python dependencies are available (the versions used by Appium itself are listed
- in the doc utility's
[requirements.txt](https://github.com/appium/appium/blob/master/packages/docutils/requirements.txt)).
It's recommended you use the same versions.
- Include the `@appium/docutils` NPM package as a dev dependency of your project
- Annotate your commands using TypeDoc (this works for JS projects and not just TypeScript
projects).
- Put your markdown docs files in a directory
- Create a [`mkdocs.yml`](https://www.mkdocs.org/user-guide/configuration/) file that extends
Appium's base MkDocs configuration. (If your `mkdocs.yml` file is in the root of your repo, then
the line at the top of it should look like:
```yml
INHERIT: ./node_modules/@appium/docutils/base-mkdocs.yml
```
Basically, make sure you're inheriting the path to `@appium/docutils`'s `base-mkdocs.yml`.
- [Python v3+](https://www.python.org/downloads/)
- [pip](https://pip.pypa.io/en/stable/installation/) (this may be installed automatically with Python)
- The `@appium/docutils` package:
```bash
npm install --save-dev @appium/docutils
```
### Initializing an Extension for Building Docs
To prepare your extension for generating documentation, run the following command:
```bash
npx appium-docs init --entry-point <my-entry-point.js>
```
...where `<my-entry-point.js>` is the **source entry point** to your extension. If you _are not_ transpiling your code via TypeScript, Babel, etc., this is typically the same as the value of the `main` property in `package.json`. If you _are_ transpiling, this is typically different. For example, your `main` property may be `dist/index.js`, but your **source entry point** is `src/index.ts`.
This will:
1. Create a `tsconfig.json` if one does not already exist. This is necessary even if your extension is not written in TypeScript.
2. Create a `typedoc.json` with the necessary configuration for TypeDoc.
3. Create a `mkdocs.yml` with the necessary configuration for MkDocs.
4. Modify your `package.json` to add a `typedoc.entryPoint` property with a value of your entry point (as specified above).
### Documenting Your Extension
At this point, you can begin documenting your extension. You don't need to do this all at once, but you should make the following changes, at minimum.
#### `newMethodMap` and `executeMethodMap`
The static properties `newMethodMap` and `executeMethodMap` may be present on your extension's main class. If they are not, then you can skip to the next section. If they are, you will need to make the following changes, depending on your extension's language.
##### JavaScript
```js
// note: this is equivalent to the TypeScript example below
class MyExtension {
static newMethodMap = /** @type {const} */({
// ...
});
static executeMethodMap = /** @type {const} */({
// ...
});
}
```
##### TypeScript
```ts
class MyExtension {
static newMethodMap = {
// ...
} as const;
static executeMethodMap = {
// ...
} as const;
}
```
#### Driver Constraints
> Note: Plugin authors can skip this section.
Your driver may have a property `desiredCapConstraints`. It should also follow the same pattern as `newMethodMap` and `executeMethodMap` above. For example:
```js
class MyExtension {
desiredCapConstraints = /** @type {const} */({
myCapability: {
presence: true,
isString: true
},
myOtherCap: {
isBoolean: true
}
});
}
```
(For extensions written in TypeScript, use `as const` as before.)
#### Commands
The documentation for a command, as defined in your extension, comes from multiple places. These sources are then combined as needed into the final output.
In Appium, new commands are defined in `newMethodMap` and execute methods are defined in `executeMethodMap`. **The value of these properties are used to build your documentation.** In particular, parameter names and optional/required status _override_ whatever method implementation does. So for example, if your `newMethodMap` contains:
```js
class MyExtension {
static newMethodMap = /** @type {const} */({
'/session/:sessionId/myThing': {
/**
* Does my thing
*/
GET: {command: 'doMyThing', payloadParams: {required: ['a', 'b']}},
}
})
}
```
...and your `doMyThing` method implementation looks like this:
```js
class MyExtension {
/**
* Doesn't do my thing
* @param {any} a - Whatever
* @param {number} d - Some number
* @param {boolean} c - Some boolean
* @returns {Promise<boolean>} Some other boolean
*/
async doMyThing(a, d = 1, c = false) {
// ...
return true;
}
}
```
The documentation will show that the `doMyThing` method accepts required parameters `a` and `b`. Even though `b` is named `d` in the implementation--and it's optional--it will be ignored. Likewise, since the definition in `newMethodMap` knows nothing about `c`, it too is ignored.
In addition, the _description_ from the docstring in `newMethodMap` overrides the description in the method implementation; it will describe the command `doMyThing` as "Does my thing".
The `@param` tags and `@returns` tag from the method's docstring provide information about the expected and returned types, as well as a description of each. This is not expressible via `newMethodMap` and `executeMethodMap` alone; it provides more information for your extension's users.
> Note for TypeScript users: while the _types_ will already be present, providing `@param` and `@returns` tags is still useful for providing descriptions.
All commands must be `async`, so they will return `Promise<T>` where type `T` is the type of whatever the `Promise` fulfills with. In the generated documentation, the `Promise` is ignored, and only `T` is reported. So for `doMyThing`, the return type of the command, as output in the documentation, will be `boolean`.
This is because while extensions must be written in JavaScript, we're documenting an API which can be called from any language; that language likely won't have a concept of a `Promise`. Likewise, `undefined` or `void` types will be output as `null` (since that is a concept that translates well to multiple languages).
#### Optional: `README.md`
If you have a `README.md`, it will be pulled in to the generated docs site automatically. This behavior can be disabled by adding the following to `typedoc.json`:
```json
{
"readme": "none"
}
```
#### Next Steps
Appium provides type definitions for extension authors; these are available via Appium itself and the [`@appium/types`](https://npm.im/@appium/types) package.
Of note, drivers should both extend `BaseDriver` _and_ implement the `ExternalDriver` interface exported by `@appium/types`. This will help ensure that your driver's implementation is correct and usable by different clients.
You're encouraged to look at the official test/example extensions, [`@appium/fake-driver`](https://npm.im/@appium/fake-driver) and [`@appium/fake-plugin`](https://npm.im/@appium/fake-plugin) for canonical examples of how to use these types.
#### Resources
How to type more complex return values or parameters is beyond the scope of this document. For more information, see:
- For extensions written in JS, the [TypeScript documentation](https://www.typescriptlang.org/docs/handbook/intro-to-js-ts.html)
- For tags in addition to what TS natively recognizes, see the [TypeDoc documentation](https://typedoc.org/guides/doccomments/)
Likewise, refer to the [MkDocs documentation](https://www.mkdocs.org/user-guide/writing-your-docs/) for further information on how to customize your MkDocs output.
### Usage
@@ -77,16 +225,16 @@ examples:
```bash
# Generate reference and build the mkdocs site into the site dir
appium-docs build
npx appium-docs build
# Same as build, but host the docs on a local dev server
# and watch for changes and rebuild when files change
appium-docs build --serve
npx appium-docs build --serve
# Build the docs and deploy them with mike versioning to the docs-site branch
# using the included commit message and rebase strategy on the branch.
# This is particularly useful for pushing content to a GitHub pages branch!
appium-docs build \
npx appium-docs build \
--deploy \
-b docs-site \
-m 'docs: auto-build docs for appium-xcuitest-driver@%s' \
+1 -1
View File
@@ -163,7 +163,7 @@ class AppiumDriver extends DriverCore {
async getSessions() {
return _.toPairs(this.sessions).map(([id, driver]) => ({
id,
capabilities: /** @type {import('@appium/types').DriverCaps} */ (driver.caps),
capabilities: /** @type {import('@appium/types').DriverCaps<any>} */ (driver.caps),
}));
}
+10 -2
View File
@@ -9,7 +9,11 @@ import {getSchema, validate} from './schema/schema';
* @type {import('lilconfig').LoaderSync}
*/
function yamlLoader(filepath, content) {
return yaml.parse(content);
try {
return yaml.parse(content);
} catch (e) {
throw new Error(`The YAML config at '${filepath}' cannot be loaded. Original error: ${e.message}`);
}
}
/**
@@ -27,7 +31,11 @@ const rawConfig = new Map();
*/
function jsonLoader(filepath, content) {
rawConfig.set(filepath, content);
return JSON.parse(content);
try {
return JSON.parse(content);
} catch (e) {
throw new Error(`The JSON config at '${filepath}' cannot be loaded. Original error: ${e.message}`);
}
}
/**
+10 -2
View File
@@ -284,7 +284,11 @@ export class Manifest {
...internal,
};
if (!_.isEqual(value, this.#data.drivers[pkgJson.appium.driverName])) {
this.setExtension(DRIVER_TYPE, pkgJson.appium.driverName, value);
this.setExtension(
/** @type {ExtType} */ (DRIVER_TYPE),
pkgJson.appium.driverName,
/** @type {ExtManifest<ExtType>} */ (value)
);
return true;
}
return false;
@@ -294,7 +298,11 @@ export class Manifest {
...internal,
};
if (!_.isEqual(value, this.#data.plugins[pkgJson.appium.pluginName])) {
this.setExtension(PLUGIN_TYPE, pkgJson.appium.pluginName, value);
this.setExtension(
/** @type {ExtType} */ (PLUGIN_TYPE),
pkgJson.appium.pluginName,
/** @type {ExtManifest<ExtType>} */ (value)
);
return true;
}
return false;
+43 -8
View File
@@ -2,7 +2,11 @@
import {init as logsinkInit} from './logsink'; // this import needs to come first since it sets up global npmlog
import logger from './logger'; // logger needs to remain second
import {routeConfiguringFunction as makeRouter, server as baseServer} from '@appium/base-driver';
import {
routeConfiguringFunction as makeRouter,
server as baseServer,
normalizeBasePath,
} from '@appium/base-driver';
import {logger as logFactory, util, env, fs} from '@appium/support';
import {asyncify} from 'asyncbox';
import _ from 'lodash';
@@ -32,8 +36,12 @@ import {
isExtensionCommandArgs,
isPluginCommandArgs,
isServerCommandArgs,
fetchInterfaces,
V4_BROADCAST_IP,
V6_BROADCAST_IP,
} from './utils';
import os from 'node:os';
import net from 'node:net';
const {resolveAppiumHome} = env;
@@ -164,8 +172,8 @@ async function prepareAppiumHome(name, appiumHome) {
}
throw new Error(
`The path '${appiumHome}' provided in the ${name} must point ` +
`to a valid folder writeable for the current user account '${os.userInfo().username}'. ` +
`Original error: ${err.message}`
`to a valid folder writeable for the current user account '${os.userInfo().username}'. ` +
`Original error: ${err.message}`
);
}
if (!stat.isDirectory()) {
@@ -178,8 +186,8 @@ async function prepareAppiumHome(name, appiumHome) {
} catch (e) {
throw new Error(
`The folder path '${appiumHome}' provided in the ${name} must be ` +
`writeable for the current user account '${os.userInfo().username}. ` +
`Original error: ${e.message}`
`writeable for the current user account '${os.userInfo().username}. ` +
`Original error: ${e.message}`
);
}
return appiumHome;
@@ -317,6 +325,34 @@ async function init(args) {
}
}
/**
* Prints the actual server address and the list of URLs that
* could be used to connect to the current server.
* Properly replaces broadcast addresses in client URLs.
*
* @param {string} url The URL the server is listening on
*/
function logServerAddress(url) {
const urlObj = new URL(url);
logger.info(`Appium REST http interface listener started on ${url}`);
if (![V4_BROADCAST_IP, V6_BROADCAST_IP, `[${V6_BROADCAST_IP}]`].includes(urlObj.hostname)) {
return;
}
const interfaces = fetchInterfaces(urlObj.hostname === V4_BROADCAST_IP ? 4 : 6);
const toLabel = (/** @type {os.NetworkInterfaceInfo} */ iface) => {
const href = urlObj.href.replace(urlObj.hostname, iface.address);
return iface.internal
? `${href} (only accessible from the same host)`
: href;
};
logger.info(
`You can provide the following ${util.pluralize('URL', interfaces.length, false)} ` +
`in your client code to connect to this server:\n` +
interfaces.map((iface) => `\t${toLabel(iface)}`).join('\n')
);
}
/**
* Initializes Appium's config. Starts server if appropriate and resolves the
* server instance if so; otherwise resolves w/ `undefined`.
@@ -417,9 +453,8 @@ async function main(args) {
});
}
logger.info(
`Appium REST http interface listener started on ${parsedArgs.address}:${parsedArgs.port}${parsedArgs.basePath}`
);
const address = net.isIPv6(parsedArgs.address) ? `[${parsedArgs.address}]` : parsedArgs.address;
logServerAddress(`http://${address}:${parsedArgs.port}${normalizeBasePath(parsedArgs.basePath)}`);
driverConfig.print();
pluginConfig.print([...pluginClasses.values()]);
+25
View File
@@ -5,9 +5,13 @@ import {inspect as dump} from 'util';
import {node} from '@appium/support';
import path from 'path';
import {SERVER_SUBCOMMAND, DRIVER_TYPE, PLUGIN_TYPE} from './constants';
import os from 'node:os';
const W3C_APPIUM_PREFIX = 'appium';
const STANDARD_CAPS_LOWERCASE = new Set([...STANDARD_CAPS].map((cap) => cap.toLowerCase()));
export const V4_BROADCAST_IP = '0.0.0.0';
export const V6_BROADCAST_IP = '::';
/**
*
@@ -331,6 +335,27 @@ export function isPluginCommandArgs(args) {
return args.subcommand === PLUGIN_TYPE;
}
/**
* Fetches the list of matched network interfaces of the current host.
*
* @param {4|6|null} family Either 4 to include ipv4 addresses only,
* 6 to include ipv6 addresses only, or null to include all of them
* @returns {os.NetworkInterfaceInfo[]} The list of matched interfcaes
*/
export function fetchInterfaces (family = null) {
let familyValue = null;
// 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6
if (family === 4) {
familyValue = [4, 'IPv4'];
} else if (family === 6) {
familyValue = [6, 'IPv6'];
}
// @ts-ignore The linter does not understand the below filter
return _.flatMap(_.values(os.networkInterfaces()).filter(Boolean))
// @ts-ignore The linter does not understand the above filter
.filter(({family}) => !familyValue || familyValue && familyValue.includes(family));
}
export {
inspect,
parseCapsForInnerDriver,
+12 -12
View File
@@ -1,6 +1,6 @@
{
"name": "appium",
"version": "2.0.0-beta.70",
"version": "2.0.0-rc.5",
"description": "Automation for Apps.",
"keywords": [
"automation",
@@ -60,17 +60,17 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-driver": "^9.3.9",
"@appium/base-plugin": "^2.2.9",
"@appium/docutils": "^0.3.13",
"@appium/schema": "^0.2.6",
"@appium/support": "^4.0.1",
"@appium/types": "^0.11.1",
"@appium/base-driver": "^9.3.15",
"@appium/base-plugin": "^2.2.15",
"@appium/docutils": "^0.4.4",
"@appium/schema": "^0.3.1",
"@appium/support": "^4.1.2",
"@appium/types": "^0.13.2",
"@sidvind/better-ajv-errors": "2.1.0",
"@types/argparse": "2.0.10",
"@types/bluebird": "3.5.38",
"@types/fancy-log": "2.0.0",
"@types/semver": "7.3.13",
"@types/semver": "7.5.0",
"@types/teen_process": "2.0.0",
"@types/wrap-ansi": "3.0.0",
"ajv": "8.12.0",
@@ -89,13 +89,13 @@
"ora": "5.4.1",
"package-changed": "3.0.0",
"resolve-from": "5.0.0",
"semver": "7.5.0",
"semver": "7.5.3",
"source-map-support": "0.5.21",
"teen_process": "2.0.2",
"type-fest": "3.10.0",
"winston": "3.8.2",
"type-fest": "3.11.1",
"winston": "3.9.0",
"wrap-ansi": "7.0.0",
"yaml": "2.2.2"
"yaml": "2.3.1"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
@@ -1,7 +1,7 @@
// @ts-check
import {exec} from 'teen_process';
import {fs, tempDir, util} from '@appium/support';
import {fs, system, tempDir, util} from '@appium/support';
import path from 'path';
import resolveFrom from 'resolve-from';
import {
@@ -24,6 +24,8 @@ const TEST_DRIVER_INVALID_PEERS_DIR = path.dirname(
);
describe('Driver CLI', function () {
this.timeout(90000); // some of these tests involve network and can be very slow
/**
* @type {string}
*/
@@ -96,6 +98,10 @@ describe('Driver CLI', function () {
});
it('should show updates for installed drivers with --updates', async function () {
if (system.isWindows()) {
// TODO figure out why this isn't working on windows
return this.skip();
}
const versions = /** @type {string[]} */ (
JSON.parse(
(
@@ -6,6 +6,7 @@ import {finalizeSchema, registerSchema, resetSchema} from '../../lib/schema/sche
import extSchema from '../fixtures/driver-schema';
import {resolveFixture} from '../helpers';
import _ from 'lodash';
import {system} from '@appium/support';
const resolveConfigFixture = _.partial(resolveFixture, 'config');
@@ -314,8 +315,12 @@ describe('config file behavior', function () {
describe('when the config file is invalid JSON', function () {
it('should reject with a user-friendly error message', async function () {
if (system.isWindows()) {
// TODO figure out why this isn't working on windows
return this.skip();
}
await readConfigFile(INVALID_JSON_FILEPATH).should.be.rejectedWith(
new RegExp(`${INVALID_JSON_FILEPATH} is invalid`)
new RegExp(`${_.escapeRegExp(INVALID_JSON_FILEPATH)}`)
);
});
});
+5 -3
View File
@@ -11,13 +11,15 @@ export default [
ref: 'appium.json#/properties/server/properties/address',
},
schema: {
$comment: 'I think hostname covers both DNS and IPv4...could be wrong',
appiumCliAliases: ['a'],
default: '0.0.0.0',
description: 'IP address to listen on',
format: 'hostname',
description: 'IPv4/IPv6 address or a hostname to listen on',
title: 'address config',
type: 'string',
anyOf: [
{format: 'hostname', type: 'string'},
{format: 'ipv6', type: 'string'}
]
},
},
{
@@ -303,7 +303,7 @@ describe('ExtensionConfig', function () {
it('should throw', function () {
expect(() => config.require('fake')).to.throw(
ReferenceError,
/^could not find a driver installed at \/some\/path\/node_modules\/flotsam/i
/^could not find a driver installed at .+flotsam/i
);
});
});
+15 -1
View File
@@ -59,7 +59,9 @@ describe('parser', function () {
}).should.throw(/unrecognized arguments: --apple/i);
});
it('should throw an error for an invalid value ("hostname")', function () {
// FIXME: this test will not work until we restore the formatting restriction to the address validation
// see #18716
it.skip('should throw an error for an invalid value ("hostname")', function () {
(() => {
p.parseArgs(['--address', '-42']);
}).should.throw(/must match format "hostname"/i);
@@ -119,6 +121,18 @@ describe('parser', function () {
p.parseArgs(['--allow-insecure', 'foo ,bar']).allowInsecure.should.eql(['foo', 'bar']);
});
it('should parse --address correctly', function () {
p.parseArgs(['--address', 'localhost']).address.should.eql('localhost');
p.parseArgs(['--address', 'appium.net']).address.should.eql('appium.net');
p.parseArgs(['--address', '127.0.0.1']).address.should.eql('127.0.0.1');
p.parseArgs(['--address', '10.0.0.1']).address.should.eql('10.0.0.1');
p.parseArgs(['--address', '::']).address.should.eql('::');
p.parseArgs(['--address', '::1']).address.should.eql('::1');
p.parseArgs(['--address', '2a02:8888:9a80:158:2418:a474:43c6:1b78']).address.should.eql(
'2a02:8888:9a80:158:2418:a474:43c6:1b78'
);
});
it('should parse --deny-insecure correctly', function () {
p.parseArgs([]).should.not.have.property('denyInsecure');
p.parseArgs(['--deny-insecure', '']).denyInsecure.should.eql([]);
+16
View File
@@ -5,6 +5,7 @@ import {
removeAppiumPrefixes,
inspect,
adjustNodePath,
fetchInterfaces,
} from '../../lib/utils';
import {BASE_CAPS, W3C_CAPS} from '../helpers';
import _ from 'lodash';
@@ -275,4 +276,19 @@ describe('utils', function () {
(await fs.exists(process.env.NODE_PATH)).should.be.true;
});
});
describe('fetchInterfaces()', function () {
it('should fetch interfaces for ipv4 only', async function () {
fetchInterfaces(4).length.should.be.greaterThan(0);
});
it('should fetch interfaces for ipv6 only', async function () {
fetchInterfaces(6).length.should.be.greaterThan(0);
});
it('should fetch interfaces for ipv4 and ipv6', async function () {
fetchInterfaces().length.should.be.greaterThan(0);
});
});
});
+58
View File
@@ -3,6 +3,64 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [9.3.15](https://github.com/appium/appium/compare/@appium/base-driver@9.3.14...@appium/base-driver@9.3.15) (2023-07-03)
### Bug Fixes
* **base-driver:** allow subclass to define shape of settings object ([3d614d6](https://github.com/appium/appium/commit/3d614d6d414d0d34026f516dbfad7d296000efcf))
* **base-driver:** pass thru all type args to ExternalDriver ([2b35170](https://github.com/appium/appium/commit/2b351705d401e2db8da76022989c1475cbdda0f8))
* **types,base-driver:** remove deviceName from base constraints ([01061b2](https://github.com/appium/appium/commit/01061b291981333bdec59adfbea60f0cef3d69c1))
* **types:** separate the type of opts from initialOpts ([d6cca51](https://github.com/appium/appium/commit/d6cca5175c3e55d4670936c9d216cd3a6610d16b))
## [9.3.14](https://github.com/appium/appium/compare/@appium/base-driver@9.3.13...@appium/base-driver@9.3.14) (2023-06-29)
**Note:** Version bump only for package @appium/base-driver
## [9.3.13](https://github.com/appium/appium/compare/@appium/base-driver@9.3.12...@appium/base-driver@9.3.13) (2023-06-15)
### Bug Fixes
* **base-driver:** update def of findElOrEls and findElOrElsWithProcessing ([54a4a8d](https://github.com/appium/appium/commit/54a4a8da030dbecde783168bb92634df837d5a41))
* **types:** various fixes for reality ([81bc527](https://github.com/appium/appium/commit/81bc527be6aa54dd30a012156b5752b8b821ac0c))
## [9.3.12](https://github.com/appium/appium/compare/@appium/base-driver@9.3.11...@appium/base-driver@9.3.12) (2023-06-14)
**Note:** Version bump only for package @appium/base-driver
## [9.3.11](https://github.com/appium/appium/compare/@appium/base-driver@9.3.10...@appium/base-driver@9.3.11) (2023-06-14)
### Bug Fixes
* **schema:** update definitelytyped ([e967240](https://github.com/appium/appium/commit/e96724077ef2b5b8aae203856195f3bb8de56116))
* **types:** update dependency type-fest to v3.11.0 ([19277f6](https://github.com/appium/appium/commit/19277f6e14a56e52b4669d633e148ad4a3da2c7a))
* **types:** update dependency type-fest to v3.11.1 ([56499eb](https://github.com/appium/appium/commit/56499eb997b551739bed628f057de7987674ea7f))
## [9.3.10](https://github.com/appium/appium/compare/@appium/base-driver@9.3.9...@appium/base-driver@9.3.10) (2023-05-19)
### Bug Fixes
* **base-driver:** add missing @types/lodash ([63a429c](https://github.com/appium/appium/commit/63a429c53bf3da353e018ed86ec24e32bf238d18))
## [9.3.9](https://github.com/appium/appium/compare/@appium/base-driver@9.3.8...@appium/base-driver@9.3.9) (2023-05-19)
**Note:** Version bump only for package @appium/base-driver
@@ -1,4 +1,5 @@
import {Constraints, IFindCommands} from '@appium/types';
/* eslint-disable @typescript-eslint/no-unused-vars */
import {Constraints, Element, IFindCommands} from '@appium/types';
import {errors} from '../../protocol';
import {BaseDriver} from '../driver';
import {mixin} from './mixin';
@@ -8,6 +9,67 @@ declare module '../driver' {
interface BaseDriver<C extends Constraints> extends IFindCommands {}
}
async function findElOrEls<C extends Constraints>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: true,
context?: any
): Promise<Element[]>;
async function findElOrEls<C extends Constraints>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: false,
context?: any
): Promise<Element>;
async function findElOrEls<C extends Constraints>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: boolean,
context?: any
): Promise<Element[] | Element> {
throw new errors.NotImplementedError('Not implemented yet for find.');
}
async function findElOrElsWithProcessing<C extends Constraints>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: true,
context?: any
): Promise<Element[]>;
async function findElOrElsWithProcessing<C extends Constraints>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: false,
context?: any
): Promise<Element>;
async function findElOrElsWithProcessing<C extends Constraints>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: boolean,
context?: any
): Promise<Element[] | Element> {
this.validateLocatorStrategy(strategy);
try {
// @ts-expect-error TS does not understand how to deal with the overload here
return await this.findElOrEls(strategy, selector, mult, context);
} catch (err) {
if (this.opts.printPageSourceOnFindFailure) {
const src = await this.getPageSource();
this.log.debug(`Error finding element${mult ? 's' : ''}: ${err.message}`);
this.log.debug(`Page source requested through 'printPageSourceOnFindFailure':`);
this.log.debug(src);
}
// still want the error to occur
throw err;
}
}
const FindCommands: IFindCommands = {
async findElement<C extends Constraints>(this: BaseDriver<C>, strategy, selector) {
return await this.findElOrElsWithProcessing(strategy, selector, false);
@@ -35,46 +97,13 @@ const FindCommands: IFindCommands = {
return await this.findElOrElsWithProcessing(strategy, selector, true, elementId);
},
/**
* Returns an object which adheres to the way the JSON Wire Protocol represents elements:
*
* Override this for your own driver!
*/
async findElOrEls<C extends Constraints, Mult extends boolean, Ctx = any>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: Mult,
context: Ctx
) {
throw new errors.NotImplementedError('Not implemented yet for find.');
},
findElOrEls,
async getPageSource<C extends Constraints>(this: BaseDriver<C>) {
throw new errors.NotImplementedError('Not implemented yet for find.');
},
async findElOrElsWithProcessing<C extends Constraints, Mult extends boolean, Ctx = any>(
this: BaseDriver<C>,
strategy: string,
selector: string,
mult: Mult,
context?: Ctx
) {
this.validateLocatorStrategy(strategy);
try {
return await this.findElOrEls(strategy, selector, mult, context);
} catch (err) {
if (this.opts.printPageSourceOnFindFailure) {
const src = await this.getPageSource();
this.log.debug(`Error finding element${mult ? 's' : ''}: ${err.message}`);
this.log.debug(`Page source requested through 'printPageSourceOnFindFailure':`);
this.log.debug(src);
}
// still want the error to occur
throw err;
}
},
findElOrElsWithProcessing,
};
mixin(FindCommands);
@@ -1,6 +1,5 @@
import './event';
import './find';
import './log';
import './settings';
import './timeout';
import './execute';
@@ -1,6 +1,6 @@
import {Constraints, Driver, ILogCommands, LogDefRecord} from '@appium/types';
import type {Constraints, Driver, ILogCommands} from '@appium/types';
import _ from 'lodash';
import {BaseDriver} from '../driver';
import type {BaseDriver} from '../driver';
import {mixin} from './mixin';
declare module '../driver' {
@@ -9,7 +9,7 @@ declare module '../driver' {
}
const LogCommands: ILogCommands = {
supportedLogTypes: <LogDefRecord>{},
supportedLogTypes: {},
async getLogTypes<C extends Constraints>(this: BaseDriver<C>) {
this.log.debug('Retrieving supported log types');
@@ -1,26 +0,0 @@
import {BaseDriver} from '../driver';
import {Constraints, ISettingsCommands, StringRecord} from '@appium/types';
import {mixin} from './mixin';
declare module '../driver' {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface BaseDriver<C extends Constraints> extends ISettingsCommands {}
}
const SettingsCommands: ISettingsCommands = {
async updateSettings<C extends Constraints>(this: BaseDriver<C>, newSettings: StringRecord) {
if (!this.settings) {
this.log.errorAndThrow('Cannot update settings; settings object not found');
}
return await this.settings.update(newSettings);
},
async getSettings<C extends Constraints>(this: BaseDriver<C>) {
if (!this.settings) {
this.log.errorAndThrow('Cannot get settings; settings object not found');
}
return this.settings.getSettings();
},
};
mixin(SettingsCommands);
@@ -2,10 +2,23 @@
/* eslint-disable require-await */
import {logger} from '@appium/support';
import type {
AppiumLogger,
Constraints,
Core,
Driver,
DriverOpts,
EventHistory,
HTTPMethod,
InitialOpts,
Protocol,
RouteMatcher,
StringRecord,
} from '@appium/types';
import AsyncLock from 'async-lock';
import {EventEmitter} from 'events';
import _ from 'lodash';
import os from 'os';
import {EventEmitter} from 'node:events';
import os from 'node:os';
import {DEFAULT_BASE_PATH, PROTOCOLS} from '../constants';
import {errors} from '../protocol';
import DeviceSettings from './device-settings';
@@ -15,35 +28,22 @@ const NEW_COMMAND_TIMEOUT_MS = 60 * 1000;
const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
/**
* @template {Constraints} C
* @template {StringRecord} [Settings=StringRecord]
* @implements {Core<C, Settings>}
*/
class DriverCore {
export class DriverCore<const C extends Constraints, Settings extends StringRecord = StringRecord>
implements Core<C, Settings>
{
/**
* Make the basedriver version available so for any driver which inherits from this package, we
* know which version of basedriver it inherited from
*/
static baseVersion = BASEDRIVER_VER;
/**
* @type {string?}
*/
sessionId;
sessionId: string | null;
/**
* @type {import('@appium/types').DriverOpts<C>}
*/
opts;
opts: DriverOpts<C>;
/**
* @type {import('@appium/types').DriverOpts<C>}
*/
initialOpts;
initialOpts: InitialOpts;
/** @type {typeof helpers} */
helpers;
helpers: typeof helpers;
/**
* basePath is used for several purposes, for example in setting up
@@ -52,83 +52,58 @@ class DriverCore {
* initially but it is automatically updated during any actual program
* execution by the routeConfiguringFunction, which is necessarily run as
* the entrypoint for any Appium server
* @type {string}
*/
basePath;
basePath: string;
/** @type {boolean} */
relaxedSecurityEnabled;
relaxedSecurityEnabled: boolean;
/** @type {string[]} */
allowInsecure;
allowInsecure: string[];
/** @type {string[]} */
denyInsecure;
denyInsecure: string[];
/** @type {number} */
newCommandTimeoutMs;
newCommandTimeoutMs: number;
/** @type {number} */
implicitWaitMs;
implicitWaitMs: number;
/** @type {string[]} */
locatorStrategies;
locatorStrategies: string[];
/** @type {string[]} */
webLocatorStrategies;
webLocatorStrategies: string[];
/** @type {Driver[]} */
managedDrivers;
managedDrivers: Driver[];
/** @type {NodeJS.Timeout?} */
noCommandTimer;
noCommandTimer: NodeJS.Timeout | null;
/** @type {EventHistory} */
_eventHistory;
protected _eventHistory: EventHistory;
// used to handle driver events
/** @type {NodeJS.EventEmitter} */
eventEmitter;
eventEmitter: NodeJS.EventEmitter;
/**
* @type {AppiumLogger}
* @privateRemarks XXX: unsure why this is wrapped in a getter when nothing else is
*/
_log;
protected _log: AppiumLogger;
/**
* @type {boolean}
*/
shutdownUnexpectedly;
shutdownUnexpectedly: boolean;
/**
* @type {boolean}
*/
shouldValidateCaps;
shouldValidateCaps: boolean;
/**
* @protected
* @type {AsyncLock}
*/
commandsQueueGuard;
protected commandsQueueGuard: AsyncLock;
/**
* settings should be instantiated by drivers which extend BaseDriver, but
* we set it to an empty DeviceSettings instance here to make sure that the
* default settings are applied even if an extending driver doesn't utilize
* the settings functionality itself
* @type {DeviceSettings<Settings>}
*/
settings;
settings: DeviceSettings<Settings>;
/**
* @param {DriverOpts<C>} opts
* @param {boolean} [shouldValidateCaps]
*/
constructor(opts = /** @type {DriverOpts<C>} */ ({}), shouldValidateCaps = true) {
this._log = logger.getLogger(helpers.generateDriverLogPrefix(this));
protocol?: Protocol;
constructor(opts: InitialOpts = <InitialOpts>{}, shouldValidateCaps = true) {
this._log = logger.getLogger(helpers.generateDriverLogPrefix(this as Core<C>));
// setup state
this.opts = opts;
this.opts = opts as DriverOpts<C>;
// use a custom tmp dir to avoid losing data and app when computer is
// restarted
@@ -168,11 +143,11 @@ class DriverCore {
* when the driver is shut down unexpectedly. Multiple calls to this method
* will cause the handler to be executed mutiple times
*
* @param {(...args: any[]) => void} handler The code to be executed on unexpected shutdown.
* @param handler The code to be executed on unexpected shutdown.
* The function may accept one argument, which is the actual error instance, which
* caused the driver to shut down.
*/
onUnexpectedShutdown(handler) {
onUnexpectedShutdown(handler: (...args: any[]) => void) {
this.eventEmitter.on(ON_UNEXPECTED_SHUTDOWN_EVENT, handler);
}
@@ -183,7 +158,7 @@ class DriverCore {
* Override it in inherited driver classes if necessary.
*/
get driverData() {
return /** @type {import('@appium/types').DriverData} */ ({});
return {};
}
/**
@@ -191,13 +166,13 @@ class DriverCore {
* handles new driver commands received from the client.
* Override it for inherited classes only in special cases.
*
* @return {boolean} If the returned value is true (default) then all the commands
* @return If the returned value is true (default) then all the commands
* received by the particular driver instance are going to be put into the queue,
* so each following command will not be executed until the previous command
* execution is completed. False value disables that queue, so each driver command
* is executed independently and does not wait for anything.
*/
get isCommandsQueueEnabled() {
get isCommandsQueueEnabled(): boolean {
return true;
}
@@ -211,9 +186,8 @@ class DriverCore {
/**
* API method for driver developers to log timings for important events
* @param {string} eventName
*/
logEvent(eventName) {
logEvent(eventName: string) {
if (eventName === 'commands') {
throw new Error('Cannot log commands directly');
}
@@ -240,10 +214,8 @@ class DriverCore {
/**
* method required by MJSONWP in order to determine whether it should
* respond with an invalid session response
* @param {string} [sessionId]
* @returns {boolean}
*/
sessionExists(sessionId) {
sessionExists(sessionId: string): boolean {
if (!sessionId) return false; // eslint-disable-line curly
return sessionId === this.sessionId;
}
@@ -251,11 +223,9 @@ class DriverCore {
/**
* method required by MJSONWP in order to determine if the command should
* be proxied directly to the driver
* @param {string} sessionId
* @returns {Core<C> | null}
*/
driverForSession(sessionId) {
return /** @type {Core<C> | null} */ (this);
driverForSession(sessionId: string): Core<Constraints> | null {
return this as Core<Constraints>;
}
isMjsonwpProtocol() {
@@ -277,11 +247,9 @@ class DriverCore {
/**
* Check whether a given feature is enabled via its name
*
* @param {string} name - name of feature/command
*
* @returns {Boolean}
* @param name - name of feature/command
*/
isFeatureEnabled(name) {
isFeatureEnabled(name: string): boolean {
// if we have explicitly denied this feature, return false immediately
if (this.denyInsecure && _.includes(this.denyInsecure, name)) {
return false;
@@ -306,10 +274,10 @@ class DriverCore {
* Assert that a given feature is enabled and throw a helpful error if it's
* not
*
* @param {string} name - name of feature/command
* @param name - name of feature/command
* @deprecated
*/
ensureFeatureEnabled(name) {
ensureFeatureEnabled(name: string) {
this.assertFeatureEnabled(name);
}
@@ -317,9 +285,9 @@ class DriverCore {
* Assert that a given feature is enabled and throw a helpful error if it's
* not
*
* @param {string} name - name of feature/command
* @param name - name of feature/command
*/
assertFeatureEnabled(name) {
assertFeatureEnabled(name: string) {
if (!this.isFeatureEnabled(name)) {
throw new Error(
`Potentially insecure feature '${name}' has not been ` +
@@ -331,12 +299,7 @@ class DriverCore {
}
}
/**
*
* @param {string} strategy
* @param {boolean} [webContext]
*/
validateLocatorStrategy(strategy, webContext = false) {
validateLocatorStrategy(strategy: string, webContext = false) {
let validStrategies = this.locatorStrategies;
this.log.debug(`Valid locator strategies for this request: ${validStrategies.join(', ')}`);
@@ -351,30 +314,15 @@ class DriverCore {
}
}
/**
*
* @param {string} [sessionId]
* @returns {boolean}
*/
proxyActive(sessionId) {
proxyActive(sessionId: string): boolean {
return false;
}
/**
*
* @param {string} sessionId
* @returns {import('@appium/types').RouteMatcher[]}
*/
getProxyAvoidList(sessionId) {
getProxyAvoidList(sessionId: string): RouteMatcher[] {
return [];
}
/**
*
* @param {string} [sessionId]
* @returns {boolean}
*/
canProxy(sessionId) {
canProxy(sessionId: string): boolean {
return false;
}
@@ -382,28 +330,28 @@ class DriverCore {
* Whether a given command route (expressed as method and url) should not be
* proxied according to this driver
*
* @param {string} sessionId - the current sessionId (in case the driver runs
* @param sessionId - the current sessionId (in case the driver runs
* multiple session ids and requires it). This is not used in this method but
* should be made available to overridden methods.
* @param {import('@appium/types').HTTPMethod} method - HTTP method of the route
* @param {string} url - url of the route
* @param {any} [body] - webdriver request body
* @param method - HTTP method of the route
* @param url - url of the route
* @param [body] - webdriver request body
*
* @returns {boolean} - whether the route should be avoided
* @returns whether the route should be avoided
*/
proxyRouteIsAvoided(sessionId, method, url, body) {
for (let avoidSchema of this.getProxyAvoidList(sessionId)) {
proxyRouteIsAvoided(sessionId: string, method: HTTPMethod, url: string, body?: any): boolean {
for (const avoidSchema of this.getProxyAvoidList(sessionId)) {
if (!_.isArray(avoidSchema) || avoidSchema.length !== 2) {
throw new Error('Proxy avoidance must be a list of pairs');
}
let [avoidMethod, avoidPathRegex] = avoidSchema;
const [avoidMethod, avoidPathRegex] = avoidSchema;
if (!_.includes(['GET', 'POST', 'DELETE'], avoidMethod)) {
throw new Error(`Unrecognized proxy avoidance method '${avoidMethod}'`);
}
if (!_.isRegExp(avoidPathRegex)) {
throw new Error('Proxy avoidance path must be a regular expression');
}
let normalizedUrl = url.replace(new RegExp(`^${_.escapeRegExp(this.basePath)}`), '');
const normalizedUrl = url.replace(new RegExp(`^${_.escapeRegExp(this.basePath)}`), '');
if (avoidMethod === method && avoidPathRegex.test(normalizedUrl)) {
return true;
}
@@ -415,7 +363,7 @@ class DriverCore {
*
* @param {Driver} driver
*/
addManagedDriver(driver) {
addManagedDriver(driver: Driver) {
this.managedDrivers.push(driver);
}
@@ -430,26 +378,3 @@ class DriverCore {
}
}
}
export {DriverCore};
/**
* @typedef {import('@appium/types').Driver} Driver
* @typedef {import('@appium/types').Constraints} Constraints
* @typedef {import('@appium/types').ServerArgs} ServerArgs
* @typedef {import('@appium/types').EventHistory} EventHistory
* @typedef {import('@appium/types').AppiumLogger} AppiumLogger
* @typedef {import('@appium/types').StringRecord} StringRecord
* @typedef {import('@appium/types').BaseDriverCapConstraints} BaseDriverCapConstraints
*/
/**
* @template {Constraints} C
* @template {StringRecord} [S=StringRecord]
* @typedef {import('@appium/types').Core<C, S>} Core
*/
/**
* @template {Constraints} C
* @typedef {import('@appium/types').DriverOpts<C>} DriverOpts
*/
@@ -9,7 +9,7 @@ import {errors} from '../protocol/errors';
export const MAX_SETTINGS_SIZE = 20 * 1024 * 1024; // 20 MB
/**
* @template {import('@appium/types').StringRecord} T
* @template {StringRecord} T
* @implements {IDeviceSettings<T>}
*/
export class DeviceSettings {
@@ -74,6 +74,10 @@ export class DeviceSettings {
export default DeviceSettings;
/**
* @template {import('@appium/types').StringRecord} [T=import('@appium/types').StringRecord]
* @typedef {import('@appium/types').StringRecord} StringRecord
*/
/**
* @template {StringRecord} [T=StringRecord]
* @typedef {import('@appium/types').IDeviceSettings<T>} IDeviceSettings
*/
+45 -28
View File
@@ -1,28 +1,29 @@
import {validateCaps, processCapabilities} from './capabilities';
import {DriverCore} from './core';
import {util} from '@appium/support';
import {
BASE_DESIRED_CAP_CONSTRAINTS,
type AppiumServer,
type BaseDriverCapConstraints,
type Capabilities,
type Constraints,
type DefaultCreateSessionResult,
type Driver,
type DriverCaps,
type DriverData,
type MultiSessionData,
type ServerArgs,
type StringRecord,
type W3CDriverCaps,
type InitialOpts,
type DefaultDeleteSessionResult,
type SingularSessionData,
} from '@appium/types';
import B from 'bluebird';
import _ from 'lodash';
import {fixCaps, isW3cCaps} from '../helpers/capabilities';
import {DELETE_SESSION_COMMAND, determineProtocol, errors} from '../protocol';
import {processCapabilities, validateCaps} from './capabilities';
import {DriverCore} from './core';
import helpers from './helpers';
import {
AppiumServer,
BaseDriverCapConstraints,
BASE_DESIRED_CAP_CONSTRAINTS,
Capabilities,
Constraints,
DefaultCreateSessionResult,
Driver,
DriverCaps,
DriverData,
DriverOpts,
MultiSessionData,
ServerArgs,
SingularSessionData,
StringRecord,
W3CDriverCaps,
} from '@appium/types';
const EVENT_SESSION_INIT = 'newSessionRequested';
const EVENT_SESSION_START = 'newSessionStarted';
@@ -31,26 +32,26 @@ const EVENT_SESSION_QUIT_DONE = 'quitSessionFinished';
const ON_UNEXPECTED_SHUTDOWN_EVENT = 'onUnexpectedShutdown';
export class BaseDriver<
C extends Constraints,
const C extends Constraints,
CArgs extends StringRecord = StringRecord,
Settings extends StringRecord = StringRecord,
CreateResult = DefaultCreateSessionResult<C>,
DeleteResult = DefaultDeleteSessionResult,
SessionData extends StringRecord = StringRecord
>
extends DriverCore<C, Settings>
implements Driver<C, CArgs>
implements Driver<C, CArgs, Settings, CreateResult, DeleteResult, SessionData>
{
cliArgs: CArgs & ServerArgs;
caps: DriverCaps<C>;
originalCaps: W3CDriverCaps<C>;
desiredCapConstraints: C;
opts: DriverOpts<C>;
server?: AppiumServer;
serverHost?: string;
serverPort?: number;
serverPath?: string;
constructor(opts: DriverOpts<C>, shouldValidateCaps = true) {
constructor(opts: InitialOpts, shouldValidateCaps = true) {
super(opts, shouldValidateCaps);
this.caps = {} as DriverCaps<C>;
@@ -63,7 +64,7 @@ export class BaseDriver<
*
* Subclasses _shouldn't_ need to use this. If you need to use this, please create
* an issue:
* @see https://github.com/appium/appium/issues/new
* @see {@link https://github.com/appium/appium/issues/new}
*/
protected get _desiredCapConstraints(): Readonly<BaseDriverCapConstraints & C> {
return Object.freeze(_.merge({}, BASE_DESIRED_CAP_CONSTRAINTS, this.desiredCapConstraints));
@@ -234,7 +235,7 @@ export class BaseDriver<
w3cCapabilities?: W3CDriverCaps<C>,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
driverData?: DriverData[]
): Promise<DefaultCreateSessionResult<C>> {
): Promise<CreateResult> {
if (this.sessionId !== null) {
throw new errors.SessionNotCreatedError(
'Cannot create a new session while one is in progress'
@@ -311,7 +312,7 @@ export class BaseDriver<
this.log.info(`Session created with session id: ${this.sessionId}`);
return [this.sessionId, caps];
return [this.sessionId, caps] as CreateResult;
}
async getSessions() {
const ret: MultiSessionData<C>[] = [];
@@ -330,7 +331,9 @@ export class BaseDriver<
* Returns capabilities for the session and event history (if applicable)
*/
async getSession() {
return this.caps.eventTimings ? {...this.caps, events: this.eventHistory} : this.caps;
return (
this.caps.eventTimings ? {...this.caps, events: this.eventHistory} : this.caps
) as SingularSessionData<C, SessionData>;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -380,6 +383,20 @@ export class BaseDriver<
return true;
}
async updateSettings(newSettings: Settings) {
if (!this.settings) {
this.log.errorAndThrow('Cannot update settings; settings object not found');
}
return await this.settings.update(newSettings);
}
async getSettings() {
if (!this.settings) {
this.log.errorAndThrow('Cannot get settings; settings object not found');
}
return this.settings.getSettings();
}
}
export * from './commands';
@@ -1,4 +1,5 @@
import {util} from '@appium/support';
import {Protocol} from '@appium/types';
// The default maximum length of a single log record
// containing http request/response body
@@ -12,7 +13,7 @@ const W3C_ELEMENT_KEY = util.W3C_WEB_ELEMENT_IDENTIFIER;
const PROTOCOLS = {
W3C: 'W3C',
MJSONWP: 'MJSONWP',
};
} as const satisfies Record<Protocol, Protocol>;
// Before Appium 2.0, this default value was '/wd/hub' by historical reasons.
const DEFAULT_BASE_PATH = '';
+1 -1
View File
@@ -15,7 +15,7 @@ export default BaseDriver;
// MJSONWP exports
export * from './protocol';
export {errorFromMJSONWPStatusCode as errorFromCode} from './protocol';
export {DEFAULT_BASE_PATH, PROTOCOLS} from './constants';
export {DEFAULT_BASE_PATH, PROTOCOLS, W3C_ELEMENT_KEY} from './constants';
// Express exports
export {STATIC_DIR} from './express/static';
@@ -320,6 +320,12 @@ class JWProxy {
return commandName;
}
/**
*
* @param {string} url
* @param {import('@appium/types').HTTPMethod} method
* @param {any?} body
*/
async proxyCommand(url, method, body = null) {
const commandName = this.requestToCommandName(url, method);
if (!commandName) {
@@ -330,6 +336,13 @@ class JWProxy {
return await this.protocolConverter.convertAndProxy(commandName, url, method, body);
}
/**
*
* @param {string} url
* @param {import('@appium/types').HTTPMethod} method
* @param {any?} body
* @returns {Promise<unknown>}
*/
async command(url, method, body = null) {
let response;
let resBodyObj;
+8 -1
View File
@@ -982,7 +982,13 @@ function isUnknownError(err) {
})
);
}
/**
* Type guard to check if an Error is of a specific type
* @template {Error} T
* @param {any} err
* @param {import('@appium/types').Class<T>} type
* @returns {err is T}
*/
function isErrorType(err, type) {
// `name` property is the constructor name
if (type.name === ProtocolError.name) {
@@ -1072,6 +1078,7 @@ function getResponseForW3CError(err) {
w3cLog.debug(`Bad parameters: ${err}`);
w3cErrorString = BadParametersError.error();
} else {
// @ts-expect-error unclear what the problem is here
w3cErrorString = err.error;
}
+7 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-driver",
"version": "9.3.9",
"version": "9.3.15",
"description": "Base driver class for Appium drivers",
"keywords": [
"automation",
@@ -37,19 +37,20 @@
"!build/tsconfig.tsbuildinfo"
],
"scripts": {
"test": "npm run test:unit",
"test:e2e": "mocha -p --timeout 20s --slow 10s \"./test/e2e/**/*.spec.js\"",
"test": "run-p test:unit test:types",
"test:e2e": "mocha --timeout 20s --slow 10s \"./test/e2e/**/*.spec.js\"",
"test:smoke": "node ./index.js",
"test:unit": "mocha \"./test/unit/**/*.spec.js\"",
"test:types": "tsd"
},
"dependencies": {
"@appium/support": "^4.0.1",
"@appium/types": "^0.11.1",
"@appium/support": "^4.1.2",
"@appium/types": "^0.13.2",
"@colors/colors": "1.5.0",
"@types/async-lock": "1.4.0",
"@types/bluebird": "3.5.38",
"@types/express": "4.17.17",
"@types/lodash": "4.14.195",
"@types/method-override": "0.0.32",
"@types/serve-favicon": "2.5.4",
"async-lock": "1.4.0",
@@ -66,7 +67,7 @@
"morgan": "1.10.0",
"serve-favicon": "2.5.0",
"source-map-support": "0.5.21",
"type-fest": "3.10.0",
"type-fest": "3.11.1",
"validate.js": "0.13.1"
},
"engines": {
@@ -4,8 +4,8 @@ import {BaseDriver} from '.../../..';
import {BaseDriverCapConstraints, ExternalDriver, Driver, DriverOpts} from '@appium/types';
expectAssignable<Driver<BaseDriverCapConstraints>>(
new BaseDriver({} as DriverOpts<BaseDriverCapConstraints>)
new BaseDriver<BaseDriverCapConstraints>({} as DriverOpts<BaseDriverCapConstraints>)
);
expectAssignable<ExternalDriver<BaseDriverCapConstraints>>(
new BaseDriver({} as DriverOpts<BaseDriverCapConstraints>)
new BaseDriver<BaseDriverCapConstraints>({} as DriverOpts<BaseDriverCapConstraints>)
);
@@ -36,18 +36,15 @@ describe('Desired Capabilities', function () {
it('should require platformName', async function () {
await d
.createSession({
alwaysMatch: {
'appium:deviceName': 'Delorean',
},
alwaysMatch: {},
firstMatch: [{}],
})
.should.be.rejectedWith(errors.SessionNotCreatedError, /platformName/);
});
it('should not care about cap order', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
'appium:deviceName': 'Delorean',
platformName: 'iOS',
},
firstMatch: [{}],
@@ -67,10 +64,9 @@ describe('Desired Capabilities', function () {
};
await d
.createSession(null, null, {
.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
},
firstMatch: [{}],
})
@@ -90,7 +86,7 @@ describe('Desired Capabilities', function () {
});
await d
.createSession(null, null, {
.createSession({
alwaysMatch: {
'appium:necessary': 'yup',
},
@@ -100,28 +96,22 @@ describe('Desired Capabilities', function () {
});
it('should accept extra capabilities', async function () {
await d.createSession(
null,
null,
/** @type {import('@appium/types').W3CCapabilities<{'hold the':string}>} */ ({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
'appium:extra': 'cheese',
'appium:hold the': 'sauce',
},
firstMatch: [{}],
})
).should.be.fulfilled;
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:extra': 'cheese',
'appium:hold the': 'sauce',
},
firstMatch: [{}],
}).should.be.fulfilled;
});
it('should log the use of extra caps', async function () {
this.timeout(500);
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
'appium:extra': 'cheese',
'appium:hold the': 'sauce',
},
@@ -133,11 +123,10 @@ describe('Desired Capabilities', function () {
it('should be sensitive to the case of caps', async function () {
await d
.createSession(null, null, {
.createSession({
alwaysMatch: {
// @ts-expect-error
platformname: 'iOS',
'appium:deviceName': 'Delorean',
},
firstMatch: [{}],
})
@@ -146,10 +135,9 @@ describe('Desired Capabilities', function () {
describe('boolean capabilities', function () {
it('should allow a string "false"', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
// @ts-expect-error
'appium:noReset': 'false',
},
@@ -162,10 +150,9 @@ describe('Desired Capabilities', function () {
});
it('should allow a string "true"', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
// @ts-expect-error
'appium:noReset': 'true',
},
@@ -178,10 +165,9 @@ describe('Desired Capabilities', function () {
});
it('should allow a string "true" in string capabilities', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
'appium:language': 'true',
},
firstMatch: [{}],
@@ -195,10 +181,9 @@ describe('Desired Capabilities', function () {
describe('number capabilities', function () {
it('should allow a string "1"', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
// @ts-expect-error
'appium:newCommandTimeout': '1',
},
@@ -211,10 +196,9 @@ describe('Desired Capabilities', function () {
});
it('should allow a string "1.1"', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
// @ts-expect-error
'appium:newCommandTimeout': '1.1',
},
@@ -227,10 +211,9 @@ describe('Desired Capabilities', function () {
});
it('should allow a string "1" in string capabilities', async function () {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
'appium:language': '1',
},
firstMatch: [{}],
@@ -244,11 +227,10 @@ describe('Desired Capabilities', function () {
it('should error if objects in caps', async function () {
await d
.createSession(null, null, {
.createSession({
alwaysMatch: {
// @ts-expect-error
platformName: {a: 'iOS'},
'appium:deviceName': 'Delorean',
},
firstMatch: [{}],
})
@@ -264,10 +246,9 @@ describe('Desired Capabilities', function () {
},
};
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
'appium:lynx-version': 5,
},
firstMatch: [{}],
@@ -285,10 +266,9 @@ describe('Desired Capabilities', function () {
},
};
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
'appium:lynx-version': 5,
},
firstMatch: [{}],
@@ -305,10 +285,9 @@ describe('Desired Capabilities', function () {
};
try {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': null,
},
firstMatch: [{}],
@@ -318,10 +297,9 @@ describe('Desired Capabilities', function () {
}
await d
.createSession(null, null, {
.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': 1,
},
firstMatch: [{}],
@@ -329,10 +307,9 @@ describe('Desired Capabilities', function () {
.should.be.rejectedWith(/'foo' must be of type string/);
try {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': undefined,
},
firstMatch: [{}],
@@ -342,10 +319,9 @@ describe('Desired Capabilities', function () {
}
try {
await d.createSession(null, null, {
await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': '',
},
firstMatch: [{}],
@@ -363,10 +339,9 @@ describe('Desired Capabilities', function () {
};
await d
.createSession(null, null, {
.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': null,
},
firstMatch: [{}],
@@ -374,10 +349,10 @@ describe('Desired Capabilities', function () {
.should.be.rejectedWith(/blank/);
await d
// @ts-expect-error `null` is not actually allowed here
.createSession(null, {
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': '',
},
firstMatch: [{}],
@@ -389,7 +364,6 @@ describe('Desired Capabilities', function () {
firstMatch: [
{
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': {},
},
],
@@ -401,7 +375,6 @@ describe('Desired Capabilities', function () {
.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': [],
},
firstMatch: [{}],
@@ -412,7 +385,6 @@ describe('Desired Capabilities', function () {
.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Dumb',
'appium:foo': ' ',
},
firstMatch: [{}],
@@ -422,17 +394,15 @@ describe('Desired Capabilities', function () {
describe('w3c', function () {
it('should accept w3c capabilities', async function () {
const [sessionId, caps] = await d.createSession(null, null, {
const [sessionId, caps] = await d.createSession({
alwaysMatch: {
platformName: 'iOS',
'appium:deviceName': 'Delorean',
},
firstMatch: [{}],
});
sessionId.should.exist;
caps.should.eql({
platformName: 'iOS',
deviceName: 'Delorean',
});
});
@@ -442,7 +412,7 @@ describe('Desired Capabilities', function () {
await B.map(testValues, (val) =>
d
// @ts-expect-error
.createSession(null, null, val)
.createSession(val)
.should.be.rejectedWith(errors.SessionNotCreatedError)
);
});
+48
View File
@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.2.15](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.14...@appium/base-plugin@2.2.15) (2023-07-03)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.14](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.13...@appium/base-plugin@2.2.14) (2023-06-29)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.13](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.12...@appium/base-plugin@2.2.13) (2023-06-15)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.12](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.11...@appium/base-plugin@2.2.12) (2023-06-14)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.11](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.10...@appium/base-plugin@2.2.11) (2023-06-14)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.10](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.9...@appium/base-plugin@2.2.10) (2023-05-19)
**Note:** Version bump only for package @appium/base-plugin
## [2.2.9](https://github.com/appium/appium/compare/@appium/base-plugin@2.2.8...@appium/base-plugin@2.2.9) (2023-05-19)
**Note:** Version bump only for package @appium/base-plugin
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/base-plugin",
"version": "2.2.9",
"version": "2.2.15",
"description": "The base plugin used to create Appium 2.0 plugins",
"keywords": [
"automation",
@@ -40,8 +40,8 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-driver": "^9.3.9",
"@appium/support": "^4.0.1"
"@appium/base-driver": "^9.3.15",
"@appium/support": "^4.1.2"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
+44
View File
@@ -3,6 +3,50 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.0.21](https://github.com/appium/appium/compare/@appium/doctor@2.0.20...@appium/doctor@2.0.21) (2023-07-03)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.7 ([fdd2ac0](https://github.com/appium/appium/commit/fdd2ac098d1b6e0d17b1429defa0123660d85ca9))
## [2.0.20](https://github.com/appium/appium/compare/@appium/doctor@2.0.19...@appium/doctor@2.0.20) (2023-06-29)
**Note:** Version bump only for package @appium/doctor
## [2.0.19](https://github.com/appium/appium/compare/@appium/doctor@2.0.18...@appium/doctor@2.0.19) (2023-06-15)
**Note:** Version bump only for package @appium/doctor
## [2.0.18](https://github.com/appium/appium/compare/@appium/doctor@2.0.17...@appium/doctor@2.0.18) (2023-06-14)
**Note:** Version bump only for package @appium/doctor
## [2.0.17](https://github.com/appium/appium/compare/@appium/doctor@2.0.16...@appium/doctor@2.0.17) (2023-06-14)
### Bug Fixes
* **doctor:** update dependency appium-adb to v9.11.3 ([a0b434d](https://github.com/appium/appium/commit/a0b434dff7a4e425f66915e12266a6e943d10372))
* **doctor:** update dependency appium-adb to v9.11.4 ([f34c75c](https://github.com/appium/appium/commit/f34c75c65068a299ab4d3427233e771e796fc43a))
* **doctor:** update dependency appium-adb to v9.11.5 ([78c6794](https://github.com/appium/appium/commit/78c67942534705133093aa9fa54d84efb9fe2f69))
## [2.0.16](https://github.com/appium/appium/compare/@appium/doctor@2.0.15...@appium/doctor@2.0.16) (2023-05-19)
**Note:** Version bump only for package @appium/doctor
+1 -1
View File
@@ -75,7 +75,7 @@ class OptionalFfmpegCommandCheck extends DoctorCheck {
return ffmpegPath
? okOptional(
`ffmpeg is installed at: ${ffmpegPath}. ${
(await exec('ffmpeg', ['-version'])).stdout.split(EOL)[0]
(await exec('ffmpeg', ['-version'])).stdout.split('\n')[0]
}`
)
: nokOptional('ffmpeg cannot be found');
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/doctor",
"version": "2.0.16",
"version": "2.0.21",
"description": "Test environment for fitness to run Appium",
"keywords": [
"automation",
@@ -44,11 +44,11 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/support": "^4.0.1",
"@appium/support": "^4.1.2",
"@colors/colors": "1.5.0",
"@types/bluebird": "3.5.38",
"@types/teen_process": "2.0.0",
"appium-adb": "9.11.2",
"appium-adb": "9.11.7",
"bluebird": "3.7.2",
"inquirer": "8.2.5",
"lodash": "4.17.21",
+21 -19
View File
@@ -1,10 +1,11 @@
import path from 'node:path';
import {
EnvVarAndPathCheck,
AndroidToolCheck,
OptionalAppBundleCheck,
OptionalGstreamerCheck,
} from '../../lib/android';
import {fs} from '@appium/support';
import {fs, system} from '@appium/support';
import * as adb from 'appium-adb';
import * as utils from '../../lib/utils';
import * as tp from 'teen_process';
@@ -13,6 +14,9 @@ import B from 'bluebird';
import {removeColors} from './helper';
describe('android', function () {
const apkAnalyzerFilename = system.isWindows() ? 'apkanalyzer.bat' : 'apkanalyzer';
const gstLaunchFilename = system.isWindows() ? 'gst-launch-1.0.exe' : 'gst-launch-1.0';
const gstInspectFilename = system.isWindows() ? 'gst-inspect-1.0.exe' : 'gst-inspect-1.0';
describe(
'EnvVarAndPathCheck',
withMocks({fs}, (mocks) => {
@@ -64,12 +68,14 @@ describe('android', function () {
});
it('diagnose - success', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
mocks.adb.expects('getAndroidBinaryPath').exactly(check.tools.length)
mocks.adb
.expects('getAndroidBinaryPath')
.exactly(check.tools.length)
.returns(B.resolve('/path/to/binary'));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'adb, emulator, apkanalyzer exist: /a/b/c/d',
message: `adb, emulator, ${apkAnalyzerFilename} exist: /a/b/c/d`,
});
mocks.verify();
});
@@ -79,19 +85,17 @@ describe('android', function () {
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'adb, emulator, apkanalyzer could not be found because ' +
'ANDROID_HOME or ANDROID_SDK_ROOT is NOT set!',
message: `adb, emulator, ${apkAnalyzerFilename} could not be found because ANDROID_HOME or ANDROID_SDK_ROOT is NOT set!`,
});
mocks.verify();
});
it('diagnose - failure - path not valid', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
mocks.adb.expects('getAndroidBinaryPath').exactly(check.tools.length)
.throws();
mocks.adb.expects('getAndroidBinaryPath').exactly(check.tools.length).throws();
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: 'adb, emulator, apkanalyzer could NOT be found in /a/b/c/d!',
message: `adb, emulator, ${apkAnalyzerFilename} could NOT be found in /a/b/c/d!`,
});
mocks.verify();
});
@@ -102,11 +106,9 @@ describe('android', function () {
);
});
it('fix - install', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
process.env.ANDROID_HOME = path.resolve('/', 'a', 'b', 'c', 'd');
removeColors(await check.fix()).should.equal(
'Manually install adb, emulator, apkanalyzer and add it to PATH. ' +
'https://developer.android.com/studio#cmdline-tools and ' +
'https://developer.android.com/studio/intro/update#sdk-manager may help to setup.'
`Manually install adb, emulator, ${apkAnalyzerFilename} and add it to PATH. https://developer.android.com/studio#cmdline-tools and https://developer.android.com/studio/intro/update#sdk-manager may help to setup.`
);
});
})
@@ -138,8 +140,10 @@ describe('android', function () {
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'bundletool.jar is used to handle Android App Bundle. Please read http://appium.io/docs/en/writing-running-appium/android/android-appbundle/ to install it'
removeColors(await check.fix()).should.match(
new RegExp(
'bundletool.jar is used to handle Android App Bundle. Please read http://appium.io/docs/en/writing-running-appium/android/android-appbundle/ to install it'
)
);
});
})
@@ -158,8 +162,7 @@ describe('android', function () {
(await check.diagnose()).should.deep.equal({
ok: true,
optional: true,
message:
'gst-launch-1.0 and gst-inspect-1.0 are installed at: path/to/gst-launch and path/to/gst-inspect',
message: `${gstLaunchFilename} and ${gstInspectFilename} are installed at: path/to/gst-launch and path/to/gst-inspect`,
});
mocks.verify();
});
@@ -168,14 +171,13 @@ describe('android', function () {
(await check.diagnose()).should.deep.equal({
ok: false,
optional: true,
message: 'gst-launch-1.0 and/or gst-inspect-1.0 cannot be found',
message: `${gstLaunchFilename} and/or ${gstInspectFilename} cannot be found`,
});
mocks.verify();
});
it('fix', async function () {
removeColors(await check.fix()).should.equal(
'gst-launch-1.0 and gst-inspect-1.0 are used to stream the screen of the device under test. ' +
'Please read https://appium.io/docs/en/writing-running-appium/android/android-screen-streaming/ to install them and for more details'
`${gstLaunchFilename} and ${gstInspectFilename} are used to stream the screen of the device under test. Please read https://appium.io/docs/en/writing-running-appium/android/android-screen-streaming/ to install them and for more details`
);
});
})
+5 -4
View File
@@ -1,3 +1,4 @@
import path from 'node:path';
import {BinaryIsInPathCheck, AndroidSdkExists} from '../../lib/dev';
import {fs} from '@appium/support';
import * as tp from 'teen_process';
@@ -65,12 +66,12 @@ describe('dev', function () {
check.autofix.should.not.be.ok;
});
it('diagnose - success', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
process.env.ANDROID_HOME = path.resolve('/', 'a', 'b', 'c', 'd');
mocks.fs.expects('exists').once().returns(B.resolve(true));
(await check.diagnose()).should.deep.equal({
ok: true,
optional: false,
message: 'android-16 was found at: /a/b/c/d/platforms/android-16',
message: `android-16 was found at: ${process.env.ANDROID_HOME}${path.sep}platforms${path.sep}android-16`,
});
mocks.verify();
});
@@ -84,12 +85,12 @@ describe('dev', function () {
mocks.verify();
});
it('diagnose - failure - invalid path', async function () {
process.env.ANDROID_HOME = '/a/b/c/d';
process.env.ANDROID_HOME = path.resolve('/', 'a', 'b', 'c', 'd');
mocks.fs.expects('exists').once().returns(B.resolve(false));
(await check.diagnose()).should.deep.equal({
ok: false,
optional: false,
message: "android-16 could NOT be found at '/a/b/c/d/platforms/android-16'!",
message: `android-16 could NOT be found at '${process.env.ANDROID_HOME}${path.sep}platforms${path.sep}android-16'!`,
});
mocks.verify();
});
+55
View File
@@ -3,6 +3,61 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.4](https://github.com/appium/appium/compare/@appium/docutils@0.4.3...@appium/docutils@0.4.4) (2023-07-03)
### Bug Fixes
* **support:** update dependency semver to v7.5.3 ([ed1856d](https://github.com/appium/appium/commit/ed1856d720c442842d5c3fe5f6caeccb8229b5f1))
## [0.4.3](https://github.com/appium/appium/compare/@appium/docutils@0.4.2...@appium/docutils@0.4.3) (2023-06-29)
### Bug Fixes
* **docutils:** docs versioning and update latest ([#18804](https://github.com/appium/appium/issues/18804)) ([52a755a](https://github.com/appium/appium/commit/52a755a9135e06ad676cc422e37b5cab019c5b85))
## [0.4.2](https://github.com/appium/appium/compare/@appium/docutils@0.4.1...@appium/docutils@0.4.2) (2023-06-15)
### Bug Fixes
* **docutils:** set mike version to major.minor by default ([#18770](https://github.com/appium/appium/issues/18770)) ([1a5b8cc](https://github.com/appium/appium/commit/1a5b8cca2d22b0be0150fe87be963973c4cc04e5))
## [0.4.1](https://github.com/appium/appium/compare/@appium/docutils@0.4.0...@appium/docutils@0.4.1) (2023-06-14)
### Bug Fixes
* **docutils:** fixes some weirdness with default behavior and the "implies" keyword ([#18751](https://github.com/appium/appium/issues/18751)) ([e5254b5](https://github.com/appium/appium/commit/e5254b5b4564450252fd3eb55b773c0d7c615316))
## [0.4.0](https://github.com/appium/appium/compare/@appium/docutils@0.3.13...@appium/docutils@0.4.0) (2023-06-14)
### Features
* **appium:** Allow to provide ipv6 addresses and handle broadcast addresses in logs ([#18674](https://github.com/appium/appium/issues/18674)) ([8f63dab](https://github.com/appium/appium/commit/8f63dabb5dca882e522026f243e3445f5418874b))
* **docutils:** init: scafffold package.json ([2766dcc](https://github.com/appium/appium/commit/2766dcca4cfdc74904cc21f4881d748ed8ffd198))
### Bug Fixes
* **docutils:** update dependency yaml to v2.3.0 ([325d237](https://github.com/appium/appium/commit/325d2372078ee01e8fb58613ffd13417f2527151))
* **docutils:** update dependency yaml to v2.3.1 ([1a2260b](https://github.com/appium/appium/commit/1a2260bb15d75e4f4c23551dc61529108a7b0d88))
* **support:** update dependency semver to v7.5.1 ([5a55509](https://github.com/appium/appium/commit/5a555090c5322318f845415d743ee5c645fb94a0))
* **types:** update dependency type-fest to v3.11.0 ([19277f6](https://github.com/appium/appium/commit/19277f6e14a56e52b4669d633e148ad4a3da2c7a))
* **types:** update dependency type-fest to v3.11.1 ([56499eb](https://github.com/appium/appium/commit/56499eb997b551739bed628f057de7987674ea7f))
## [0.3.13](https://github.com/appium/appium/compare/@appium/docutils@0.3.12...@appium/docutils@0.3.13) (2023-05-19)
+2
View File
@@ -1,6 +1,8 @@
#!/usr/bin/env node
// @ts-check
'use strict';
require('source-map-support').install();
const {main} = require('../build/lib/cli');
const {getLogger} = require('../build/lib/logger');
+34 -22
View File
@@ -18,7 +18,7 @@ import {
NAME_PYTHON,
} from '../constants';
import {DocutilsError} from '../error';
import {findMkDocsYml, findPython, readPackageJson} from '../fs';
import {findMike, findMkDocsYml, findPython, readPackageJson} from '../fs';
import {getLogger} from '../logger';
import {argify, spawnBackgroundProcess, SpawnBackgroundProcessOpts, stopwatch} from '../util';
@@ -26,33 +26,29 @@ const log = getLogger('builder:deploy');
/**
* Runs `mike serve`
* @param pythonPath Path to Python 3 executable
* @param mikePath Path to `mike` executable
* @param args Extra args to `mike build`
* @param opts Extra options for `teen_process.Subprocess.start`
*/
async function doServe(
pythonPath: string,
mikePath: string,
args: string[] = [],
opts: SpawnBackgroundProcessOpts = {}
) {
const finalArgs = ['-m,', NAME_MIKE, 'serve', ...args];
return spawnBackgroundProcess(pythonPath, finalArgs, opts);
const finalArgs = ['serve', ...args];
return spawnBackgroundProcess(mikePath, finalArgs, opts);
}
/**
* Runs `mike build`
* @param pythonPath Path to Python 3 executable
* @param mikePath Path to `mike` executable
* @param args Extra args to `mike build`
* @param opts Extra options to `teen_process.exec`
*/
async function doDeploy(
pythonPath: string,
args: string[] = [],
opts: TeenProcessExecOptions = {}
) {
const finalArgs = ['-m', NAME_MIKE, 'deploy', ...args];
log.debug('Executing %s via: %s %O', NAME_MIKE, pythonPath, finalArgs);
return await exec(pythonPath, finalArgs, opts);
async function doDeploy(mikePath: string, args: string[] = [], opts: TeenProcessExecOptions = {}) {
const finalArgs = ['deploy', ...args];
log.debug('Executing %s via: %s %O', NAME_MIKE, mikePath, finalArgs);
return await exec(mikePath, finalArgs, opts);
}
/**
@@ -68,7 +64,14 @@ async function findDeployVersion(packageJsonPath?: string, cwd = process.cwd()):
'No "version" field found in package.json; please add one or specify a version to deploy'
);
}
return version;
// return MAJOR.MINOR as the version by default, if that is a thing we can extract, otherwise
// just return the version as is
const versionParts = version.split('.');
if (versionParts.length === 1) {
return version;
}
return `${versionParts[0]}.${versionParts[1]}`;
}
/**
@@ -125,17 +128,25 @@ export async function deploy({
port,
host,
};
const mikePath = await findMike();
if (!mikePath) {
throw new DocutilsError(
`Could not find ${NAME_MIKE} executable; please run "${NAME_BIN} init"`
);
}
if (serve) {
const mikeArgs = [
...argify(_.pickBy(mikeOpts, (value) => _.isNumber(value) || Boolean(value))),
version,
...argify(_.pickBy(mikeOpts, (value) => _.isNumber(value) || Boolean(value)))
];
if (alias) {
mikeArgs.push(alias);
mikeArgs.push('--update-aliases', version, alias);
} else {
mikeArgs.push(version);
}
stop(); // discard
// unsure about how SIGHUP is handled here
await doServe(pythonPath, mikeArgs, serveOpts);
await doServe(mikePath, mikeArgs, serveOpts);
} else {
log.info('Deploying into branch %s', branch);
const mikeArgs = [
@@ -145,12 +156,13 @@ export async function deploy({
(value, key) => _.includes(['port', 'host'], key) || (!_.isNumber(value) && !value)
)
),
version,
];
if (alias) {
mikeArgs.push(alias);
mikeArgs.push('--update-aliases', version, alias);
} else {
mikeArgs.push(version);
}
await doDeploy(pythonPath, mikeArgs, execOpts);
await doDeploy(mikePath, mikeArgs, execOpts);
log.success('Finished deployment into branch %s (%dms)', branch, stop());
}
+11 -9
View File
@@ -100,8 +100,10 @@ export function parseNav(nav: MkDocsYmlNav): ParsedNavData[] {
}
/**
* Finds all items within the list of parsed nav data which correpsond to the header. This is
* imperfect, as it's possible for the header string to appear in multiple places in the nav, but let's just ignore that until we can't.
* Finds all items within the list of parsed nav data which correpsond to the header.
*
* This is imperfect, as it's possible for the header string to appear in multiple places in the
* nav, but let's just ignore that until we can't.
* @param navData Some parsed nav data
* @param header Header name
*/
@@ -110,6 +112,8 @@ function filterHeaderItems(navData: ParsedNavData[], header: string) {
}
/**
* Given a root header keypath (like a prefix), a numeric offset, and some parsed nav data, compute
* a keypath
*
* @param rootHeaderKeypath Root keypath as determined by {@linkcode getRootHeaderKeypath}
* @param offset Numeric offset within the array of items for the header
@@ -136,16 +140,16 @@ function navDataDidChange(
newNavData: Array<Omit<ParsedNavData, 'name'>>,
navData: ParsedNavData[]
): boolean {
const diff = _.xorWith(
newNavData,
navData,
(a, b) => a.keypath === b.keypath && a.fileOrUrl === b.fileOrUrl
);
// the result should be the items from newNavData which either don't appear in
// navdata, or the items which do appear in navData but have a different fileOrUrl
const matchedKeypaths = _.intersectionBy(newNavData, navData, 'keypath');
const diff = _.xorBy(newNavData, matchedKeypaths, 'fileOrUrl');
return !_.isEmpty(diff);
}
/**
* Find the "root" keypath for a particular header
*
* @param headerItems Some subset of the nav data having keypaths corresponding to `header`
* @param header Header string
* @returns The keypath up to the header string, inclusive
@@ -167,7 +171,6 @@ function getRootHeaderKeypath(headerItems: ParsedNavData[], header: string) {
* by `dir`; if `all` is true, we just use the default header, because we will be returning a whole
* lot of headers).
*
*
* @param dir Abs path to directory containing markdown files generated by TypeDoc
* @param mkDocsDocsDir Configured `docs_dir` or via options
* @param navData Nav data parsed by {@linkcode parseNav}
@@ -202,7 +205,6 @@ async function getNavItemsForDir(
const offset = navHeaderItems.findIndex((item) => item.fileOrUrl === fileOrUrl);
const newOffset = offset >= 0 ? offset : navHeaderItems.length;
const data = navHeaderItems[offset];
log.warn(rootHeaderKeypath, newOffset, data);
const keypath = getKeypathForHeaderItem(rootHeaderKeypath, newOffset, data);
newNavHeaderItems.push({keypath, fileOrUrl});
}
+4 -1
View File
@@ -79,7 +79,10 @@ export async function checkMissingPaths<T extends Record<string, Options>>(
if (missingPaths.length) {
return missingPaths
.map(({id, path}) => `Path specified using --${_.kebabCase(id)} (${path}) does not exist`)
.map(
({id, path}) =>
`Default or specified path via --${_.kebabCase(id)} (${path}) does not exist`
)
.join('\n');
}
+4 -3
View File
@@ -124,7 +124,7 @@ const opts = {
remote: {
alias: 'r',
describe: 'Remote to push to',
implies: ['deploy', 'push'],
implies: 'push',
group: BuildCommandGroup.Deploy,
type: 'string',
requiresArg: true,
@@ -133,7 +133,7 @@ const opts = {
},
prefix: {
describe: 'Subdirectory within <branch> to commit to',
implies: ['deploy', 'branch'],
implies: 'branch',
group: BuildCommandGroup.Deploy,
type: 'string',
nargs: 1,
@@ -168,9 +168,10 @@ const opts = {
},
rebase: {
describe: 'Rebase <branch> with remote before deploy',
implies: ['deploy', 'branch', 'remote'],
implies: 'deploy',
group: BuildCommandGroup.Deploy,
type: 'boolean',
defaultDescription: 'true',
},
serve: {
describe: 'Start development server',
+22
View File
@@ -30,6 +30,7 @@ const opts = {
nargs: 1,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
dir: {
default: '.',
@@ -59,6 +60,7 @@ const opts = {
group: InitCommandGroup.MkDocs,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
mkdocs: {
default: true,
@@ -74,6 +76,7 @@ const opts = {
normalize: true,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
'package-json': {
defaultDescription: './package.json',
@@ -98,6 +101,7 @@ const opts = {
normalize: true,
requiresArg: true,
type: 'string',
implies: 'python',
},
'repo-name': {
defaultDescription: '(derived from --repo-url)',
@@ -106,6 +110,7 @@ const opts = {
nargs: 1,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
'repo-url': {
defaultDescription: '(from package.json)',
@@ -114,6 +119,7 @@ const opts = {
nargs: 1,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
'site-description': {
defaultDescription: '(from package.json)',
@@ -122,6 +128,7 @@ const opts = {
nargs: 1,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
'site-name': {
defaultDescription: '(extension package name)',
@@ -130,6 +137,7 @@ const opts = {
nargs: 1,
requiresArg: true,
type: 'string',
implies: 'mkdocs',
},
'tsconfig-json': {
defaultDescription: './tsconfig.json',
@@ -139,12 +147,14 @@ const opts = {
normalize: true,
requiresArg: true,
type: 'string',
implies: 'typescript',
},
typedoc: {
default: true,
description: 'Create typedoc.json if needed',
group: InitCommandGroup.Behavior,
type: 'boolean',
implies: 'entry-point',
},
'typedoc-json': {
defaultDescription: './typedoc.json',
@@ -154,6 +164,17 @@ const opts = {
normalize: true,
requiresArg: true,
type: 'string',
implies: 'typedoc',
},
'entry-point': {
alias: 'e',
describe: 'Source entry point of extension',
group: InitCommandGroup.Paths,
nargs: 1,
normalize: false,
requiresArg: true,
type: 'string',
implies: 'typedoc',
},
typescript: {
default: true,
@@ -167,6 +188,7 @@ const opts = {
group: InitCommandGroup.Behavior,
type: 'boolean',
conflicts: 'force',
implies: 'python',
},
} as const satisfies Record<string, Options>;
+27 -4
View File
@@ -8,19 +8,20 @@
import {getLogger} from '../logger';
import {fs} from '@appium/support';
import _ from 'lodash';
import {sync as readPkg} from 'read-pkg';
import {hideBin} from 'yargs/helpers';
import yargs from 'yargs/yargs';
import {DEFAULT_LOG_LEVEL, LogLevelMap, NAME_BIN} from '../constants';
import {DocutilsError} from '../error';
import {build, init, validate} from './command';
import {findConfig} from './config';
import {fs} from '@appium/support';
import {sync as readPkg} from 'read-pkg';
const pkg = readPkg({cwd: fs.findRoot(__dirname)});
const log = getLogger('cli');
const IMPLICATIONS_FAILED_REGEX = /implications\s+failed:\n\s*(.+)\s->\s(.+)$/i;
export async function main(argv = hideBin(process.argv)) {
const config = await findConfig(argv);
@@ -70,12 +71,34 @@ export async function main(argv = hideBin(process.argv)) {
* Custom failure handler so we can log nicely.
*/
(msg: string | null, error) => {
/**
* yargs' default output if an "implication" fails (e.g., arg _A_ requires arg _B_) leaves much to be desired.
*
* @remarks Unfortunately, we do not have access to the parsed arguments object, since it may have failed parsing.
* @param msg Implication failure message
* @returns Whether the message was an implication failure
*/
const handleImplicationFailure = (msg: string | null): boolean => {
let match: RegExpMatchArray | null | undefined;
if (!(match = msg?.match(IMPLICATIONS_FAILED_REGEX))) {
return false;
}
const [, arg, missingArg] = match;
log.error(
`Argument "--${arg}" requires "--${missingArg}"; note that "--${arg}" may be enabled by default`
);
return true;
};
// if it is a DocutilsError, it has nothing to do with the CLI
if (error instanceof DocutilsError) {
log.error(error.message);
} else {
y.showHelp();
log.error(`\n\n${msg ?? error.message}`);
if (!handleImplicationFailure(msg)) {
log.error(`\n\n${msg ?? error.message}`);
}
}
y.exit(1, error);
}
+30
View File
@@ -14,6 +14,7 @@ import {JsonValue} from 'type-fest';
import {Application, TypeDocReader} from 'typedoc';
import YAML from 'yaml';
import {
NAME_MIKE,
NAME_MKDOCS_YML,
NAME_NPM,
NAME_PACKAGE_JSON,
@@ -24,6 +25,7 @@ import {
import {DocutilsError} from './error';
import {getLogger} from './logger';
import {MkDocsYml} from './model';
import { exec } from 'teen_process';
const log = getLogger('fs');
@@ -228,6 +230,34 @@ const whichPython3 = _.partial(cachedWhich, `${NAME_PYTHON}3`, {nothrow: true});
*/
const whichTypeDoc = _.partial(cachedWhich, NAME_TYPEDOC, {nothrow: true});
/**
* `mike` cannot be invoked via `python -m`, so we need to find the script.
*/
export const findMike = _.partial(async () => {
// see if it's in PATH
let mikePath = await cachedWhich(NAME_MIKE, {nothrow: true});
if (mikePath) {
return mikePath;
}
// if it isn't, it may be in a user dir
const pythonPath = await findPython();
if (!pythonPath) {
return;
}
try {
// the user dir can be found this way.
// usually it's something like ~/.local
const {stdout} = await exec(pythonPath, ['-m', 'site', '--user-base']);
if (stdout) {
mikePath = path.join(stdout.trim(), 'bin', 'mike');
if (await fs.isExecutable(mikePath)) {
return mikePath;
}
}
}
catch {}
});
/**
* Finds the `typedoc` executable.
*
+49 -2
View File
@@ -11,10 +11,11 @@ import {
NAME_PYTHON,
REQUIREMENTS_TXT_PATH,
NAME_TYPEDOC_JSON,
NAME_PACKAGE_JSON,
} from './constants';
import YAML from 'yaml';
import {exec} from 'teen_process';
import {Simplify} from 'type-fest';
import {PackageJson, Simplify} from 'type-fest';
import {DocutilsError} from './error';
import {createScaffoldTask, ScaffoldTaskOptions} from './scaffold';
import {getLogger} from './logger';
@@ -54,9 +55,19 @@ const BASE_TSCONFIG_JSON: Readonly<TsConfigJson> = Object.freeze({
},
});
/**
* Data for the base `package.json` file.
* We expect `package.json` to exist, and we are not in the business of creating it.
* However, we will need to add a `typedoc.entryPoint` prop to it.
*/
const BASE_PACKAGE_JSON: Readonly<PackageJson> = Object.freeze({});
const log = getLogger('init');
const dryRunLog = getLogger('dry-run', log);
/**
* Files included, by default, in `tsconfig.json`
*/
const DEFAULT_INCLUDE = ['lib', 'test', 'index.js'];
/**
* Function which scaffolds a `tsconfig.json` file
@@ -97,6 +108,26 @@ export const initTypeDocJson = createScaffoldTask<InitTypeDocOptions, TypeDocJso
'TypeDoc configuration'
);
/**
* Function which scaffolds a `package.json` file
*
* This only amends prop `typedoc.entryPoint` to the `package.json` file.
*
* If, strangely, `package.json` did not exist, then it will now contain _only_ that prop.
*/
export const initTypeDocPkgJson = createScaffoldTask<InitTypeDocOptions, PackageJson>(
NAME_PACKAGE_JSON,
BASE_PACKAGE_JSON,
'Package configuration for TypeDoc',
{
transform: (content, opts) =>
({
...content,
typedoc: {entryPoint: opts.typeDocEntryPoint},
} as PackageJson),
}
);
/**
* Function which scaffolds an `mkdocs.yml` file
*/
@@ -224,6 +255,7 @@ export async function init({
pythonPath,
upgrade,
typedocJson: typeDocJsonPath,
entryPoint: typeDocEntryPoint,
}: InitOptions = {}): Promise<void> {
if (!typescript && typedoc) {
log.warn(
@@ -250,6 +282,13 @@ export async function init({
dryRun,
cwd,
});
await initTypeDocPkgJson({
packageJson: packageJsonPath,
overwrite: true, // <-- always overwrite
dryRun,
cwd,
typeDocEntryPoint,
});
}
if (python) {
@@ -271,7 +310,10 @@ export async function init({
}
}
export type InitTypeDocOptions = ScaffoldTaskOptions;
export interface InitTypeDocOptions extends ScaffoldTaskOptions {
typeDocEntryPoint?: string;
}
export interface InitTsConfigOptions extends ScaffoldTaskOptions {
/**
* List of source files (globs supported); typically `src` or `lib`
@@ -334,5 +376,10 @@ export type InitOptions = Simplify<
* If `true`, upgrade only
*/
upgrade?: boolean;
/**
* Path to entry point of extension (source; not "main" field)
*/
entryPoint?: string;
}
>;
+11 -4
View File
@@ -92,6 +92,7 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
const relativeDest = relativePath(dest);
log.debug('Initializing %s', relativeDest);
let shouldWriteDest = false;
let isNew = false;
let destContent: T;
let result: ScaffoldTaskResult<T>;
try {
@@ -105,6 +106,7 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
shouldWriteDest = true;
log.debug('Creating new file %s', relativeDest);
destContent = {} as T;
isNew = true;
}
const defaults: T = transform(defaultContent, opts, pkg);
@@ -113,7 +115,7 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
shouldWriteDest = shouldWriteDest || !_.isEqual(destContent, finalDestContent);
if (shouldWriteDest) {
log.info('Changes needed to %s', relativeDest);
log.info('Changes needed in %s', relativeDest);
log.debug('Original %s: %O', relativeDest, destContent);
log.debug('Final %s: %O', relativeDest, finalDestContent);
const patch = makePatch(dest, destContent, finalDestContent, serialize);
@@ -126,6 +128,11 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
try {
await safeWriteFile(dest, finalDestContent, overwrite);
if (isNew) {
log.success('Initialized %s', description);
} else {
log.success('Updated %s', description);
}
} catch (e) {
const err = e as NodeJS.ErrnoException;
// this should only be thrown if `force` is false
@@ -139,9 +146,9 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
}
}
} else {
log.info('No changes to %s', relativeDest);
log.info('No changes necessary for %s', relativeDest);
}
log.success('Initialized %s', description);
log.success(`${description}: done`);
return {path: dest, content: finalDestContent};
};
}
@@ -153,7 +160,7 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
export type ScaffoldTaskTransformer<Opts extends ScaffoldTaskOptions, T extends JsonValue> = (
content: Readonly<T>,
opts: TaskSpecificOpts<Opts>,
pkg: NormalizedPackageJson
pkg: Readonly<NormalizedPackageJson>
) => T;
/**
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/docutils",
"version": "0.3.13",
"version": "0.4.4",
"description": "Documentation generation utilities for Appium and related projects",
"keywords": [
"automation",
@@ -49,9 +49,9 @@
"start": "node ./build/lib/cli/index.js"
},
"dependencies": {
"@appium/support": "^4.0.1",
"@appium/support": "^4.1.2",
"@appium/tsconfig": "^0.3.0",
"@appium/typedoc-plugin-appium": "^0.6.4",
"@appium/typedoc-plugin-appium": "^0.6.5",
"@sliphua/lilconfig-ts-loader": "3.2.2",
"@types/which": "3.0.0",
"chalk": "4.1.2",
@@ -65,15 +65,15 @@
"log-symbols": "4.1.0",
"pkg-dir": "5.0.0",
"read-pkg": "5.2.0",
"semver": "7.5.0",
"semver": "7.5.3",
"source-map-support": "0.5.21",
"teen_process": "2.0.2",
"type-fest": "3.10.0",
"type-fest": "3.11.1",
"typedoc": "0.23.28",
"typedoc-plugin-markdown": "3.14.0",
"typedoc-plugin-resolve-crossmodule-references": "0.3.3",
"typescript": "4.9.5",
"yaml": "2.2.2",
"yaml": "2.3.1",
"yargs": "17.7.2",
"yargs-parser": "21.1.1"
},
+1 -1
View File
@@ -1,4 +1,4 @@
mkdocs==1.4.3
mkdocs-material==9.1.9
mkdocs-material==9.1.17
mkdocs-material-extensions==1.1.1
mike==1.1.2
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.4.10](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.9...@appium/driver-test-support@0.4.10) (2023-07-03)
**Note:** Version bump only for package @appium/driver-test-support
## [0.4.9](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.8...@appium/driver-test-support@0.4.9) (2023-06-29)
**Note:** Version bump only for package @appium/driver-test-support
## [0.4.8](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.7...@appium/driver-test-support@0.4.8) (2023-06-15)
**Note:** Version bump only for package @appium/driver-test-support
## [0.4.7](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.6...@appium/driver-test-support@0.4.7) (2023-06-14)
### Bug Fixes
* **schema:** update definitelytyped ([e967240](https://github.com/appium/appium/commit/e96724077ef2b5b8aae203856195f3bb8de56116))
* **test-support:** update dependency sinon to v15.1.0 ([4c61d53](https://github.com/appium/appium/commit/4c61d533b8f52131a637f5cd0b019375f85f075f))
* **types:** update dependency type-fest to v3.11.0 ([19277f6](https://github.com/appium/appium/commit/19277f6e14a56e52b4669d633e148ad4a3da2c7a))
* **types:** update dependency type-fest to v3.11.1 ([56499eb](https://github.com/appium/appium/commit/56499eb997b551739bed628f057de7987674ea7f))
## [0.4.6](https://github.com/appium/appium/compare/@appium/driver-test-support@0.4.5...@appium/driver-test-support@0.4.6) (2023-05-17)
+52 -20
View File
@@ -5,8 +5,10 @@ import B from 'bluebird';
import {TEST_HOST, getTestPort, createAppiumURL} from './helpers';
import chai from 'chai';
import sinon from 'sinon';
import {Agent} from 'node:http';
const should = chai.should();
const {expect} = chai;
/**
* Creates some helper functions for E2E tests to manage sessions.
@@ -148,6 +150,9 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
describe('session handling', function () {
it('should handle idempotency while creating sessions', async function () {
// workaround for https://github.com/node-fetch/node-fetch/issues/1735
const httpAgent = new Agent({keepAlive: true});
const sessionIds = [];
let times = 0;
do {
@@ -159,11 +164,7 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
headers: {
'X-Idempotency-Key': '123456',
},
// XXX: I'm not sure what these are, as they are not documented axios options,
// nor are they mentioned in our source
// @ts-expect-error
simple: false,
resolveWithFullResponse: true,
httpAgent,
}
);
@@ -178,6 +179,9 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
});
it('should handle idempotency while creating parallel sessions', async function () {
// workaround for https://github.com/node-fetch/node-fetch/issues/1735
const httpAgent = new Agent({keepAlive: true});
const reqs = [];
let times = 0;
do {
@@ -192,6 +196,7 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
headers: {
'X-Idempotency-Key': '12345',
},
httpAgent,
}
)
);
@@ -396,22 +401,49 @@ export function driverE2ETestSuite(DriverClass, defaultCaps = {}) {
});
describe('event timings', function () {
it('should not add timings if not using opt-in cap', async function () {
const session = await startSession({capabilities: {alwaysMatch: defaultCaps}});
const res = await getSession(session.sessionId);
should.not.exist(res.events);
await endSession(session.sessionId);
/** @type {NewSessionResponse} */
let session;
/** @type {SingularSessionData} */
let res;
describe('when not provided the eventTimings cap', function () {
before(async function () {
session = await startSession({capabilities: {alwaysMatch: defaultCaps}});
res = await getSession(session.sessionId);
});
after(async function () {
if (session) {
await endSession(session.sessionId);
}
});
it('should not respond with events', function () {
expect(res.events).to.be.undefined;
});
});
it('should add start session timings', async function () {
const caps = {...defaultCaps, 'appium:eventTimings': true};
const session = await startSession({capabilities: {alwaysMatch: caps}});
const res = await getSession(session.sessionId);
should.exist(res.events);
should.exist(res.events?.newSessionRequested);
should.exist(res.events?.newSessionStarted);
res.events?.newSessionRequested[0].should.be.a('number');
res.events?.newSessionStarted[0].should.be.a('number');
await endSession(session.sessionId);
describe('when provided the eventTimings cap', function () {
before(async function () {
session = await startSession({
capabilities: {alwaysMatch: {...defaultCaps, 'appium:eventTimings': true}},
});
res = await getSession(session.sessionId);
});
after(async function () {
if (session) {
await endSession(session.sessionId);
}
});
it('should add a newSessionRequested event', function () {
expect(res.events?.newSessionRequested?.[0]).to.be.a('number');
});
it('should add a newSessionStarted event', function () {
expect(res.events?.newSessionRequested?.[0]).to.be.a('number');
});
});
});
});
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/driver-test-support",
"version": "0.4.6",
"version": "0.4.10",
"description": "Test utilities for Appium drivers",
"keywords": [
"automation",
@@ -41,18 +41,18 @@
},
"types": "./build/lib/index.d.ts",
"dependencies": {
"@appium/types": "^0.11.1",
"@types/lodash": "4.14.194",
"@appium/types": "^0.13.2",
"@types/lodash": "4.14.195",
"@types/stoppable": "1.1.1",
"axios": "1.4.0",
"bluebird": "3.7.2",
"chai": "4.3.7",
"get-port": "5.1.1",
"lodash": "4.17.21",
"sinon": "15.0.4",
"sinon": "15.1.0",
"source-map-support": "0.5.21",
"stoppable": "1.1.0",
"type-fest": "3.10.0"
"type-fest": "3.11.1"
},
"peerDependencies": {
"appium": "^2.0.0-beta.43",
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.0.4](https://github.com/appium/appium/compare/@appium/eslint-config-appium@8.0.3...@appium/eslint-config-appium@8.0.4) (2023-07-03)
### Bug Fixes
* **eslint-config-appium:** remove prototype assignment warning ([679865e](https://github.com/appium/appium/commit/679865ed2f1df59baef8c4d884b0a63a9222940e))
## [8.0.3](https://github.com/appium/appium/compare/@appium/eslint-config-appium@8.0.2...@appium/eslint-config-appium@8.0.3) (2023-04-03)
**Note:** Version bump only for package @appium/eslint-config-appium
-12
View File
@@ -91,18 +91,6 @@ module.exports = {
'require-atomic-updates': 0,
'no-prototype-builtins': 1,
'no-redeclare': 1,
'no-restricted-syntax': [
'warn',
{
selector: 'AssignmentExpression[left.object.property.name="prototype"]',
message: 'Avoid assignment to prototype; use class fields, methods or mixins instead.',
},
{
selector:
'CallExpression[callee.object.name="Object"][callee.property.name="assign"][arguments.0.property.name="prototype"]',
message: 'Avoid assignment to prototype; use class fields, methods or mixins instead.',
},
],
},
overrides: [
/**
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/eslint-config-appium",
"version": "8.0.3",
"version": "8.0.4",
"description": "Shared ESLint config for Appium projects",
"keywords": [
"eslint",
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.0.14](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.13...@appium/execute-driver-plugin@3.0.14) (2023-06-14)
### Bug Fixes
* **execute-driver-plugin:** update dependency vm2 to v3.9.19 ([8594d13](https://github.com/appium/appium/commit/8594d13bf6a1cb59bc76cbe2d39976ae86cdb3b2))
## [3.0.13](https://github.com/appium/appium/compare/@appium/execute-driver-plugin@3.0.12...@appium/execute-driver-plugin@3.0.13) (2023-05-17)
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/execute-driver-plugin",
"version": "3.0.13",
"version": "3.0.14",
"description": "Plugin for batching and executing driver commands with Appiums",
"keywords": [
"automation",
@@ -42,7 +42,7 @@
"bluebird": "3.7.2",
"lodash": "4.17.21",
"source-map-support": "0.5.21",
"vm2": "3.9.17",
"vm2": "3.9.19",
"webdriverio": "7.31.1"
},
"peerDependencies": {
+29
View File
@@ -3,6 +3,35 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [5.2.6](https://github.com/appium/appium/compare/@appium/fake-driver@5.2.5...@appium/fake-driver@5.2.6) (2023-07-03)
### Bug Fixes
* **types:** separate the type of opts from initialOpts ([d6cca51](https://github.com/appium/appium/commit/d6cca5175c3e55d4670936c9d216cd3a6610d16b))
## [5.2.5](https://github.com/appium/appium/compare/@appium/fake-driver@5.2.4...@appium/fake-driver@5.2.5) (2023-06-15)
### Bug Fixes
* **fake-driver:** update definition of findElOrEls ([598b255](https://github.com/appium/appium/commit/598b255e8d4a2b41cca0ec0c02a54c46946eadb5))
* **types,fake-driver:** getLog doesn't necessarily return an array ([00ae162](https://github.com/appium/appium/commit/00ae1625d73305b8840d45a0dc98ac3cc628ac37))
* **types:** various fixes for reality ([81bc527](https://github.com/appium/appium/commit/81bc527be6aa54dd30a012156b5752b8b821ac0c))
## [5.2.4](https://github.com/appium/appium/compare/@appium/fake-driver@5.2.3...@appium/fake-driver@5.2.4) (2023-06-14)
### Bug Fixes
* **universal-xml-plugin:** update dependency @xmldom/xmldom to v0.8.8 ([3bf4060](https://github.com/appium/appium/commit/3bf406015eb9854abbf6cf024e3bd724468c924d))
## [5.2.3](https://github.com/appium/appium/compare/@appium/fake-driver@5.2.2...@appium/fake-driver@5.2.3) (2023-04-03)
+70 -46
View File
@@ -7,17 +7,82 @@ import {util} from '@appium/support';
import {mixin} from './mixin';
const {W3C_WEB_ELEMENT_IDENTIFIER} = util;
async function findElOrEls<Ctx = any>(
this: FakeDriver<any>,
strategy: string,
selector: string,
mult: true,
context?: Ctx
): Promise<Element[]>;
async function findElOrEls<Ctx = any>(
this: FakeDriver<any>,
strategy: string,
selector: string,
mult: false,
context?: Ctx
): Promise<Element>;
async function findElOrEls<Ctx = any>(
this: FakeDriver<any>,
strategy: string,
selector: string,
mult: boolean,
context?: Ctx
): Promise<Element | Element[]> {
const qMap = {
xpath: 'xpathQuery',
id: 'idQuery',
'accessibility id': 'idQuery',
'class name': 'classQuery',
'tag name': 'classQuery',
'css selector': 'cssQuery',
} as Record<string, keyof FakeDriver['appModel']>;
// TODO this error checking should probably be part of MJSONWP?
if (!_.includes(_.keys(qMap), strategy)) {
throw new errors.UnknownCommandError();
}
if (selector === 'badsel') {
throw new errors.InvalidSelectorError();
}
const els = this.appModel[qMap[strategy]](selector, context);
let retval: Element | Element[];
if (els.length) {
if (mult) {
const allEls: Element[] = [];
for (const el of els) {
allEls.push(this.wrapNewEl(el));
}
retval = allEls;
} else {
retval = this.wrapNewEl(els[0]);
}
} else if (mult) {
retval = [];
} else {
throw new errors.NoSuchElementError();
}
return retval;
}
interface FakeDriverFindMixin {
getExistingElementForNode(node: FakeElement): string | null;
wrapNewEl(obj: FakeElement): Element;
findElOrEls<Many extends boolean, Ctx = any>(
findElOrEls(
this: FakeDriver,
strategy: string,
selector: string,
many: Many,
context?: Ctx
): Promise<Many extends true ? Element[] : Element>;
mult: true,
context?: any
): Promise<Element[]>;
findElOrEls(
this: FakeDriver,
strategy: string,
selector: string,
mult: false,
context?: any
): Promise<Element>;
findElement(strategy: string, selector: string): Promise<Element>;
findElements(strategy: string, selector: string): Promise<Element[]>;
findElementFromElement(elementId: string, strategy: string, selector: string): Promise<Element>;
@@ -56,48 +121,7 @@ const FindMixin: FakeDriverFindMixin = {
return {ELEMENT: maxElId, [W3C_WEB_ELEMENT_IDENTIFIER]: maxElId};
},
async findElOrEls<Many extends boolean, Ctx = any>(
this: FakeDriver,
strategy: string,
selector: string,
many: Many,
context: Ctx
): Promise<Many extends true ? Element[] : Element> {
const qMap = {
xpath: 'xpathQuery',
id: 'idQuery',
'accessibility id': 'idQuery',
'class name': 'classQuery',
'tag name': 'classQuery',
'css selector': 'cssQuery',
} as Record<string, keyof FakeDriver['appModel']>;
// TODO this error checking should probably be part of MJSONWP?
if (!_.includes(_.keys(qMap), strategy)) {
throw new errors.UnknownCommandError();
}
if (selector === 'badsel') {
throw new errors.InvalidSelectorError();
}
const els = this.appModel[qMap[strategy]](selector, context);
let retval: Element | Element[];
if (els.length) {
if (many) {
const allEls: Element[] = [];
for (const el of els) {
allEls.push(this.wrapNewEl(el));
}
retval = allEls;
} else {
retval = this.wrapNewEl(els[0]);
}
} else if (many) {
retval = [];
} else {
throw new errors.NoSuchElementError();
}
return retval as Many extends true ? Element[] : Element;
},
findElOrEls,
/**
* This should override whatever's in ExternalDriver
+3 -2
View File
@@ -8,7 +8,7 @@ const ORIENTATIONS = new Set(['LANDSCAPE', 'PORTRAIT']);
interface FakeDriverGeneralMixin {
title(): Promise<string>;
keys(value: string | string[]): Promise<void>;
setGeoLocation(location: Location): Promise<void>;
setGeoLocation(location: Location): Promise<Location>;
getGeoLocation(): Promise<Location>;
getPageSource(): Promise<string>;
getOrientation(): Promise<string>;
@@ -22,7 +22,7 @@ interface FakeDriverGeneralMixin {
doubleClick(): Promise<void>;
execute(script: string, args: any[]): Promise<any>;
fakeAddition(a: number, b: number, c?: number): Promise<number>;
getLog(type: string): Promise<unknown[]>;
getLog(type: string): Promise<any>;
}
declare module '../driver' {
@@ -46,6 +46,7 @@ const GeneralMixin: FakeDriverGeneralMixin = {
// TODO test this adequately once WD bug is fixed
this.appModel.lat = location.latitude;
this.appModel.long = location.longitude;
return location;
},
async getGeoLocation(this: FakeDriver) {
+1 -1
View File
@@ -55,7 +55,7 @@ export class FakeDriver extends BaseDriver {
elMap;
constructor(
opts = /** @type {import('@appium/types').DriverOpts<FakeDriverConstraints>} */ ({}),
opts = /** @type {import('@appium/types').InitialOpts} */ ({}),
shouldValidateCaps = true
) {
super(opts, shouldValidateCaps);
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-driver",
"version": "5.2.3",
"version": "5.2.6",
"description": "Mock driver used internally by Appium for testing. Ignore",
"keywords": [
"automation",
@@ -43,11 +43,11 @@
"test": "npm run test:unit",
"test:e2e": "mocha --timeout 20s --slow 10s \"./test/e2e/**/*.spec.js\"",
"test:smoke": "node ./build/lib/index.js",
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
"test:unit": "mocha --timeout 10s --slow 5s \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@types/bluebird": "3.5.38",
"@xmldom/xmldom": "0.8.7",
"@xmldom/xmldom": "0.8.8",
"asyncbox": "2.9.4",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+48
View File
@@ -3,6 +3,54 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [3.1.19](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.18...@appium/fake-plugin@3.1.19) (2023-07-03)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.18](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.17...@appium/fake-plugin@3.1.18) (2023-06-29)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.17](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.16...@appium/fake-plugin@3.1.17) (2023-06-15)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.16](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.15...@appium/fake-plugin@3.1.16) (2023-06-14)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.15](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.14...@appium/fake-plugin@3.1.15) (2023-06-14)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.14](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.13...@appium/fake-plugin@3.1.14) (2023-05-19)
**Note:** Version bump only for package @appium/fake-plugin
## [3.1.13](https://github.com/appium/appium/compare/@appium/fake-plugin@3.1.12...@appium/fake-plugin@3.1.13) (2023-05-19)
**Note:** Version bump only for package @appium/fake-plugin
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/fake-plugin",
"version": "3.1.13",
"version": "3.1.19",
"description": "A fake Appium 2.0 plugin",
"keywords": [
"automation",
@@ -39,8 +39,8 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/base-plugin": "^2.2.9",
"@appium/support": "^4.0.1",
"@appium/base-plugin": "^2.2.15",
"@appium/support": "^4.1.2",
"@types/bluebird": "3.5.38",
"bluebird": "3.7.2",
"lodash": "4.17.21",
+9
View File
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.2](https://github.com/appium/appium/compare/@appium/images-plugin@2.1.1...@appium/images-plugin@2.1.2) (2023-06-14)
### Bug Fixes
* **images-plugin:** Make sure the template is always a buffer ([#18696](https://github.com/appium/appium/issues/18696)) ([0497090](https://github.com/appium/appium/commit/0497090a90e6219a383125543780cd71269a4604))
## [2.1.1](https://github.com/appium/appium/compare/@appium/images-plugin@2.1.0...@appium/images-plugin@2.1.1) (2023-05-19)
+5 -1
View File
@@ -239,7 +239,11 @@ export default class ImageElement {
if (strategy !== IMAGE_STRATEGY) {
throw new errors.InvalidSelectorError(`Lookup strategies other than '${IMAGE_STRATEGY}' are not supported`);
}
return await this.finder.findByImage(selector, driver, {multiple, containerRect: this.rect});
return await this.finder.findByImage(
Buffer.from(selector, 'base64'),
driver,
{multiple, containerRect: this.rect}
);
}
/**
+1 -1
View File
@@ -52,7 +52,7 @@ export default class ImageElementPlugin extends BasePlugin {
return await next();
}
return await this.finder.findByImage(selector, driver, {multiple});
return await this.finder.findByImage(Buffer.from(selector, 'base64'), driver, {multiple});
}
async handle(next, driver, cmdName, ...args) {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/images-plugin",
"version": "2.1.1",
"version": "2.1.2",
"description": "Plugin for working with images and image elements in Appium",
"keywords": [
"automation",
@@ -39,7 +39,7 @@
"test:unit": "mocha \"./test/unit/**/*.spec.js\""
},
"dependencies": {
"@appium/opencv": "^2.1.0",
"@appium/opencv": "^2.1.1",
"lodash": "4.17.21",
"lru-cache": "7.18.3",
"sharp": "0.32.1",
+9
View File
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.1.1](https://github.com/appium/appium/compare/@appium/opencv@2.1.0...@appium/opencv@2.1.1) (2023-06-14)
### Bug Fixes
* Do not use additional checks on opencv objects ([#18714](https://github.com/appium/appium/issues/18714)) ([38f8690](https://github.com/appium/appium/commit/38f86900da462a444ae5a9c92794f64a297f79bd))
## [2.1.0](https://github.com/appium/appium/compare/@appium/opencv@2.0.10...@appium/opencv@2.1.0) (2023-05-17)
+2 -6
View File
@@ -1,5 +1,3 @@
import _ from 'lodash';
export class OpenCvAutoreleasePool {
/**
* @type {Set}
@@ -17,9 +15,7 @@ export class OpenCvAutoreleasePool {
*/
add(...items) {
for (const item of items) {
if (_.has(item, 'delete') && _.isFunction(item.delete)) {
this._items.add(item);
}
this._items.add(item);
}
return items.length === 1 ? items[0] : items;
}
@@ -32,4 +28,4 @@ export class OpenCvAutoreleasePool {
}
this._items.clear();
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/opencv",
"version": "2.1.0",
"version": "2.1.1",
"description": "OpenCV-related helper library",
"keywords": [
"automation",
+32
View File
@@ -3,6 +3,38 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.3.18](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.17...@appium/plugin-test-support@0.3.18) (2023-07-03)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.17](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.16...@appium/plugin-test-support@0.3.17) (2023-06-29)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.16](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.15...@appium/plugin-test-support@0.3.16) (2023-06-15)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.15](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.14...@appium/plugin-test-support@0.3.15) (2023-06-14)
**Note:** Version bump only for package @appium/plugin-test-support
## [0.3.14](https://github.com/appium/appium/compare/@appium/plugin-test-support@0.3.13...@appium/plugin-test-support@0.3.14) (2023-05-17)
**Note:** Version bump only for package @appium/plugin-test-support
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/plugin-test-support",
"version": "0.3.14",
"version": "0.3.18",
"description": "Testing utilities for Appium plugins",
"keywords": [
"automation",
@@ -41,7 +41,7 @@
"test:unit": "exit 0"
},
"dependencies": {
"@appium/types": "^0.11.1",
"@appium/types": "^0.13.2",
"get-port": "5.1.1",
"log-symbols": "4.1.0",
"source-map-support": "0.5.21",
+24
View File
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.3.1](https://github.com/appium/appium/compare/@appium/schema@0.3.0...@appium/schema@0.3.1) (2023-07-03)
### Bug Fixes
* **appium:** restrict address to ipv6/hostname ([#18824](https://github.com/appium/appium/issues/18824)) ([f09fbb6](https://github.com/appium/appium/commit/f09fbb64dce0e179a007f3a0ae9800e61fbe90eb)), closes [#18716](https://github.com/appium/appium/issues/18716)
## [0.3.0](https://github.com/appium/appium/compare/@appium/schema@0.2.6...@appium/schema@0.3.0) (2023-06-14)
### Features
* **appium:** Allow to provide ipv6 addresses and handle broadcast addresses in logs ([#18674](https://github.com/appium/appium/issues/18674)) ([8f63dab](https://github.com/appium/appium/commit/8f63dabb5dca882e522026f243e3445f5418874b))
### Bug Fixes
* **schema:** update definitelytyped ([e967240](https://github.com/appium/appium/commit/e96724077ef2b5b8aae203856195f3bb8de56116))
* **types:** fix broken appium config schema types, temporarily ([fd2c72f](https://github.com/appium/appium/commit/fd2c72fd886ecaaf0f6588a328ff8a268a54fb55))
## [0.2.6](https://github.com/appium/appium/compare/@appium/schema@0.2.5...@appium/schema@0.2.6) (2023-03-08)
**Note:** Version bump only for package @appium/schema
+12 -4
View File
@@ -21,13 +21,21 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
description: 'Configuration when running Appium as a server',
properties: {
address: {
$comment: 'I think hostname covers both DNS and IPv4...could be wrong',
appiumCliAliases: ['a'],
default: '0.0.0.0',
description: 'IP address to listen on',
format: 'hostname',
description: 'IPv4/IPv6 address or a hostname to listen on',
title: 'address config',
type: 'string',
anyOf: [
{
type: 'string',
format: 'hostname',
},
{
type: 'string',
format: 'ipv6',
},
],
},
'allow-cors': {
description:
@@ -338,7 +346,7 @@ export const AppiumConfigJsonSchema = /** @type {const} */ ({
},
},
{
oneOf: [{$ref: '#/$defs/logFilterText'}, {$ref: '#/$defs/logFilterRegex'}],
anyOf: [{$ref: '#/$defs/logFilterText'}, {$ref: '#/$defs/logFilterRegex'}],
},
],
},
+13 -5
View File
@@ -14,15 +14,23 @@
"description": "Configuration when running Appium as a server",
"properties": {
"address": {
"$comment": "I think hostname covers both DNS and IPv4...could be wrong",
"appiumCliAliases": [
"a"
],
"default": "0.0.0.0",
"description": "IP address to listen on",
"format": "hostname",
"description": "IPv4/IPv6 address or a hostname to listen on",
"title": "address config",
"type": "string"
"type": "string",
"anyOf": [
{
"type": "string",
"format": "hostname"
},
{
"type": "string",
"format": "ipv6"
}
]
},
"allow-cors": {
"description": "Whether the Appium server should allow web browser connections from any host",
@@ -339,7 +347,7 @@
}
},
{
"oneOf": [
"anyOf": [
{
"$ref": "#/$defs/logFilterText"
},
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/schema",
"version": "0.2.6",
"version": "0.3.1",
"description": "Appium Configuration Schema",
"keywords": [
"automation",
@@ -38,7 +38,7 @@
"test": "exit 0"
},
"dependencies": {
"@types/json-schema": "7.0.11",
"@types/json-schema": "7.0.12",
"json-schema": "0.4.0",
"source-map-support": "0.5.21"
},
+9
View File
@@ -3,6 +3,15 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.3.1](https://github.com/appium/appium/compare/@appium/strongbox@0.3.0...@appium/strongbox@0.3.1) (2023-06-14)
### Bug Fixes
* **strongbox:** do not force-lowercase slugged containers ([1ccf857](https://github.com/appium/appium/commit/1ccf857a4bdd77af3ccf2ad268e3410855509af8))
## [0.3.0](https://github.com/appium/appium/compare/@appium/strongbox@0.2.0...@appium/strongbox@0.3.0) (2023-04-20)
+1 -2
View File
@@ -1,6 +1,5 @@
import path from 'node:path';
import slug from 'slugify';
export function slugify(value: string) {
return slug(value, {lower: true});
return slug(value);
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/strongbox",
"version": "0.3.0",
"version": "0.3.1",
"description": "Persistent storage for Appium extensions",
"keywords": [
"automation",
@@ -12,7 +12,7 @@ type MockFs = {
describe('Strongbox', function () {
let sandbox: SinonSandbox;
let MockFs: MockFs = {} as any;
const DATA_DIR = '/some/dir';
const DATA_DIR = path.resolve(path.sep, 'some', 'dir');
// note to self: looks like this is safe to do before the rewiremock.proxy call
let BaseItem: typeof import('../../lib/base-item').BaseItem;
+14 -17
View File
@@ -16,7 +16,7 @@ describe('Strongbox', function () {
let DEFAULT_SUFFIX: string;
let MockFs: MockFs = {} as any;
const DATA_DIR = '/some/dir';
const DATA_DIR = path.resolve('some', 'dir', 'strongbox');
beforeEach(function () {
sandbox = createSandbox();
@@ -44,17 +44,18 @@ describe('Strongbox', function () {
describe('when provided an absolute container path', function () {
it('should use the provided container path', function () {
expect(strongbox('test', {container: '/somewhere/else'}).container).to.equal(
'/somewhere/else'
);
const container = path.resolve(path.sep, 'somewhere');
expect(strongbox('test', {container}).container).to.equal(container);
});
});
describe('when provided a relative container path', function () {
it('should throw an error', function () {
expect(() => strongbox('test', {container: 'somewhere/else'})).to.throw(
const container = path.join('somewhere', 'else');
expect(() => strongbox('test', {container})).to.throw(
TypeError,
'container slug somewhere/else must be an absolute path'
`container slug ${container} must be an absolute path`
);
});
});
@@ -85,17 +86,13 @@ describe('Strongbox', function () {
it('should create an empty Item', async function () {
expect(item).to.eql({
id: '/some/dir/strongbox/slug-test',
id: path.resolve(DATA_DIR, 'strongbox', 'SLUG-test'),
name: 'SLUG test',
encoding: 'utf8',
value: undefined,
container: '/some/dir/strongbox',
container: path.resolve(DATA_DIR, 'strongbox'),
});
});
it('should slugify the id', function () {
expect(item.id).to.equal('/some/dir/strongbox/slug-test');
});
});
describe('when the file exists', function () {
@@ -105,11 +102,11 @@ describe('Strongbox', function () {
it('should read its value', async function () {
const item = await box.createItem('SLUG test');
expect(item).to.eql({
id: '/some/dir/strongbox/slug-test',
id: path.resolve(DATA_DIR, 'strongbox', 'SLUG-test'),
name: 'SLUG test',
encoding: 'utf8',
value: 'foo bar',
container: '/some/dir/strongbox',
container: path.resolve(DATA_DIR, 'strongbox'),
});
});
});
@@ -129,7 +126,7 @@ describe('Strongbox', function () {
await item.write('boo bah');
expect(MockFs.writeFile).to.have.been.calledWith(
path.join(DATA_DIR, DEFAULT_SUFFIX, 'test'),
path.resolve(DATA_DIR, DEFAULT_SUFFIX, 'test'),
'boo bah',
'utf8'
);
@@ -148,7 +145,7 @@ describe('Strongbox', function () {
await box.createItem('test');
await expect(box.createItem('test')).to.be.rejectedWith(
Error,
'Item with id "/some/dir/strongbox/test" already exists'
`Item with id "${path.resolve(DATA_DIR, 'strongbox', 'test')}" already exists`
);
});
});
@@ -194,7 +191,7 @@ describe('Strongbox', function () {
it('should write the value to disk', async function () {
await box.createItemWithValue('test', 'value');
expect(MockFs.writeFile).to.have.been.calledWith(
path.join(DATA_DIR, DEFAULT_SUFFIX, 'test'),
path.resolve(DATA_DIR, DEFAULT_SUFFIX, 'test'),
'value'
);
});
+47
View File
@@ -3,6 +3,53 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [4.1.2](https://github.com/appium/appium/compare/@appium/support@4.1.1...@appium/support@4.1.2) (2023-07-03)
### Bug Fixes
* **support:** update dependency semver to v7.5.3 ([ed1856d](https://github.com/appium/appium/commit/ed1856d720c442842d5c3fe5f6caeccb8229b5f1))
## [4.1.1](https://github.com/appium/appium/compare/@appium/support@4.1.0...@appium/support@4.1.1) (2023-06-29)
**Note:** Version bump only for package @appium/support
## [4.1.0](https://github.com/appium/appium/compare/@appium/support@4.0.3...@appium/support@4.1.0) (2023-06-15)
### Features
* **support:** export a bunch of types that weren't properly exported ([88293f8](https://github.com/appium/appium/commit/88293f803933ecf7d258b02afd08d636b310a24b))
## [4.0.3](https://github.com/appium/appium/compare/@appium/support@4.0.2...@appium/support@4.0.3) (2023-06-14)
### Bug Fixes
* **support:** Require the optional sharp dep properly from mjpeg module ([#18769](https://github.com/appium/appium/issues/18769)) ([c18eb03](https://github.com/appium/appium/commit/c18eb03261a4c0f73ec71417825a387da1c59991))
## [4.0.2](https://github.com/appium/appium/compare/@appium/support@4.0.1...@appium/support@4.0.2) (2023-06-14)
### Bug Fixes
* **support:** update dependency @types/semver to v7.5.0 ([528dcfe](https://github.com/appium/appium/commit/528dcfe324e75e48471cdb694448c2a09ec2d828))
* **support:** update dependency semver to v7.5.1 ([5a55509](https://github.com/appium/appium/commit/5a555090c5322318f845415d743ee5c645fb94a0))
* **types:** update dependency type-fest to v3.11.0 ([19277f6](https://github.com/appium/appium/commit/19277f6e14a56e52b4669d633e148ad4a3da2c7a))
* **types:** update dependency type-fest to v3.11.1 ([56499eb](https://github.com/appium/appium/commit/56499eb997b551739bed628f057de7987674ea7f))
## [4.0.1](https://github.com/appium/appium/compare/@appium/support@4.0.0...@appium/support@4.0.1) (2023-05-19)
+21 -3
View File
@@ -1,14 +1,32 @@
import sharp from 'sharp';
let _sharp;
/**
* @returns {import('sharp')}
*/
export function requireSharp() {
if (!_sharp) {
try {
_sharp = require('sharp');
} catch (err) {
throw new Error(
`Cannot load the 'sharp' module needed for images processing. ` +
`Consider visiting https://sharp.pixelplumbing.com/install ` +
`for troubleshooting. Original error: ${err.message}`
);
}
}
return _sharp;
}
/**
* Crop the image by given rectangle (use base64 string as input and output)
*
* @param {string} base64Image The string with base64 encoded image.
* Supports all image formats natively supported by Sharp library.
* @param {sharp.Region} rect The selected region of image
* @param {import('sharp').Region} rect The selected region of image
* @return {Promise<string>} base64 encoded string of cropped image
*/
export async function cropBase64Image(base64Image, rect) {
const buf = await sharp(Buffer.from(base64Image, 'base64')).extract(rect).toBuffer();
const buf = await requireSharp()(Buffer.from(base64Image, 'base64')).extract(rect).toBuffer();
return buf.toString('base64');
}
@@ -57,3 +57,24 @@ export default {
env,
console,
};
export type {ConsoleOpts} from './console';
export type {ReadFn, WalkDirCallback} from './fs';
export type {
NetOptions,
DownloadOptions,
AuthCredentials,
NotHttpUploadOptions,
HttpUploadOptions,
} from './net';
export type {InstallPackageOpts, ExecOpts, NpmInstallReceipt} from './npm';
export type {Affixes, OpenedAffixes} from './tempdir';
export type {PluralizeOptions, EncodingOptions, LockFileOptions, NonEmptyString} from './util';
export type {
ExtractAllOptions,
ZipEntry,
ZipOptions,
ZipCompressionOptions,
ZipSourceOptions,
} from './zip';
export type {SecureValuePreprocessingRule} from './log-internal';
+2 -2
View File
@@ -1,7 +1,7 @@
import _ from 'lodash';
import log from './logger';
import B from 'bluebird';
import sharp from 'sharp';
import {requireSharp} from './image-util';
import {Writable} from 'stream';
import {requirePackage} from './node';
import axios from 'axios';
@@ -77,7 +77,7 @@ class MJpegStream extends Writable {
}
try {
return await sharp(lastChunk).png().toBuffer();
return await requireSharp()(lastChunk).png().toBuffer();
} catch (e) {
return null;
}
+9 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@appium/support",
"version": "4.0.1",
"version": "4.1.2",
"description": "Support libs used across appium packages",
"keywords": [
"automation",
@@ -42,7 +42,7 @@
},
"dependencies": {
"@appium/tsconfig": "^0.3.0",
"@appium/types": "^0.11.1",
"@appium/types": "^0.13.2",
"@colors/colors": "1.5.0",
"@types/archiver": "5.3.2",
"@types/base64-stream": "1.0.2",
@@ -55,11 +55,11 @@
"@types/ncp": "2.0.5",
"@types/npmlog": "4.1.4",
"@types/pluralize": "0.0.29",
"@types/semver": "7.3.13",
"@types/semver": "7.5.0",
"@types/shell-quote": "1.7.1",
"@types/supports-color": "8.1.1",
"@types/teen_process": "2.0.0",
"@types/uuid": "9.0.1",
"@types/uuid": "9.0.2",
"@types/which": "3.0.0",
"archiver": "5.3.1",
"axios": "1.4.0",
@@ -86,17 +86,19 @@
"read-pkg": "5.2.0",
"resolve-from": "5.0.0",
"sanitize-filename": "1.6.3",
"semver": "7.5.0",
"sharp": "0.32.1",
"semver": "7.5.3",
"shell-quote": "1.8.1",
"source-map-support": "0.5.21",
"supports-color": "8.1.1",
"teen_process": "2.0.2",
"type-fest": "3.10.0",
"type-fest": "3.11.1",
"uuid": "9.0.0",
"which": "3.0.1",
"yauzl": "2.10.0"
},
"optionalDependencies": {
"sharp": "0.32.1"
},
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0",
"npm": ">=8"
+6 -5
View File
@@ -9,6 +9,12 @@ describe('isExecutable()', function () {
});
describe('when the path exists', function () {
beforeEach(function () {
if (isWindows()) {
return this.skip();
}
});
describe('when the path is not executable', function () {
it('should return `false`', async function () {
await fs.isExecutable(__filename).should.eventually.be.false;
@@ -16,11 +22,6 @@ describe('isExecutable()', function () {
});
describe('when the path is executable', function () {
beforeEach(function () {
if (isWindows()) {
return this.skip();
}
});
it('should return `true`', async function () {
await fs.isExecutable('/bin/bash').should.eventually.be.true;
});

Some files were not shown because too many files have changed in this diff Show More