Render commands markdown using YML (#9260)

* Render commands markdown using YML

* Made directory 'commands-yml' which contains:
  * .babelrc to transpile the scripts
  * parse.js which renders the markdown
  * template.md which is a handlebars template that the yml gets passed to
  * validator.js which validates the yml data
* The rendering works by translating the yml to js, validating the js and then passing the data to template.md (which uses handlebars)
* The directory `/commands-yml/commands` gets rendered and then copied to `/docs/en/commands` as markdown
* Uses babel-node to run the script
* Uses validatejs to validate the javascript
* Template.yml is just a sample

* Infers the platform and appium support range from driver
  * For example, if you put in 'xcuitest: true' it will infer that platform support is 9.3+ and Appium Version is 1.6.0
This commit is contained in:
Dan Graham
2017-09-21 14:12:43 -07:00
committed by GitHub
parent 949b7de5ad
commit 5b36e303a7
7 changed files with 407 additions and 116 deletions
+3
View File
@@ -0,0 +1,3 @@
{
"presets": ["env"]
}
+100
View File
@@ -0,0 +1,100 @@
---
name: 'Template'
description:
>
An indepth description
of what this command does
example_usage:
java:
|
MobileElement el = driver.findElementByAccessibilityId("SomeId");
el.click();
python:
|
el = self.driver.find_element_by_accessibility_id('SomeId')
el.click();
javascript_wd:
|
await driver.status();
javascript_wdio:
|
await driver.status();
ruby:
|
# Ruby Code here
php:
|
# PHP Code here
csharp:
|
// CSharp Code here
client_docs:
java: "https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/WebElement.html#click--"
python: "https://github.com/appium/python-client"
javascript_wdio: "http://webdriver.io/index.html"
javascript_wd: "https://github.com/admc/wd/releases"
ruby: "https://github.com/appium/ruby_lib/releases/latest"
php: "https://github.com/appium/php-client/releases/latest-"
csharp: "https://github.com/appium/appium" # TODO
# Driver support by platform
driver_support:
ios:
xcuitest:
platform_min: '1.2'
platform_max: '9.3'
appium_min: '1.7'
appium_max: '1.9'
driver_min: '1.1'
driver_max: '9.9'
uiautomation:
platform_min: '1.0'
android:
uiautomator2:
platform_min: '8.0'
uiautomator: true
mac:
mac:
platform_min: '?' # TODO
client_support:
java:
min: 1.2
max: 2.1
python:
min: 2
ruby: true # If true, means it supports all
php: false # If false(y), means it is not supported
# Information about the HTTP endpoints
endpoint:
url: /wd/hub/session/:session_id/element/:element_id/click
method: 'POST'
url_parameters:
- name: session_id
description: ID of the session
- name: element_id
description: ID of the element
json_parameters:
- name: item
type: Object
description: Description of Item
- name: item.1 # Use dot notation for nested items
type: String # Type is optional
description: Item One description
response:
- name: build.version # Use dot notation for nested JSON parameters
type: string # JS type
description: A generic release label (i.e. "2.0rc3")
# Links to specifications. Should link to at least one specification
specifications:
w3c: https://www.w3.org/TR/webdriver/#element-click
jsonwp: https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#sessionsessionidelementidclick
# Other relevant links
links:
- name: Google
url: http://www.google.com
+117
View File
@@ -0,0 +1,117 @@
/* eslint-disable no-console */
import path from 'path';
import yaml from 'yaml-js';
import { fs } from 'appium-support';
import validate from 'validate.js';
import Handlebars from 'handlebars';
import replaceExt from 'replace-ext';
import _ from 'lodash';
import { asyncify } from 'asyncbox';
import validator from './validator';
// What range of platforms do the driver's support
const platformRanges = {
xcuitest: ['9.3'],
uiautomation: ['8.0', '9.3'],
uiautomator2: ['?'],
uiautomator: ['4.2'],
windows: ['10'],
mac: ['?'], // TODO
};
// When was the driver supported in Appium?
const appiumRanges = {
xcuitest: ['1.6.0'],
uiautomator2: ['1.6.0'],
espresso: ['?'],
windows: ['1.6.0'],
mac: ['1.6.4'],
};
// Create Handlebars helper that shows a version range
Handlebars.registerHelper('versions', (object, name, driverName) => {
if (!object) {
return 'None';
}
if (!_.isObject(object)) {
object = {};
}
let min = object[name ? `${name}_min` : 'min'];
let max = object[name ? `${name}_max` : 'max'];
if (!min) {
if (name === 'appium' && appiumRanges[driverName]) {
min = appiumRanges[driverName][0];
} else if (name === 'platform' && platformRanges[driverName]) {
min = platformRanges[driverName][0];
}
}
if (!max) {
if (name === 'appium' && appiumRanges[driverName]) {
max = appiumRanges[driverName][1];
} else if (name === 'platform' && platformRanges[driverName]) {
max = platformRanges[driverName][1];
}
}
if (!min && !max) {
return 'All';
} else if (!max) {
return `${min}+`;
} else if (!min) {
return `<= ${max}`;
}
return `${min} to ${max}`;
});
Handlebars.registerHelper('hyphenate', (str) => str.replace('_', '-'));
Handlebars.registerHelper('uppercase', (str) => str.toUpperCase());
Handlebars.registerHelper('capitalize', (driverName) => {
switch (driverName.toLowerCase()) {
case 'xcuitest':
return 'XCUITest';
case 'uiautomation':
return 'UIAutomation';
case 'uiautomator2':
return 'UiAutomator2';
case 'uiautomator':
return 'UiAutomator';
default:
return driverName.length === 0 ? driverName : driverName[0].toUpperCase() + driverName.substr(1);
}
});
async function main () {
const commands = path.resolve(__dirname, 'commands/**/*.yml');
console.log('Traversing YML files', commands);
for (let filename of await fs.glob(commands)) {
console.log('Rendering file:', filename, path.relative(__dirname, filename), path.extname(filename));
// Translate the YML specs to JS
const inputYML = await fs.readFile(filename, 'utf8');
const inputJS = yaml.load(inputYML);
const validationErrors = validate(inputJS, validator);
if (validationErrors) {
throw new Error(validationErrors);
}
// Pass the inputJS into our Handlebars template
const template = Handlebars.compile(await fs.readFile(path.resolve(__dirname, 'template.md'), 'utf8'), {noEscape: true, strict: true});
const markdown = template(inputJS);
// Write the markdown to its right place
const markdownPath = replaceExt(path.relative(__dirname, filename), '.md');
const outfile = path.resolve(__dirname, '..', 'docs', 'en', markdownPath);
console.log('Writing file to:', outfile);
await fs.writeFile(outfile, markdown, 'utf8');
}
}
asyncify(main);
/* eslint-enable */
+126
View File
@@ -0,0 +1,126 @@
# {{name}}
{{description}}
## Example Usage
```java
// Java
{{example_usage.java}}
```
```python
# Python
{{example_usage.python}}
```
```javascript
// Javascript
// webdriver.io example
{{example_usage.javascript_wdio}}
// wd example
{{example_usage.javascript_wd}}
```
```ruby
# Ruby example
{{example_usage.ruby}}
```
```php
# PHP example
{{example_usage.php}}
```
```csharp
// C# example
{{example_usage.csharp}}
```
## Description
{{description}}
## Client Docs
* [Java]({{client_docs.java}})
* [Python]({{client_docs.python}})
* [Javascript (WebdriverIO)]({{client_docs.javascript_wdio}})
* [Javascript (WD)]({{client_docs.javascript_wd}})
* [Ruby]({{client_docs.ruby}})
* [PHP]({{client_docs.php}})
* [C#]({{client_docs.csharp}})
## Support
### Appium Server
|Platform|Driver|Platform Versions|Appium Version|Driver Version|
|--------|----------------|------|--------------|--------------|
{{#each driver_support.ios}}
| {{#if @first}}iOS{{/if}} | [{{capitalize @key}}](/docs/en/drivers/ios-{{@key}}.md) | {{versions this "platform" @key}} | {{versions this "appium" @key}} | {{versions this "driver" @key}} |
{{/each}}
{{#each driver_support.android}}
| {{#if @first}}Android{{/if}} | [{{capitalize @key}}](/docs/en/drivers/android-{{@key}}.md) | {{versions this "platform" @key}} | {{versions this "appium" @key}} | {{versions this "driver" @key}} |
{{/each}}
{{#each driver_support.mac}}
| {{#if @first}}Mac{{/if}} | [{{capitalize @key}}](/docs/en/drivers/{{@key}}.md) | {{versions this "platform" @key}} | {{versions this "appium" @key}} | {{versions this "driver" @key}} |
{{/each}}
{{#each driver_support.windows}}
| {{#if @first}}Windows{{/if}} | [{{capitalize @key}}](/docs/en/drivers/{{@key}}.md) | {{versions this "platform" @key}} | {{versions this "appium" @key}} | {{versions this "driver" @key}} |
{{/each}}
### Appium Clients
|Language|Support|
|--------|-------|
|[Java](https://github.com/appium/java-client/releases/latest)| {{versions client_support.java}} |
|[Python](https://github.com/appium/python-client/releases/latest)| {{versions client_support.python}} |
|[Javascript (WebdriverIO)](http://webdriver.io/index.html)| {{versions client_support.javascript_wdio}} |
|[Javascript (WD)](https://github.com/admc/wd/releases/latest)| {{versions client_support.javascript_wd}} |
|[Ruby](https://github.com/appium/ruby_lib/releases/latest)| {{versions client_support.ruby}} |
|[PHP](https://github.com/appium/php-client/releases/latest)| {{versions client_support.php}} |
|[C#](https://github.com/appium/appium-dotnet-driver/releases/tag/1.2.0.8)| {{versions client_support.csharp}} |
## HTTP API Specifications
### Endpoint
`{{uppercase endpoint.method}} {{endpoint.url}}`
### URL Parameters
|name|description|
|----|-----------|
{{#each endpoint.url_parameters}}
|{{this.name}}|{{this.description}}|
{{/each}}
### JSON Parameters
{{#if endpoint.json_parameters}}
|name|type|description|
|----|-----------|
{{#each endpoint.json_parameters}}
| {{this.name}} | {{this.type}} | {{this.description}} |
{{/each}}
{{else}}
None
{{/if}}
### Response
|name|type|description|
|----|----|-----------|
{{#each endpoint.response}}
| {{this.name}} | {{this.type}} | {{this.description}} |
{{/each}}
## See Also
{{#if specifications.w3c}}
* [W3C Specification]({{specifications.w3c}})
{{/if}}
{{#if specifications.jsonwp}}
* [JSONWP Specification]({{specifications.jsonwp}})
{{/if}}
{{#each links}}
* [{{this.name}}]({{this.url}})
{{/each}}
+52
View File
@@ -0,0 +1,52 @@
import validate from 'validate.js';
import _ from 'lodash';
validate.validators.array = function (value, options, key, attributes) {
if (attributes[key] && !validate.isArray(attributes[key])) {
return `must be an array`;
}
};
validate.validators.hasAttributes = function (value, options) {
if (!value) {
return;
}
if (_.isObject(value) && !_.isArray(value)) {
value = [value];
}
for (let item of value) {
for (let option of options) {
if (_.isUndefined(item[option])) {
return `must have attributes: ${options}`;
}
}
}
};
export default {
'name': {presence: true},
'example_usage': {presence: true},
'example_usage.java': {presence: true},
'example_usage.javascript_wdio': {presence: true},
'example_usage.javascript_wd': {presence: true},
'example_usage.ruby': {presence: true},
'example_usage.csharp': {presence: true},
'example_usage.php': {presence: true},
'description': {presence: true},
'client_docs.java': {presence: true, url: true},
'client_docs.javascript_wdio': {presence: true, url: true},
'client_docs.javascript_wd': {presence: true, url: true},
'client_docs.ruby': {presence: true, url: true},
'client_docs.csharp': {presence: true, url: true},
'client_docs.php': {presence: true, url: true},
'endpoint': {presence: true},
'driver_support': {presence: true},
'endpoint.url': {presence: true},
'endpoint.url_parameters': { 'array': true, hasAttributes: ['name', 'description'] },
'endpoint.json_parameters': { 'array': true, hasAttributes: ['name', 'description'] },
'endpoint.response': {presence: true, hasAttributes: ['name', 'type', 'description'] },
'specifications': {presence: true},
'links': { 'array': true, hasAttributes: ['name', 'url'] },
};
-114
View File
@@ -1,114 +0,0 @@
# Template <Name of Command>
A brief description of the command.
## Example Usage
```java
// Java example
```
```python
# Python example
```
```javascript
// Javascript
// webdriver.io example
driver.status();
// wd example
await driver.status();
```
```ruby
# Ruby example
```
```php
# PHP example
```
## Description
An in-depth description of the command. If this is a W3C command, you can copy and paste the W3C description here and make any amendments that would apply to Appium
## Code samples
Optional section. If the command is straightforward, no need to add samples, the example usage above is good enough. This is for important/complicated things like session creation, and touch actions.
## Client Docs
* [Java](http://seleniumhq.github.io/selenium/docs/api/java/index.html)
* [Python](http://selenium-python.readthedocs.io/api.html#selenium.webdriver.common.action_chains.ActionChains.click)
* [Javascript (WebdriverIO)](http://webdriver.io/api/protocol/status.html)
* [Javascript (WD)](https://github.com/admc/wd/blob/master/lib/commands.js#L1438)
* [Ruby](http://www.rubydoc.info/gems/selenium-webdriver/0.0.28/Selenium/WebDriver/Element#click-instance_method)
* TODO: PHP
## Support
### Appium Server
|Platform|Driver|Platform Versions|Appium Version|Driver Version|
|--------|----------------|------|--------------|--------------|
|iOS|[XCUITest](/docs/en/drivers/ios-xcuitest.md)| 9.3+ | 1.6.0+ | All |
| |[UIAutomation](/docs/en/drivers/ios-xcuitest.md)| 8.0 to 9.3 | All | All |
|Android|[UiAutomator2](/docs/en/drivers/android-uiautomator2.md)| ? | 1.6.0+ | All|
| |[UiAutomator](/docs/en/drivers/android-uiautomator.md)| 4.2+ | All | All |
| |[Espresso](/docs/en/drivers/android-espresso.md)| TBD | TBD |TBD
|Windows|[Windows](/docs/en/drivers/windows.md)| 10+ | 1.6.0+ |All|
|Mac|[Mac](/docs/en/drivers/mac.md)|?| 1.6.4+ |All|
|Mac|None||||
### Appium Clients
|Language|Support|
|--------|-------|
|[Java](https://github.com/appium/java-client/releases/latest)|All|
|[Python](https://github.com/appium/python-client)|All|
|[Javascript (WebdriverIO)](http://webdriver.io/index.html)|1.12.3|
|[Javascript (WD)](https://github.com/admc/wd/releases)|None|
|[Ruby](https://github.com/appium/ruby_lib/releases/latest)|All|
|[PHP](https://github.com/appium/php-client/releases/latest)|All|
## HTTP API Specifications
### Endpoint
`POST /wd/hub/path/to/endpoint`
### URL Parameters
|name|description|
|----|-----------|
|field_name_1|Description of field_name_1|
|field_name_2|Description of field_name_2|
or
(none)
### JSON Parameters
|name|description|
|----|-----------|
|field_name_1|Description of field_name_1|
|field_name_2|Description of field_name_2|
or
(none)
### Response
|name|type|description|
|----|----|-----------|
|field_name_1|number|Description of field_name_1|
|field_name_2|number|Description of field_name_2|
|object.key1|string|Description of key1|
|object.key2|string|Description of key2|
## Specifications
## See Also
* [W3C Specification](https://www.w3.org/TR/webdriver/#status) Link to the w3c spec if there is one
* [JSONWP Specification](https://github.com/SeleniumHQ/selenium/wiki/JsonWireProtocol#sessionsessionidelementidclick)
+9 -2
View File
@@ -70,7 +70,8 @@
"precommit-msg": "echo 'Pre-commit checks...' && exit 0",
"precommit-test": "REPORTER=dot gulp once",
"lint": "gulp eslint",
"coverage": "gulp coveralls"
"coverage": "gulp coveralls",
"generate-docs": "babel-node ./commands-yml/parse.js"
},
"pre-commit": [
"precommit-msg",
@@ -78,7 +79,9 @@
],
"devDependencies": {
"appium-gulp-plugins": "2.x",
"babel-cli": "^6.26.0",
"babel-eslint": "7.x",
"babel-preset-env": "^1.6.0",
"chai": "3.x",
"chai-as-promised": "5.x",
"eslint": "3.x",
@@ -88,10 +91,14 @@
"eslint-plugin-mocha": "4.x",
"eslint-plugin-promise": "3.x",
"gulp": "3.x",
"handlebars": "^4.0.10",
"mocha": "2.x",
"pre-commit": "1.x",
"replace-ext": "^1.0.0",
"sinon": "1.x",
"wd": "1.x"
"validate.js": "^0.11.1",
"wd": "1.x",
"yaml-js": "^0.2.0"
},
"optionalDependencies": {
"fsevents": "1.x",