Skip to content

Commit 94e0477

Browse files
committed
first commit
0 parents  commit 94e0477

14 files changed

+451
-0
lines changed

Diff for: .eslintrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "appium"
3+
}

Diff for: .gitignore

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
.DS_Store
2+
.vscode
3+
4+
# Logs
5+
logs
6+
*.log
7+
npm-debug.log*
8+
package-lock.json*
9+
10+
# Runtime data
11+
pids
12+
*.pid
13+
*.seed
14+
15+
# Directory for instrumented libs generated by jscoverage/JSCover
16+
lib-cov
17+
18+
# Coverage directory used by tools like istanbul
19+
coverage
20+
21+
# nyc test coverage
22+
.nyc_output
23+
24+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
25+
.grunt
26+
27+
# node-waf configuration
28+
.lock-wscript
29+
30+
# Compiled binary addons (http://nodejs.org/api/addons.html)
31+
build/Release
32+
33+
# Dependency directories
34+
node_modules
35+
jspm_packages
36+
37+
# Optional npm cache directory
38+
.npm
39+
40+
# Optional REPL history
41+
.node_repl_history
42+
43+
# Ignore gulped JS
44+
build
45+
46+
# Pack output
47+
*.tgz

Diff for: README.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
Appium MiniProgram Driver
2+
----
3+
4+
Appium MiniProgram Driver is a test automation tool for WeiXin(a.k.a. WeChat) MiniProgram.
5+
6+
Appium MiniProgram Driver 是针对微信小程序的测试自动化工具。将微信官方自动化接口封装为 Appium Driver,借助 Appium 框架与 WebDriver API,我们可以使用多种编程语言编写小程序自动化脚本。
7+
8+
## 现状
9+
10+
试验性完成了:
11+
12+
- 截图
13+
- css selector 获取单个、多个 element
14+
- tap 指定 element
15+
- longPress 指定 element
16+
- pageScrollTo
17+
- scroll-view 的 scrollTo
18+
- swiper 的 swipeTo
19+
20+
## 障碍
21+
22+
#### 手势 API 不标准
23+
24+
只是简单尝试一下就遇到了 scroll-view 和 swiper 两个标签有自己的特殊 API。不过解决起来也还好,暂时不算坑。
25+
26+
#### 截图 API 有问题
27+
28+
miniprogram-automator 的 screenshot 本身就有问题,一开始好好的突然开始截图失败。
29+
30+
```
31+
> mpProgram.screenshot()
32+
Promise { <pending> }
33+
> (node:50420) UnhandledPromiseRejectionWarning: Error: fail to capture screenshot
34+
at Transport.Connection.onMessage (/PATH_TO_PROJECT/appium-mp-driver/node_modules/miniprogram-automator/out/Connection.js:1:986)
35+
at Transport.emit (events.js:315:20)
36+
at Transport.EventEmitter.emit (domain.js:505:15)
37+
at WebSocket.<anonymous> (/PATH_TO_PROJECT/appium-mp-driver/node_modules/miniprogram-automator/out/Transport.js:1:219)
38+
at WebSocket.onMessage (/PATH_TO_PROJECT/appium-mp-driver/node_modules/miniprogram-automator/node_modules/ws/lib/event-target.js:120:16)
39+
at WebSocket.emit (events.js:315:20)
40+
at WebSocket.EventEmitter.emit (domain.js:505:15)
41+
at Receiver.receiverOnMessage (/PATH_TO_PROJECT/appium-mp-driver/node_modules/miniprogram-automator/node_modules/ws/lib/websocket.js:789:20)
42+
at Receiver.emit (events.js:315:20)
43+
at Receiver.EventEmitter.emit (domain.js:505:15)
44+
(node:50420) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 14)
45+
```

Diff for: gulpfile.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
'use strict';
2+
3+
const gulp = require('gulp');
4+
const boilerplate = require('appium-gulp-plugins').boilerplate.use(gulp);
5+
6+
7+
boilerplate({
8+
build: 'appium-mp-driver',
9+
watchE2E: true,
10+
});

Diff for: index.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// transpile:main
2+
3+
import yargs from 'yargs';
4+
import { asyncify } from 'asyncbox';
5+
import * as server from './lib/server';
6+
import * as driver from './lib/driver';
7+
8+
const { startServer } = server;
9+
10+
const DEFAULT_HOST = 'localhost';
11+
const DEFAULT_PORT = 4723;
12+
13+
async function main () {
14+
let port = yargs.argv.port || DEFAULT_PORT;
15+
let host = yargs.argv.host || DEFAULT_HOST;
16+
return await startServer(port, host);
17+
}
18+
19+
if (require.main === module) {
20+
asyncify(main);
21+
}
22+
23+
const { MpDriver } = driver;
24+
25+
export default MpDriver;
26+
export { MpDriver };

Diff for: lib/commands/find.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/* eslint-disable */
2+
import { errors } from 'appium-base-driver';
3+
4+
let commands = {};
5+
6+
commands.findElOrEls = async function (strategy, selector, mult, context) {
7+
this.findedElements = this.findedElements || {};
8+
9+
let result;
10+
const doFind = async () => {
11+
if(strategy !== 'css selector') {
12+
throw new Error('Unsupported strategy for findElOrEls: ' + strategy);
13+
}
14+
15+
// 默认在当前页面上查找元素
16+
let context;
17+
if (typeof context === 'undefined' || !context) {
18+
context = await this.mpProgram.currentPage();
19+
} else {
20+
context = this.findedElements[context];
21+
}
22+
23+
if (mult) {
24+
result = await context.$$(selector);
25+
if (result) {
26+
result = result.map(el => {
27+
this.findedElements[el.id] = el;
28+
return {
29+
elementId: el.id
30+
};
31+
})
32+
return true;
33+
} else {
34+
return false;
35+
}
36+
} else {
37+
result = await context.$(selector);
38+
if (result) {
39+
this.findedElements[result.id] = result;
40+
result = {
41+
elementId: result.id
42+
};
43+
return true;
44+
} else {
45+
return false;
46+
}
47+
}
48+
}
49+
50+
try {
51+
await this.implicitWaitForCondition(doFind);
52+
} catch (err) {
53+
if (err.message && err.message.match(/Condition unmet/)) {
54+
// 没有找到元素
55+
throw new errors.NoSuchElementError(result);
56+
} else {
57+
throw err;
58+
}
59+
}
60+
return result;
61+
};
62+
63+
export default commands;

Diff for: lib/commands/gesture.js

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/* eslint-disable */
2+
import { errors } from 'appium-base-driver';
3+
4+
let commands = {};
5+
6+
commands.click = async function (elementId = null) {
7+
await this.tapEl(elementId);
8+
};
9+
10+
commands.tapEl = async function (elementId, longPress) {
11+
const page = await this.mpProgram.currentPage();
12+
const el = await page.$(elementId);
13+
if (el) {
14+
if (longPress) {
15+
await el.longPress();
16+
} else {
17+
await el.tap();
18+
}
19+
} else {
20+
throw new Error(`click failed, element ${elementId} not found`);
21+
}
22+
};
23+
24+
commands.tap = async function (gestures, longPress) {
25+
let elementId = gestures[0].options.element;
26+
await this.tapEl(elementId, longPress);
27+
};
28+
29+
commands.performTouch = async function (gestures) {
30+
if (gestures.length === 3) {
31+
32+
if (gestures[0].action === 'press' && gestures[1].action === 'moveTo' && gestures[2].action === 'release') {
33+
return await this.doSwipe(gestures);
34+
}
35+
36+
} else if (gestures.length === 2) {
37+
38+
if (gestures[0].action === 'press' && gestures[1].action === 'release') {
39+
return await this.tap(gestures, false);
40+
} else if (gestures[0].action === 'longPress' && gestures[1].action === 'release') {
41+
return await this.tap(gestures, true);
42+
}
43+
44+
} else if (gestures.length === 1) {
45+
if (gestures[0].action === 'tap') {
46+
47+
return await this.tap(gestures, false);
48+
49+
} else if (gestures[0].action === 'pageScrollTo') {
50+
51+
// 小程序 page 特有的 API
52+
await this.mpProgram.pageScrollTo(gestures[0].options.scrollTop)
53+
return true
54+
55+
} else if (gestures[0].action === 'scrollTo') {
56+
57+
// 小程序 scroll-view 特有的 API
58+
const opts = gestures[0].options;
59+
const page = await this.mpProgram.currentPage();
60+
const scrollView = await page.$(opts.element);
61+
if (!scrollView) {
62+
throw new errors.NoSuchElementError(scrollView);
63+
}
64+
if (scrollView.tagName !== 'scrollView') {
65+
throw new Error(`element ${opts.element} is not a scrollView`)
66+
}
67+
await scrollView.scrollTo(opts.x, opts.y);
68+
return true;
69+
70+
} else if (gestures[0].action === 'swipeTo') {
71+
72+
// 小程序 swiper 组件特有的 API,微信能搞点阳间的玩意不
73+
const opts = gestures[0].options;
74+
const page = await this.mpProgram.currentPage();
75+
const swiper = await page.$(opts.element);
76+
if (!swiper) {
77+
throw new errors.NoSuchElementError(swiper);
78+
}
79+
if (swiper.tagName !== 'swiper') {
80+
throw new Error(`element ${opts.element} is not a swiper`)
81+
}
82+
await swiper.swipeTo(opts.index);
83+
return true;
84+
85+
}
86+
}
87+
};
88+
89+
90+
export default commands;
91+

Diff for: lib/commands/index.js

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import screenShotCmds from './screenshot';
2+
import findCmds from './find';
3+
import gestureCmds from './gesture';
4+
5+
let commands = {};
6+
Object.assign(
7+
commands,
8+
// contextCommands,
9+
// settingsCmds,
10+
// timeoutCmds,
11+
findCmds,
12+
// generalCmds,
13+
screenShotCmds,
14+
// elementCmds,
15+
gestureCmds,
16+
// navigationCmds,
17+
// executeCmds,
18+
// mobileCmds,
19+
// add other command types here
20+
);
21+
22+
export default commands;

Diff for: lib/commands/screenshot.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let commands = {};
2+
3+
commands.getScreenshot = async function () {
4+
return await this.mpProgram.screenshot();
5+
};
6+
7+
export default commands;

Diff for: lib/desired-caps.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
const desiredCapConstraints = {
2+
platformName: {
3+
presence: true,
4+
isString: true,
5+
inclusionCaseInsensitive: [
6+
'iOS',
7+
'Android',
8+
'Simulator'
9+
]
10+
},
11+
automationName: {
12+
presence: true,
13+
isString: true,
14+
inclusionCaseInsensitive: [
15+
'MP',
16+
]
17+
},
18+
app: {
19+
isString: true
20+
},
21+
MpAppAddress: {
22+
isString: true
23+
},
24+
avd: {
25+
isString: true
26+
},
27+
udid: {
28+
isString: true
29+
}
30+
};
31+
32+
export default desiredCapConstraints;

0 commit comments

Comments
 (0)