Skip to content

Commit

Permalink
feat(e2e): 修复事件绑定父元素时无法正确回放的问题;增加mockapi开关;增加自定义开发期望条件配置;增加是否截屏比对配置开关;
Browse files Browse the repository at this point in the history
re #80
  • Loading branch information
fanniehuang committed Jan 26, 2021
1 parent 92c1c7d commit b592cab
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 44 deletions.
5 changes: 2 additions & 3 deletions packages/wxa-cli/src/tester/domWalker.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ class XMLManager {

eventMap = eventMap.replace(/^\|/, '');
element.attribs['data-_wxaTestEventMap'] = eventMap;

// generate unique id for tag.
}
// generate unique id for tag.
// pagePath + hash(parentNode + prevNode) + optional(class/id)
let pagePath = path.relative(this.scheduler.wxaConfigs.context, path.dirname(this.mdl.src) + path.sep + path.basename(this.mdl.src, path.extname(this.mdl.src)));

Expand All @@ -75,7 +75,6 @@ class XMLManager {
let id = this.assembleUniqueId(keyElement);
element.attribs['data-_wxaTestUniqueId'] = id;
element.attribs['class'] = this.dropSpace((element.attribs['class'] || '') + ' '+ id);
}
}

findSelfOrAncestorIterationDirective(element) {
Expand Down
14 changes: 13 additions & 1 deletion packages/wxa-cli/src/tester/readMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 主动操作返回(因无法监听返回事件,所以录制过程中 *点击物理返回键*、*小程序titlebar返回键*、*ios手势返回*等返回操作,暂无法支持)
* 小程序原生的showModal、showActionSheet上的点击操作,无法录制&回放。虽然可以往wxa/core植入点代码,知道用户点击了哪个,执行了哪个函数。但回放的时候,原生的元素取不到,如果直接执行对应函数的话,modal弹框会一直在界面上,除非用户操作不然都不会消失
* 已知bug:
* tap事件绑定在父元素上,且依赖子元素上data数据的话,会有问题。应该要每个元素都给id编号,然后tap事件找到detail.target真实触发的元素,回放的时候tap detail.target那个元素
* 暂无
* 待优化:
* 不带-r参数时,即回放模式,仅添加元素id(回放测试用例时能找到对应元素),不侵入过多代码(现在会劫持各种tap等事件,植入全局按钮组件)

Expand Down Expand Up @@ -88,3 +88,15 @@ module.exports = {
}
```
* `wxa2 test --e2e` 进入测试用例回放模式,`--test=testName`指定执行用例,多个用例逗号分隔,操作截屏以时间命名保存在测试用例目录中,带参数`--screenshot`则会与`expect_screenshot`的截屏进行diff


### 二次开发录制好的测试用例
通过修改`测试用例/record.js`,可以进行用例二次开发
record.js是一个数组,每一项Object对应用户一次操作(点击、输入or删除一个字符)

|key|类型|默认值|备注|
| :-----| :---- | :---- | :---- |
| action | Object| 本次操作信息,小程序包装好的事件信息,可<a href="https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxml/event.html">查看文档</a> | 【type:操作类型,tap touchstart点击,input输入】<br> 【currentTarget.dataset._wxatestuniqueid:触发事件的页面元素id】<br/>|
| screenshotDiff | Boolean| false | 每一步操作截屏,是否要和expect_screenshot进行diff比对。启动命令带--screenshot参数时,忽略该配置,都会截屏diff比对。 |
| coustomExpect | Function| - | 编写自定义期望条件 |

68 changes: 40 additions & 28 deletions packages/wxa-cli/src/tester/wxa-e2eTest/e2eTestCaseTpl.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ let page;
let testCaseNameArr = JSON.parse('<%- testCaseNameArr %>')
let testDir = '<%- testDir %>';
let screenshotDirname = '<%- screenshotPath %>'
let screenshot = <%- screenshot %>;
let screenshotDiff = <%- screenshotDiff %>;
let base = <%- base %>;
let noMockApi = <%- noMockApi%>
let customExpect = <%- customExpect%>
const sleep = t => new Promise(resolve => setTimeout(resolve, t));


Expand Down Expand Up @@ -51,37 +53,43 @@ for (let j = 0; j < testCaseNameArr.length; j++) {
test(testName, async () => {
let screenshotDir = path.join(testDir, testName, screenshotDirname);
let diffDir = path.join(screenshotDir, 'diff');
if (base) {
try {
fs.rmdirSync(screenshotDir);
} catch (err) {}
}
try {
fs.mkdirSync(screenshotDir)
if (screenshot) {
if (!base) {
fs.mkdirSync(diffDir);
}
} catch(err) {
console.log(err);
}
} catch(err) {}

let apiMockMap = require(path.join(testDir, testName, `./api.json`));
let record = require(path.join(testDir, testName, `./record.js`));
let screenCount = 0;
// mock接口
await miniProgram.mockWxMethod(
'request',
function(reqObj, apiMockMap) {
let mapKey = `${reqObj.url}__e2e__${reqObj.method}__e2e__${Object.keys(reqObj.data).join(',')}`;
if (apiMockMap[mapKey] && apiMockMap[mapKey].length > 0) {
console.log(mapKey, 'mock success')
return apiMockMap[mapKey].shift();
}
return new Promise(resolve => {
reqObj.success = res => resolve(res)
reqObj.fail = res => resolve(res)
// origin 指向原始方法
console.log(mapKey, 'no mock')
this.origin(reqObj)
})
},
apiMockMap
)
if (!noMockApi) {
let apiMockMap = require(path.join(testDir, testName, `./api.json`));
await miniProgram.mockWxMethod(
'request',
function(reqObj, apiMockMap) {
let mapKey = `${reqObj.url}__e2e__${reqObj.method}__e2e__${Object.keys(reqObj.data).join(',')}`;
if (apiMockMap[mapKey] && apiMockMap[mapKey].length > 0) {
console.log(mapKey, 'mock success')
return apiMockMap[mapKey].shift();
}
return new Promise(resolve => {
reqObj.success = res => resolve(res)
reqObj.fail = res => resolve(res)
// origin 指向原始方法
console.log(mapKey, 'no mock')
this.origin(reqObj)
})
},
apiMockMap
)
}

// 开始回放+截屏
page = await miniProgram.reLaunch(`/${record[0].action.page}`);
let element;
Expand All @@ -96,13 +104,17 @@ for (let j = 0; j < testCaseNameArr.length; j++) {
}
await sleep(1000);
}

await page.waitFor(`.${recordAction.id}`);
element = await page.$(`.${recordAction.id}`)
let same = await screenShot({
screenshotDir,
screenCount: ++screenCount,
diff: screenshot
screenCount: screenCount++,
diff: screenshotDiff || (record[i-1] && record[i-1].screenshotDiff)
})
if (customExpect && i !== 0 && record[i - 1].customExpect && typeof record[i - 1].customExpect === 'function') {
record[i - 1].customExpect();
}

if (!!~['tap', 'longpress'].indexOf(recordAction.event)) {
await element[recordAction.event]();
Expand All @@ -122,10 +134,10 @@ for (let j = 0; j < testCaseNameArr.length; j++) {
let same = await screenShot({
screenshotDir,
screenCount: ++screenCount,
diff: screenshot
diff: screenshotDiff || record[record.length - 1].sceeenshotDiff
})
// 无diff图片,则比对通过
if (screenshot) {
if (!base) {
expect(diffDir).toBeEmpty(testName);
}
})
Expand Down
2 changes: 1 addition & 1 deletion packages/wxa-cli/src/tester/wxa-e2eTest/e2eTestSuite.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ const addRecord = function(type, ...args) {
}

// 调用eventMap中原方法
let eventFunc = getEventFunc(type, target.dataset[EVENTMAPKEY]);
let eventFunc = getEventFunc(type, e.currentTarget.dataset[EVENTMAPKEY]);
if (!eventFunc) {
console.warn(`wxa e2eTest, event "${type}" is lost`);
return;
Expand Down
26 changes: 17 additions & 9 deletions packages/wxa-cli/src/tester/wxa-e2eTest/runTestCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,24 @@ export default async function(cmd, wxaConfigs) {
let timeStamp = formatDate(+new Date());
screenshotPath = timeStamp;
}
try {
let recordString = await testCase2js({
cliPath: cli,
testCaseNameArr: JSON.stringify(testCaseNameArr),
testDir,
screenshotPath,
base: !!cmd.base,
screenshotDiff: !!cmd.screenshotDiff,
noMockApi: !!cmd.noMock,
customExpect: !!cmd.customExpect
});
writeFile(path.join(testDir, '.cache', 'index.test.js'), recordString)
} catch (err) {
console.log(err);
process.exit(-1);
}


let recordString = await testCase2js({
cliPath: cli,
testCaseNameArr: JSON.stringify(testCaseNameArr),
testDir,
screenshotPath,
base: !!cmd.base,
screenshot: !!cmd.screenshot
});
writeFile(path.join(testDir, '.cache', 'index.test.js'), recordString)
try {
execSync(`jest ${path.join(testDir, '.cache', 'index.test.js')}`, {
stdio: 'inherit'
Expand Down
5 changes: 3 additions & 2 deletions packages/wxa-cli/src/wxa.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,9 @@ commander
.option('-r, --record', 'e2e测试录制模式,启动小程序自动开始录制')
.option('-t, --test [testName]', 'e2e执行测试用例,缺省则执行所有用例,多个用例名用逗号区分')
.option('--base', '仅截屏,作为后续回放用例比较基准')
.option('--screenshot', '是否进行截屏比对')
.option('--custom-diff', '是否进行自定义条件比对')
.option('--screenshot-diff', '是否进行截屏比对')
.option('--custom-expect', '进行自定义期望匹配,record.js里每一步的customExpect函数编写期望代码')
.option('--no-mock', '不mock接口')
.action((cmd)=>{
logger.info('Hey', `This is ${chalk.keyword('orange')('wxa@'+version)}, Running in ${chalk.keyword('orange')(process.env.NODE_ENV || 'development')}, Tester Mode`);
let wxaConfigs = getConfigs();
Expand Down

0 comments on commit b592cab

Please sign in to comment.