diff --git a/docs/auto/api.md b/docs/auto/api.md new file mode 100644 index 0000000000000000000000000000000000000000..2b8d81466d16bcb246400120fef0eea1c975306b --- /dev/null +++ b/docs/auto/api.md @@ -0,0 +1,833 @@ +### uni-app自动化 API + + +### Page + +Page 模块提供了控制页面的方法。 + +#### 属性 + +page.path + +页面路径。 + +`page.path: string` + +page.query + +页面参数。 +`page.query: Object` + +#### 方法 + +page.$ + +获取页面元素。 + +`page.$(selector: string): Promise` + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|selector|string|是|-|选择器| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-desc') + console.log(element.tagName) // -> 'view' +``` + + +page.$$ + +获取页面元素数组。 + +`page.$$(selector: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|selector|string|是|-|选择器| + +该方法跟 $ 一样均无法选择自定义组件内的元素,请使用 element.$。 + +示例代码: +``` + const page = await program.currentPage() + const elements = await page.$$('.list-text') + console.log(elements.length) +``` + +page.waitFor + +等待直到指定条件成立。 + +`page.waitFor(condition: string | number | Function): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|condition|string number Function|是|-|等待条件| + + +如果条件是 string 类型,那么该参数会被当成选择器,当该选择器选中元素个数不为零时,结束等待。 + +如果条件是 number 类型,那么该参数会被当成超时时长,当经过指定时间后,结束等待。 + +如果条件是 Function 类型,那么该参数会被当成断言函数,当该函数返回真值时,结束等待。 + + +示例代码: +``` + const page = await program.currentPage() + await page.waitFor(5000) // 等待 5 秒 + await page.waitFor('picker') // 等待页面中出现 picker 元素 + await page.waitFor(async () => { + return (await page.$$('picker')).length > 5 + }) // 等待页面中 picker 元素数量大于 5 +``` + +page.data + +获取页面渲染数据。 + +`page.data(path?: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|path|string|否|-|数据路径| + +示例代码: +``` + const page = await program.currentPage() + console.log(await page.data('list')) +``` + + +page.setData + +设置页面渲染数据。 + +`page.setData(data: Object): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|data|Object|是|-|要改变的数据| + +示例代码: +``` + const page = await program.currentPage() + await page.setData({ + text: 'changed data' + }) +``` + + +page.size + +获取页面大小。 + +`page.size(): Promise` + + +返回值说明 + +|字段|类型|说明| +|:-:|:-:|:-:| +|width|number|页面可滚动宽度| +|height|number|页面可滚动高度| + +示例代码: +``` + const page = await program.currentPage() + const { width, height } = await page.size() + console.log(width, height) +``` + +page.scrollTop + +获取页面滚动位置。 + +`page.scrollTop(): Promise` + + +示例代码: +``` + const page = await program.currentPage() + await program.pageScrollTo(20) + console.log(await page.scrollTop()) +``` + +page.callMethod + +调用页面指定方法。 + +`page.callMethod(method: string, ...args: any[]): Promise` + + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|method|string|是|-|需要调用的方法名| +|...args|array|否|-|方法参数| + + +示例代码: +``` + const page = await program.currentPage() + await page.callMethod('onShareAppMessage') +``` + + + +### Element +Element 模块提供了控制页面元素的方法。 + +#### 属性 + +element.tagName + +标签名,小写。 + +`element.tagName: string` + + +#### 方法 + +element.$ + +在元素范围内获取元素。 + +`element.$(selector: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|selector|string|是|-|选择器| + +示例代码: +``` + const page = await program.currentPage() + let element = await page.$('.index-hd') + element = await element.$('.index-desc') + console.log(await element.text()) +``` + + +element.$$ + +在元素范围内获取元素数组。 + +`element.$$(selector: string): Promise` + + +参数说明 + +|字段|类型|必填 默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|selector|string|是|-|选择器| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-bd') + const elements = await element.$$('.list-text') + console.log(await elements[0].text()) +``` + + +element.size + +获取元素大小。 + +`element.size(): Promise` + + +返回值说明 + +|字段|类型|说明| +|:-:|:-:|:-:| +|width|number|元素宽度| +|height|number|元素高度| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-bd') + const { width, height } = await element.size() + console.log(width, height) +``` + + +element.offset + +获取元素绝对位置。 + +`element.offset(): Promise` + + +返回值说明 + +|字段|类型|说明| +|:-:|:-:|:-:| +|left|number|左上角x坐标,单位:px| +|top|number|左上角y坐标,单位:px| + +坐标信息以页面左上角为原点。 + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-bd') + const { left top } = await element.offset() + console.log(left, top) +``` + + +element.text + +获取元素文本。 + +`element.text(): Promise` + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-desc') + console.log(await element.text()) +``` + + +element.attribute + +获取元素特性。 + +`element.attribute(name: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|name|string|是|-|特性名| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.logo') + console.log(await element.attribute('src')) // -> 'static/logo.png' +``` + + +element.property + +获取元素属性。 + +`element.property(name: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|name|string|是|-|属性名| + +`element.property` 与 `element.attribute` 主要区别如下: + +`element.attribute` 获取的是标签上的值,因此它的返回类型一定是字符串,element.property 则不一定。 + +`element.attribute` 可以获取到 class 和 id 之类的值,element.property 不行。 + +`element.property` 可以获取到文档里对应组件列举的大部分属性值,比如表单 input 等组件的 value 值。 + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('input') + console.log(await element.property('value')) +``` + + +element.html + +获取元素 HTML。 + +`element.html(): Promise` + + +element.outerHTML + +同 html,只是会获取到元素本身。 + +`element.outerHTML(): Promise` + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-desc') + console.log(await element.html()) + console.log(await element.outerHtml()) +``` + + +element.value + +获取元素值。 + +`element.value(): Promise` + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.input') + console.log(await element.value()) +``` + + +element.style + +获取元素样式值。 + +`element.style(name: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|name|string|是|-|样式名| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.index-desc') + console.log(await element.style('color')) // -> 'rgb(136, 136, 136)' +``` + + +element.tap + +点击元素。 + +`element.tap(): Promise` + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('.list-item-hd') + await element.tap() +``` + + +element.longpress + +长按元素。 + +`element.longpress(): Promise` + + + +element.touchstart + +手指开始触摸元素。 + +`element.touchstart(options: Object): Promise` + + +options 字段定义如下: + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|touches|array|是|-|触摸事件,当前停留在屏幕中的触摸点信息的数组| +|changedTouches|array|是|-|触摸事件,当前变化的触摸点信息的数组| + + +element.touchmove + +手指触摸元素后移动。 + +`element.touchmove(options: Object): Promise` + +options 字段同 touchstart。 + + +element.touchend + +手指结束触摸元素。 + +`element.touchend(options: Object): Promise` + +options 字段同 touchstart。 + + +``` + const page = await program.currentPage() + const element = await page.$('.touch') + await element.touchstart({ + touches: [ + { + identifier: 1, + pageX: 500, + pageY: 500 + } + ], + changedTouches: [ + { + identifier: 1, + pageX: 500, + pageY: 500 + } + ] + }) + await element.touchend({ + touches: [], + changedTouches: [ + { + identifier: 1, + pageX: 500, + pageY: 500 + } + ] + }) +``` + + +element.trigger + +触发元素事件。 + +`element.trigger(type: string, detail?: Object): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|type|string|是|-|触发事件类型| +|detail|Object|否|-|触发事件时传递的 detail 值| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('picker') + await element.trigger('change', { value: 1 }) +``` +该方法无法改变组件状态,仅触发响应方法,也无法触发用户操作事件,即 `tap`,`longpress` 等事件,请使用对应的其它方法调用。 + + +element.input + +输入文本,仅 input、textarea 组件可以使用。 + +`element.input(value: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|value|string|是|-|需要输入的文本| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('input') + await element.input('test') +element.callMethod +``` + +调用组件实例指定方法,仅自定义组件可以使用。 + +`element.callMethod(method: string, ...args: any[]): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|method|string|是|-|需要调用的方法名| +|...args|array|否|-|方法参数| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('set-tab-bar') + await element.callMethod('navigateBack') +``` + + +element.data + +获取组件实例渲染数据,仅自定义组件可以使用。 + +`element.data(path?: string): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|path|string|否|-|数据路径| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('set-tab-bar') + console.log(await element.data('hasSetTabBarBadge')) +``` + + +element.setData + +设置组件实例渲染数据,仅自定义组件可以使用。 + +`element.setData(data: Object): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|data|Object|是|-|要改变的数据| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('set-tab-bar') + await page.setData({ + hasSetTabBarBadge: true + }) +``` + + +element.callContextMethod + +调用上下文 Context 对象方法,仅 video 组件可以使用。 + +`element.callContextMethod(method: string, ...args: any[]): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|method|string|是|-|需要调用的方法名| +|...args|array|否|-|方法参数| + +video 组件必须设置了 id 才能使用。 + +``` + const page = await program.currentPage() + const element = await page.$('video') + await element.callContextMethod('play') +``` + + +element.scrollWidth + +获取滚动宽度,仅 scroll-view 组件可以使用。 + +`element.scrollWidth(): Promise` + + +element.scrollHeight + +获取滚动高度,仅 scroll-view 组件可以使用。 + +`element.scrollHeight(): Promise` + + +element.scrollTo + +滚动到指定位置,仅 scroll-view 组件可以使用。 + +`element.scrollTo(x: number, y: number): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|x|number|是|-|横向滚动位置| +|y|number|是|-|纵向滚动位置| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('scroll-view') + const y = (await element.scrollHeight()) - 50 + await element.scrollTo(0, y) +``` + + +element.swipeTo + +滑动到指定滑块,仅 swiper 组件可以使用。 + +`element.swipeTo(index: number): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|index|number|是|-|目标滑块的 index| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('swiper') + await element.swipeTo(2) +``` + + +element.moveTo + +移动视图容器,仅 movable-view 组件可以使用。 + +`element.moveTo(x: number, y: number): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|x|number|是|-|x 轴方向的偏移| +|y|number|是|-|y 轴方向的偏移| + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('movable-view') + await element.moveTo(40, 40) +element.slideTo +``` + + +滑动到指定数值,仅 slider 组件可以使用。 + +`element.slideTo(value: number): Promise` + + +参数说明 + +|字段|类型|必填|默认值|说明| +|:-:|:-:|:-:|:-:|:-:| +|value|number|是|-|要设置的值| + + +示例代码: +``` + const page = await program.currentPage() + const element = await page.$('slider') + await element.slideTo(10) +``` + + + +**平台差异** + +### program(全局对象) + +|方法|APP-NVUE|APP-VUE|H5|微信小程序|百度小程序|说明| +|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +|pageStack |√ |√ |√ |√ |√ |获取小程序页面堆栈| +|navigateTo |√ |√ |√ |√ |√ |保留当前页面,跳转到应用内的某个页面,同`uni.navigateTo`| +|redirectTo |√ |√ |√ |√ |√ |关闭当前页面,跳转到应用内的某个页面,同`uni.redirectTo`| +|navigateBack |√ |√ |√ |√ |√ |关闭当前页面,返回上一页面,同`uni.navigateBack`| +|reLaunch |√ |√ |√ |√ |√ |关闭所有页面,打开到应用内的某个页面,同`uni.reLaunch`| +|switchTab |√ |√ |√ |√ |√ |跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面,同`uni.switchTab`| +|currentPage |√ |√ |√ |√ |√ |获取当前页面| +|systemInfo |√ |√ |√ |√ |√ |获取系统信息,同`uni.getSystemInfo`| +|pageScrollTo |x |√ |√ |√ |√ |将页面滚动到目标位置,同`uni.pageScrollTo`| +|callUniMethod |√ |√ |√ |√ |√ |调用 uni 对象上的指定方法| +|evaluate |x |x |x |√ |x |注入代码片段并返回执行结果| +|screenshot |√ |√ |√ |√ |x |对当前页面截图,目前只有开发者工具模拟器支持,客户端无法使用| +|exposeFunction |x |x |x |√ |x |在全局暴露方法,供小程序侧调用测试脚本中的方法| +|mockUniMethod |x |x |x |x |x |覆盖 uni 对象上指定方法的调用结果| +|restoreUniMethod |x |x |x |x |x |重置 uni 指定方法,消除 mockUniMethod 调用的影响| +|testAccounts |x |x |x |√ |x |获取多账号调试中已添加的用户列表| + +### Page +|属性 |APP-NVUE |APP-VUE|H5 |微信小程序 |百度小程序 |说明 | +|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +|path |√ |√ |√ |√ |√ |页面路径 | +|query |√ |√ |√ |√ |√ |页面参数 | + +|方法 |APP-NVUE |APP-VUE |H5 |微信小程序 |百度小程序 |说明| +|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +|$ |√ |√ |√ |√ |√ |获取页面元素 | +|$$ |√ |√ |√ |√ |√ |获取页面元素数组 | +|waitFor |√ |√ |√ |√ |√ |等待直到指定条件成立 | +|data |√ |√ |√ |√ |√ |获取页面渲染数据 | +|setData |√ |√ |√ |√ |√ |设置页面渲染数据 | +|size |√ |√ |√ |√ |√ |获取页面大小(width,height) | +|scrollTop |√ |√ |√ |√ |√ |获取页面滚动位置 | +|callMethod |√ |√ |√ |√ |√ |调用页面指定方法 | + +### Element +|属性 |APP-NVUE |APP-VUE|H5 |微信小程序 |百度小程序 |说明 | +|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +|tagName|√ |√ |√ |√ |√ |标签名,小写 | + +|方法 |APP-NVUE |APP-VUE|H5 |微信小程序 |百度小程序 |说明| +|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +|$ |√ |√ |√ |√ |√ |在元素范围内获取元素 | +|$$ |√ |√ |√ |√ |√ |在元素范围内获取元素数组 | +|size |√ |√ |√ |√ |√ |获取元素大小(width,height) | +|offset |√ |√ |√ |√ |√ |获取元素绝对位置(left,top) | +|text |√ |√ |√ |√ |√ |获取元素文本 | +|attribute |√ |√ |√ |√ |√ |获取元素特性 | +|style |√ |√ |√ |√ |√ |获取元素样式值 | +|tap |√ |√ |√ |√ |√ |点击元素 | +|value |√ |√ |√ |√ |√ |获取元素值 | +|callMethod |√ |√ |√ |√ |√ |调用组件实例指定方法,仅自定义组件可以使用 | +|html |√ |√ |√ |√ |√ |获取元素 HTML | +|outerHtml |√ |√ |√ |√ |√ |同 html,只是会获取到元素本身 | +|data |√ |√ |√ |√ |√ |获取组件实例渲染数据,仅自定义组件可以使用 | +|setData |√ |√ |√ |√ |√ |设置组件实例渲染数据,仅自定义组件可以使用 | +|property |√ |√ |√ |√ |x |获取元素属性 | +|touchstart |√ |√ |√ |√ |x |手指开始触摸元素 | +|touchmove |√ |√ |√ |√ |x |手指触摸元素后移动 | +|touchend |√ |√ |√ |√ |x |手指结束触摸元素 | +|longpress |√ |√ |√ |√ |x |获取元素文本 | +|trigger |√ |√ |√ |√ |x |触发元素事件 | +|input |√ |√ |√ |√ |x |输入文本,仅 input、textarea 组件可以使用 | +|callContextMethod |x |x |x |√ |x |调用上下文 Context 对象方法,仅 video 组件可以使用 | +|scrollWidth |x |√ |√ |√ |x |获取滚动宽度,仅 scroll-view 组件可以使用 | +|scrollHeight |x |√ |√ |√ |x |获取滚动高度,仅 scroll-view 组件可以使用 | +|scrollTo |x |√ |√ |√ |x |滚动到指定位置,仅 scroll-view 组件可以使用 | +|swipeTo |√ |√ |√ |√ |x |滑动到指定滑块,仅 swiper 组件可以使用 | +|moveTo |√ |√ |√ |√ |x |移动视图容器,仅 movable-view 组件可以使用 | +|slideTo |√ |√ |√ |√ |x |滑动到指定数值,仅 slider 组件可以使用 | + +#### 条件编译 +js代码 支持多个条件 +``` +if (__PLATFORM__ === "h5") {} +if (__PLATFORM__ === "app-plus") {} +if (__PLATFORM__ === "mp-weixin") {} +if (__PLATFORM__ === "mp-baidu") {} +``` diff --git a/docs/auto/quick-start.md b/docs/auto/quick-start.md new file mode 100644 index 0000000000000000000000000000000000000000..074977cb01bef3816568f66d401acb4d005b34e8 --- /dev/null +++ b/docs/auto/quick-start.md @@ -0,0 +1,263 @@ +### uni-app自动化 + +自动化 SDK 为开发者提供了一套通过外部脚本操控uni-app的方案,从而实现uni-app自动化测试的目的。 + +#### 特性 +通过该 SDK,你可以做到以下事情: + + +控制跳转到指定页面 + +获取页面数据 + +获取页面元素状态 + +触发元素绑定事件 + +调用 uni 对象上任意接口 + + + +**平台差异说明** + +|App|H5|微信小程序|支付宝小程序|百度小程序|字节跳动小程序|QQ小程序| +|:-:|:-:|:-:|:-:|:-:|:-:|:-:| +|√(ios仅支持模拟器)|√|√|x|x|x|x| + + +目前仅 [cli](https://uniapp.dcloud.net.cn/quickstart?id=_2-通过vue-cli命令行) 工程支持 + +``` +# 全局安装vue-cli +$ npm install -g @vue/cli +``` + + +#### H5平台测试流程 + +1. 创建项目 +``` +$ vue create -p dcloudio/uni-preset-vue#alpha my-project +# 进入项目目录 +$ cd my-project +``` + +2. 安装依赖 +``` +npm install puppeteer +``` + +3. 编译并启动调试服务 +``` +npm run dev:h5 -- --auto-port 9520 +``` +启动成功 +``` + App running at: + - Local: http://localhost:8080/h5/ + - Network: http://192.168.x.x:8080/h5/ +``` + +4. 运行测试(需要单独开启命令行) +``` +npm run test:h5 +``` + +5. 测试结果 +``` +>> cross-env UNI_PLATFORM=h5 jest -i +... +Test Suites: 1 passed, 1 total +Tests: 4 passed, 4 total +Snapshots: 0 total +Time: 14.995s, estimated 16s +``` + +更多配置参考 `jest.config.js` 节点 `testEnvironmentOptions` + + +#### 微信小程序测试流程 + +1. 创建cli项目,同H5平台 (必须配置微信小程序 appid, manifest.json -> mp-weixin -> appid) + +2. 运行测试(如果微信开发者工具无法成功打开项目,请手动打开) +``` +npm run test:mp-weixin +``` + +3. 测试结果 +``` +> cross-env NODE_ENV=development UNI_PLATFORM=mp-weixin vue-cli-service uni-build --watch "--auto-port" "9520" +Test Suites: 1 passed, 1 total +Tests: 4 passed, 4 total +Snapshots: 0 total +Time: 14.995s, estimated 16s +``` + + +#### 测试示例 + +使用 hello uni-app 工程测试 H5 平台 + +1. 创建 `cli` 项目,选择 `hello uni-app` +``` +$ vue create -p dcloudio/uni-preset-vue#alpha my-hello-uni-app +# 进入项目目录 +$ cd my-hello-uni-app +``` + +2. 安装 `puppeteer` +``` +npm install puppeteer +``` + +3. 创建测试文件 `src/__tests__/pages/tabBar/component/component.spec.js`,复制下面代码 +``` +describe('pages/tabBar/component/component.nvue', () => { + let page + beforeAll(async () => { + // 重新reLaunch至首页,并获取首页page对象(其中 program 是uni-automator自动注入的全局对象) + page = await program.reLaunch('/pages/tabBar/component/component') + await page.waitFor(1000) + }) + + it('u-link', async () => { + // 检测首页u-link的文本内容 + expect(await (await page.$('.hello-link')).text()).toBe('https://uniapp.dcloud.io/component/') + }) + + it('视图容器', async () => { + // 检测首个 panel 是视图容器 + expect(await (await page.$('.uni-panel-text')).text()).toBe('视图容器') + // 检测首个 panel 切换展开 + const panelH = await page.$('.uni-panel-h'); + // 不能做完全匹配,百度小程序会生成额外的class + expect(await panelH.attribute('class')).toContain('uni-panel-h') + await panelH.tap() + await page.waitFor(500) + // 已展开 + expect(await panelH.attribute('class')).toContain('uni-panel-h-on') + }) + + it('.uni-panel', async () => { + const lists = await page.$$('.uni-panel') + expect(lists.length).toBe(8) + }) + + it('.uni-panel action', async () => { + const listHead = await page.$('.uni-panel-h') + expect(await listHead.attribute('class')).toContain('uni-panel-h-on') + await listHead.tap() + await page.waitFor(200) + expect(await listHead.attribute('class')).toContain( + 'uni-panel-h', + ) + + // 展开第一个 panel,点击第一个 item,验证打开的新页面是否正确 + await listHead.tap() + await page.waitFor(200) + const item = await page.$('.uni-navigate-item') + await item.tap() + await page.waitFor(500) + expect((await program.currentPage()).path).toBe('pages/component/view/view') + await page.waitFor(500) + + // 执行 navigateBack 验证是否返回 + expect((await program.navigateBack()).path).toBe('pages/tabBar/component/component') + }) +}) +``` + +4. 编译并开启调试服务 +``` +npm run dev:h5 -- --auto-port 9520 +``` + +5. 运行测试(需要单独开启命令行) +``` +npm run test:h5 +``` + +5. 测试结果 +``` +> cross-env UNI_PLATFORM=h5 jest -i + PASS src/__tests__/pages/tabBar/component/component.spec.js (14.789s) + pages/tabBar/component/component.nvue + √ u-link (8ms) + √ 视图容器 (518ms) + √ .uni-panel (2ms) + √ .uni-panel action (4447ms) +Test Suites: 1 passed, 1 total +Tests: 4 passed, 4 total +Snapshots: 0 total +Time: 14.995s, estimated 16s +``` + + +#### jest.config.js +``` +module.exports = { + globalTeardown: '@dcloudio/uni-automator/dist/teardown.js', + testEnvironment: '@dcloudio/uni-automator/dist/environment.js', + testEnvironmentOptions: { + compile: true, + h5: { // 为了节省测试时间,可以指定一个 H5 的 url 地址,若不指定,每次运行测试,会先 npm run dev:h5 + url: "http://192.168.x.x:8080/h5/", + options: { + headless: false // 配置是否显示 puppeteer 测试窗口 + } + } + }, + testTimeout: 15000, + reporters: [ + 'default' + ], + watchPathIgnorePatterns: ['/node_modules/', '/dist/', '/.git/'], + moduleFileExtensions: ['js', 'json'], + rootDir: __dirname, + testMatch: ['/src/__tests__/**/*spec.[jt]s?(x)'], + testPathIgnorePatterns: ['/node_modules/'] +} + +``` + + +#### 真机自动化 + + + +#### 常用示例 + + + +**注意事项** + +1. 如果页面涉及到分包加载问题,`reLaunch` 获取的页面路径可能会出现问题 ,解决方案如下 : +```javascript +// 重新reLaunch至首页,并获取首页page对象(其中 program 是uni-automator自动注入的全局对象) +page = await program.reLaunch('/pages/extUI/calendar/calendar') +// 微信小程序如果是分包页面,需要延迟大概 7s 以上,保证可以正确获取page对象 +await page.waitFor(7000) +page = await program.currentPage() +``` + +2. 微信小程序 element 不能跨组件选择元素,首先要先获取到当前自组件对象,在往下继续查找 + +```html + + + +``` + +```javascript +// 错误,取不到元素 +await page.$('.test') + +// 可以取到元素 +let tag = await page.$('uni-tag') +await tag.$('.test') +``` + +3. 微信小程序不能使用父子选择器 +4. 百度小程序选择元素 必须 有事件的元素才能被选中,否则提示元素不存在 +5. 分包中的页面,打开之后要延迟时间长一点,否者不能正确获取到页面信息 diff --git a/docs/collocation/_sidebar.md b/docs/collocation/_sidebar.md index a179b5aaf179fe99e2c597e447d7e8d1eb022793..d106300c43ffdd7ea25a45abf2e907bff7ad176d 100644 --- a/docs/collocation/_sidebar.md +++ b/docs/collocation/_sidebar.md @@ -12,6 +12,9 @@ * [生命周期](collocation/frame/lifecycle.md) * [页面](collocation/frame/window.md) * [页面通讯](collocation/frame/communication.md) +* 自动化测试 + * [快速开始](auto/quick-start.md) + * [API](auto/api.md)