Building Docs for Appium Extensions
Once you've built a driver or built a plugin 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
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 what behaviour will result on your particular platform. But this information is already more or less stored in your code, as the command implementation (and any docstrings or comments). Having this information in two places creates an opportunity for the docs to get out of sync with the 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.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.
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. But this guide will outline the basic approach.
Appium settled on MkDocs as a Markdown-based documentation site generator. It uses a Python toolchain (and not Node.js), but it turned out to be the best option for our purposes. You can adjust this, but by default Appium's utilities also assume that you'll be using the mkdocs-material theme/extension for MkDocs.
From here, building a basic docs site is as easy as collecting your Markdown files together and creating a sort of manifest file defining how you want them to be organized.
The other main piece is automatic documentation generation from your code files. Appium maintains a plugin for TypeDoc. 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 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" 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.
To take advantage of Appium's documentation utilities, you'll need to install:
Initializing an Extension for Building Docs
To prepare your extension for generating documentation, run the following command:
<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
- Create a
tsconfig.jsonif one does not already exist. This is necessary even if your extension is not written in TypeScript.
- Create a
typedoc.jsonwith the necessary configuration for TypeDoc.
- Create a
mkdocs.ymlwith the necessary configuration for MkDocs.
- Modify your
package.jsonto add a
typedoc.entryPointproperty 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.
The static properties
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.
Note: Plugin authors can skip this section.
Your driver may have a property
desiredCapConstraints. It should also follow the same pattern as
executeMethodMap above. For example:
(For extensions written in TypeScript, use
as const as before.)
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
doMyThing method implementation looks like this:
The documentation will show that the
doMyThing method accepts required parameters
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".
@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
executeMethodMap alone; it provides more information for your extension's users.
Note for TypeScript users: while the types will already be present, providing
@returnstags 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
void types will be output as
null (since that is a concept that translates well to multiple languages).
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
Appium provides type definitions for extension authors; these are available via Appium itself and the
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.
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
- For tags in addition to what TS natively recognizes, see the TypeDoc documentation
Likewise, refer to the MkDocs documentation for further information on how to customize your MkDocs output.
At this point, you can use the
appium-docs CLI tool. Run this tool with no arguments to get the
full help output and see all the available subcommands and parameters. Here are a few usage
# Generate reference and build the mkdocs site into the site dir 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 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! npx appium-docs build \ --deploy \ -b docs-site \ -m 'docs: auto-build docs for appium-xcuitest-driver@%s' \ --rebase