UNPKG

flasco_wda-driver

Version:

self wda-driver adjust from zqingr/wda-driver

402 lines (305 loc) 10.6 kB
# wda-driver Facebook WebDriverAgent的Node库 大部分功能都已经完成 ## 安装 1. 你需要自己安装和启动 WebDriverAgent 可以跟着官方文档安装: <https://github.com/facebook/WebDriverAgent> 你可以从xcode里面启动调试程序 也可以直接命令行启动 ``` xcodebuild -project WebDriverAgent.xcodeproj -scheme WebDriverAgentRunner -destination 'platform=iOS Simulator,name=iPhone 6' test ``` 2. 安装wda-driver ``` npm install --save wda-driver ``` ## TCP 有时候连接不上 (可选) 你可以直接用wda提供的网络地址,但是不太稳定,有时候会出现连接不上的问题 可以安装iproxy工具 <https://github.com/libimobiledevice/libusbmuxd> 开启非常简单 `iproxy <local port> <remote port> [udid]` 例如:```iproxy 8100 8100``` ## 引用 ```javascript const wda = require('wda-driver') ``` ## 如何使用 ### 建立一个客户端 ```javascript const wda = require('wda-driver') const c = new wda.Client('http://localhost:8100') // http://localhost:8100 是默认值,你可以直接这样 c = wda.Client() ``` ### 客户端操作 ```javascript // 查看客户端状态 console.log(await c.status()) // 点击home按钮 await c.home() // Hit healthcheck await c.healthcheck() // 获取 page source // 两个参数 // format (str): 'xml' 或 'json' // accessible (bool): 设置为true的时候,只返回json const source = await c.source() // 格式为 XML const source = await c.source(null, true) // 默认 false, 格式为 JSON ``` 截取屏幕,只支持png格式的图片,不传参数的话会直接返回base64的buffer ```javascript await c.screenshot('screen.png') ``` 打开 app ```javascript const s = await c.session('com.apple.Health') console.log(await s.orientation()) await s.close() ``` 获取当前app的信息 ```javascript await c.getActiveAppInfo(); ``` 一次请求带链式操作 ```javascript /** 在当前应用程序的范围内执行复杂的触摸操作。  触摸操作表示为具有预定义值和键集的字典列表。  每个字典必须包含“action”键,它是以下之一: - 'tap' 单次点击 - 'longPress' 长按 - 'press' 按下 - 'release' 松开 - 'moveTo' 移动虚拟按键 - 'wait' 链式操作中等待 - 'cancel' 取消链中的前一个动作 每个字典还可以包含“选项”键以及与相应操作相关的附加参数字典 对于'tap', 'longPress', 'press' and 'moveTo', 下面的属性是强制要求的: - 'x' x坐标 - 'y' y坐标 - 'element' 将要执行操作的相应元素实例 如果仅设置'element',则将使用该元素的点击坐标. 如果仅设置'x'和'y',则将这些视为绝对坐标. 如果同时设置'element'和'x'/'y',则这些将作为相对元素坐标. 它也是强制性的,'release'和'wait'动作前面至少有一个链项,它包含绝对坐标,如'tap','press'或'longPress'。 不允许空链. 以下附加选项可用于不同的操作: - 'tap': 'count' (按的次数; 默认是1) - 'longPress': 'duration' (长按的持续时间,默认是 500ms) - 'wait': 'ms' (等待的时长; 默认是 0ms) 上面列出来的可以在多指操作中生效,每个链式操作会对每个按点生效 */ const actions = [ { action: 'tap', options: { x: 300, y: 100 } }, { action: 'wait', options: { ms: 700 } }, { action: 'tap', options: { x: 300, y: 100 } } ]; await session.chainOperation(actions); ``` 判断当前设备是否锁屏 ```javascript await c.isLocked(); ``` 浏览器可以这样带网址的打开: ```javascript const s = await c.session('com.apple.mobilesafari', ['-u', 'https://www.google.com/ncr']) console.log(await s.orientation()) await s.close() ``` ### 对话操作 ```javascript // 当前的 bundleId 和 sessionId console.log(s.getId(), s.getBundleId()) // <PORTRAIT 或者 LANDSCAPE> console.log(await s.orientation()) // PORTRAIT // 改变方向 // LANDSCAPE | PORTRAIT | UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT |UIA_DEVICE_ORIENTATION_PORTRAIT_UPSIDEDOWN await s.orientation(orientation) // 让app进入后台5秒钟 await s.deactivate(5) // 5s // 获取高度和宽度 console.log(await s.getWindowSize()) // 返回例子: {'height': 736, 'width': 414} // 根据坐标点击屏幕 await s.tap(88, 200) // 双击屏幕 await s.doubleTap(200, 200) // 滑动相关的方法 await s.swipe(x1, y1, x2, y2, 0.5) // 0.5s await s.swipeLeft() await s.swipeRight() await s.swipeUp() await s.swipeDown() // 长按屏幕 await s.tapHold(x, y, 1.0) ``` ### 查找元素 ```javascript // 用获取的session的selector方法查找元素 const selector = s.selector({id: "URL"}) await selector.exists() // return true or false // 用id等查询元素 s.selector({id: 'URL'}) s.selector({name: 'URL'}) s.selector({text: "URL"}) // text是name的别名 s.selector({nameContains: 'UR'}) s.selector({label: 'Address'}) s.selector({labelContains: 'Addr'}) s.selector({name:'URL', index: 1}) // 查找第二个name为URL的元素 // 合并查询 // 属性可以选择一下元素 // :"className", "name", "label", "visible", "enabled" s.selector({className: 'Button', name: 'URL', visible: true, labelContains: "Addr"}) ``` 更多牛逼的查询方式 ```javascript s.selector({xpath: '//Button[@name="URL"]'}) s.selector({classChain: '**/Button[`name == "URL"`]'}) s.selector({predicate: 'name LIKE "UR*"'}) ``` ### 元素操作 (例如: `tap`, `scroll`, `set_text` 等...) 例子:查找一个元素然后点击他 ```javascript // 查找第一个text为Dashboard的元素 // function get() 非常重要.执行后才会返回一个元素对象 // 如果元素在10(默认)秒内查找到了, 就会返回一个元素对象 const e = await s.selector({text: 'Dashboard'}).get(10) // e 是一个元素对象 await e.tap() // 点击元素 ``` 如果元素存在点击元素 ```javascript await s.selector({text: 'Dashboard'}).clickExists() // 如果不存在会立刻返回 await s.selector({text: 'Dashboard'}).clickExists(5) // 等待5秒 ``` 其它的元素查找 ```javascript // 查找元素是否存在 console.log(await s.selector({text: 'Dashboard'}).exists()) // 查找所有匹配的元素 await s.selector({className: 'Other'}).findElements() // 查找第二个元素 await s.selector({className: 'Other', index: 2}).exists() // 查找子元素 await s.selector({text: 'Dashboard'}).child({className: 'Cell'}).exists() // 默认是10秒 // 但是你可以自己设置 s.setTimeout(50) // 元素操作 await e.tap() await e.click() // tap的别名 // 必须是系统默认键盘,搜狗测试的不行 await e.clearText() await e.setText("Hello world") await e.tapHold(2) // 长按2秒 await e.scroll() // 滚动到元素可见出 // 方向可以使 "up", "down", "left", "right" // 也可以指定移动的距离 await e.scroll('up', 100) // 发送文字 await e.setText("Hello WDA") // 一般用法 await e.setText("Hello WDA\n") // 发送回车 await e.setText("\b\b\b") // 删除三个字符 // 等待元素小时 await s({className: 'Other'}).waitGone(10) // Swipe TODO // s(className="Image").swipe("left") // 宋芳 s(className="Map").pinch(2, 1) // scale=2, speed=1 s(className="Map").pinch(0.1, -1) // scale=0.1, speed=-1 // 获取属性 (boolean) await e.getAccessible() await e.getDisplayed() await e.getEnabled() await e.getVisible() await e.getAccessibilityContainer() // 或许属性 (字符串) await e.getId() await e.getLabel() await e.getClassName() await e.getText() await e.getName() await e.getDisplayed() await e.getEnabled() await e.getValue() await e.getValue() // 返回元素的边界 const rect = await e.getBounds() // Rect { x: 0, y: 73, width: 375, height: 666 } rect.y // 73 ``` 警告框 ```javascript console.log(await s.alert().exists()) console.log(await s.alert().text()) console.log(await s.alert().text()) await s.alert().accept() // 其实点击的左侧按钮 await s.alert().dismiss() // 其实点击的第二个按钮 await s.alert().wait(5) // 等待5秒弹框出现 await s.alert().wait() // 默认等待20秒弹窗出现 await s.alert().buttons() // 例如返回 return: ["设置", "好"] await s.alert().click('好') ``` ## iOS Build-in Apps **苹果自带应用** | Name | Bundle ID | | ----------- | ---------------------------------------- | | iMovie | com.apple.iMovie | | Apple Store | com.apple.AppStore | | Weather | com.apple.weather | | 相机Camera | com.apple.camera | | iBooks | com.apple.iBooks | | Health | com.apple.Health | | Settings | com.apple.Preferences | | Watch | com.apple.Bridge | | Maps | com.apple.Maps | | Game Center | com.apple.gamecenter | | Wallet | com.apple.Passbook | | 电话 | com.apple.mobilephone | | 备忘录 | com.apple.mobilenotes | | 指南针 | com.apple.compass | | 浏览器 | com.apple.mobilesafari | | 日历 | com.apple.mobilecal | | 信息 | com.apple.MobileSMS | | 时钟 | com.apple.mobiletimer | | 照片 | com.apple.mobileslideshow | | 提醒事项 | com.apple.reminders | | Desktop | com.apple.springboard (Start this will cause your iPhone reboot) | **第三方应用 Thirdparty** | Name | Bundle ID | | ------ | --------------------- | | 腾讯QQ | com.tencent.mqq | | 微信 | com.tencent.xin | | 部落冲突 | com.supercell.magic | | 钉钉 | com.laiwang.DingTalk | | Skype | com.skype.tomskype | | Chrome | com.google.chrome.ios | 另一个获取你手机里面app列表的方法是用 `ideviceinstaller` 安装 `brew install ideviceinstaller` 显示列表 ```sh $ ideviceinstaller -l ``` ## Reference 这个项目移植的python项目 https://github.com/openatx/facebook-wda Source code - [Router](https://github.com/facebook/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBElementCommands.m#L62) - [Alert](https://github.com/facebook/WebDriverAgent/blob/master/WebDriverAgentLib/Commands/FBAlertViewCommands.m#L25) ## DESIGN [DESIGN](DESIGN.md) ## LICENSE [MIT](LICENSE)