提交 2973d0c9 编写于 作者: P pissang

test: improve ui of recording screenshots.

上级 e3d8d628
const {waitTime} = require('./util');
module.exports = class Timeline {
constructor(page) {
......@@ -17,7 +19,7 @@ module.exports = class Timeline {
}
async runAction(action, playbackSpeed) {
async runAction(action, takeScreenshot, playbackSpeed) {
this.stop();
playbackSpeed = playbackSpeed || 1;
......@@ -45,7 +47,7 @@ module.exports = class Timeline {
self._elapsedTime += dTime * playbackSpeed;
self._current = current;
await self._update();
await self._update(takeScreenshot);
if (self._currentOpIndex >= self._ops.length) {
// Finished
resolve();
......@@ -66,7 +68,7 @@ module.exports = class Timeline {
}
}
async _update() {
async _update(takeScreenshot) {
let op = this._ops[this._currentOpIndex];
if (op.time > this._elapsedTime) {
......@@ -85,11 +87,25 @@ module.exports = class Timeline {
await page.mouse.up();
break;
case 'mousemove':
page.mouse.move(op.x, op.y);
await page.mouse.move(op.x, op.y);
break;
case 'screenshot':
await takeScreenshot();
break;
}
this._currentOpIndex++;
// If next op is an auto screenshot
let nextOp = this._ops[this._currentOpIndex];
console.log(nextOp.type);
if (nextOp && nextOp.type === 'screenshot-auto') {
// TODO Configuration time
await waitTime(300);
console.log(Date.now());
await takeScreenshot();
console.log(Date.now());
this._currentOpIndex++;
}
}
};
\ No newline at end of file
......@@ -5,7 +5,7 @@ const fs = require('fs');
const path = require('path');
const program = require('commander');
const compareScreenshot = require('./compareScreenshot');
const {testNameFromFile, fileNameFromTest, getVersionDir, buildRuntimeCode} = require('./util');
const {testNameFromFile, fileNameFromTest, getVersionDir, buildRuntimeCode, waitTime} = require('./util');
const {origin} = require('./config');
const Timeline = require('./Timeline');
......@@ -48,19 +48,14 @@ function replaceEChartsVersion(interceptedRequest, version) {
}
}
function waitTime(time) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
}
async function takeScreenshot(page, fullPage, fileUrl, desc, version) {
async function takeScreenshot(page, fullPage, fileUrl, desc, version, minor) {
let screenshotName = testNameFromFile(fileUrl);
if (desc) {
screenshotName += '-' + slugify(desc, { replacement: '-', lower: true });
}
if (minor) {
screenshotName += '-' + minor;
}
let screenshotPrefix = version ? 'expected' : 'actual';
fse.ensureDirSync(path.join(__dirname, getScreenshotDir()));
let screenshotPath = path.join(__dirname, `${getScreenshotDir()}/${screenshotName}-${screenshotPrefix}.png`);
......@@ -91,14 +86,23 @@ async function runActions(page, testOpt, version, screenshots) {
window.scrollTo(x, y);
}, action.scrollX, action.scrollY);
await timeline.runAction(action, playbackSpeed);
// Wait for animation finished
// TODO Configuration.
await waitTime(action.wait == null ? 200 : action.wait);
let count = 0;
async function _innerTakeScreenshot() {
const desc = action.desc || action.name;
const {screenshotName, screenshotPath} = await takeScreenshot(page, false, testOpt.fileUrl, desc, version, count++);
screenshots.push({screenshotName, desc, screenshotPath});
}
await timeline.runAction(action, _innerTakeScreenshot, playbackSpeed);
const desc = action.desc || action.name;
const {screenshotName, screenshotPath} = await takeScreenshot(page, false, testOpt.fileUrl, desc, version);
screenshots.push({screenshotName, desc, screenshotPath});
if (count === 0) {
// TODO Configuration time
await waitTime(300);
await _innerTakeScreenshot();
}
// const desc = action.desc || action.name;
// const {screenshotName, screenshotPath} = await takeScreenshot(page, false, testOpt.fileUrl, desc, version);
// screenshots.push({screenshotName, desc, screenshotPath});
}
timeline.stop();
}
......@@ -132,18 +136,14 @@ async function runTestPage(browser, testOpt, version, runtimeCode) {
console.error(e);
}
await waitTime(500); // Wait for animation or something else.
// Final shot.
let desc = 'Full Shot';
const {screenshotName, screenshotPath} = await takeScreenshot(page, true, fileUrl, desc, version);
screenshots.push({screenshotName, desc, screenshotPath});
await runActions(page, testOpt, version, screenshots);
if (!screenshots.length) {
waitTime(500); // Wait for animation or something else.
}
await page.close();
return {
......
......@@ -108,10 +108,14 @@
}
.test-screenshots {
margin-top: 80px;
margin-top: 20px;
padding: 0 20px;
}
.test-screenshots h4 {
margin-top: 60px;
}
.test-screenshots img {
/* height: 200px; */
width: 100%;
......
......@@ -69,11 +69,14 @@
<a target="_blank" :href="currentTestRecordUrl"><i class="el-icon-video-camera"></i>Record Interaction</a>
</div>
<div class="test-screenshots" v-for="result in currentTest.results">
<h4>{{result.desc || result.name}}</h4>
<div class="test-screenshots" v-for="(result, idx) in currentTest.results">
<!-- Not display title if it's same with previous -->
<h4 v-if="result.desc !== (currentTest.results[idx - 1] && currentTest.results[idx - 1].desc)">
{{result.desc || result.name}}
</h4>
<el-row :gutter="40" class="screenshots">
<el-col :span="8">
<el-card>
<el-card shadow="hover">
<div slot="header" class="clearfix">
<span>Expected</span>
</div>
......@@ -82,7 +85,7 @@
</el-col>
<el-col :span="8">
<el-card>
<el-card shadow="hover">
<div slot="header" class="clearfix">
<span>Actual</span>
</div>
......@@ -91,7 +94,7 @@
</el-col>
<el-col :span="8">
<el-card>
<el-card shadow="hover">
<div slot="header" class="clearfix">
<span>Diff({{result.diffRatio.toFixed(4)}})</span>
</div>
......
......@@ -8,7 +8,7 @@
<body>
<div id="app" style="display: none">
<iframe :src="url"></iframe>
<div id="recording-status" v-if="currentAction">
<div id="recording-status">
<el-button
:icon="recordingAction ? 'el-icon-video-camera' : 'el-icon-video-camera'"
class="recording-button" circle :type="recordingAction ? 'danger' : 'info'"
......@@ -20,25 +20,31 @@
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>Actions</span>
<div style="float:right">
<div class="toolbar">
<el-button type="primary" size="mini" icon="el-icon-caret-right" circle @click="run"></el-button>
<el-button type="primary" size="mini" icon="el-icon-circle-plus" @click="newAction">New</el-button>
<el-popover title="Configuration" class="configuration">
<div class="config-item">
<el-checkbox v-model="config.screenshotAfterMouseUp">Auto screenshot after mouse up</el-checkbox>
</div>
<i slot="reference" class="el-icon-setting"></i>
</el-popover>
</div>
</div>
<div v-for="action in actions" :class="{'action-item': true, 'active': action.name === currentAction.name}" @click.self="select(action.name)">
{{action.name}}
<div style="float:right" class="operations">
<el-popover>
<div style="text-align: center">{ scrollX: {{action.scrollX}}, scrollY: {{action.scrollY}} }</div>
<div v-for="op in action.ops" style="text-align: center">{{op}}</div>
<!-- <el-button slot="reference" size="mini">OP ({{action.ops.length}})</el-button> -->
<div slot="reference">
<span>Data ({{action.ops.length}})</span>
<i slot="reference" class="el-icon-caret-bottom"></i>
</div>
</el-popover>
<i slot="reference" class="el-icon-delete" @click="doDelete(action.name)"></i>
<i class="el-icon-delete" @click="doDelete(action.name)"></i>
<i class="el-icon-refresh-left" @click="clearOps(action.name)"></i>
</div>
</div>
</el-card>
......
......@@ -104,6 +104,25 @@ iframe {
width: 300px;
}
#actions .toolbar {
float: right;
margin-top: -5px;
}
#actions .toolbar>* {
display: inline-block;
vertical-align: middle;
}
#actions .toolbar i.el-icon-setting {
font-size: 20px;
cursor: pointer;
margin-left: 10px;
}
#actions .toolbar .config-item {
margin: 5px 0;
}
#actions .action-item {
line-height: 40px;
padding: 0 20px;
......@@ -111,7 +130,7 @@ iframe {
cursor: pointer;
}
\
#actions .action-item:hover {
background: #eee;
}
......@@ -129,6 +148,10 @@ iframe {
display: inline-block;
vertical-align: middle;
}
#actions .action-item .operations i {
margin-left: 5px;
font-size: 18px;
}
#actions .action-item .operations .el-icon-delete {
color: #F56C6C;
......
......@@ -10,6 +10,9 @@ const app = new Vue({
currentAction: null,
recordingAction: null,
config: {
screenshotAfterMouseUp: true
},
drawerVisible: true
},
......@@ -50,6 +53,20 @@ const app = new Vue({
}).catch(e => {});
},
clearOps(actionName) {
app.$confirm('Aure you sure?', 'Clear this action', {
confirmButtonText: 'Yes',
cancelButtonText: 'No',
type: 'warning'
}).then(() => {
this.deletePopoverVisible = false;
let action = this.actions.find(action => action.name === actionName);
if (action) {
action.ops = [];
}
}).catch(e => {});
},
run() {
socket.emit('runSingle', {
testName: app.currentTestName
......@@ -71,10 +88,14 @@ function saveData() {
function keyboardRecordingHandler(e) {
if (e.key.toLowerCase() === 'r' && e.shiftKey) {
if (!app.recordingAction) {
// Create a new action if currentAction has ops.
if (!app.currentAction || app.currentAction.ops.length > 0) {
app.newAction();
}
app.recordingAction = app.currentAction;
if (app.recordingAction) {
let $iframe = document.body.querySelector('iframe');
app.recordingAction.ops = [];
app.recordingAction.scrollY = $iframe.contentWindow.scrollY;
app.recordingAction.scrollX = $iframe.contentWindow.scrollX;
app.recordingAction.timestamp = Date.now();
......@@ -93,12 +114,19 @@ function recordIframeEvents(iframe, app) {
function addMouseOp(type, e) {
if (app.recordingAction) {
let time = Date.now() - app.recordingAction.timestamp;
app.recordingAction.ops.push({
type,
time: Date.now() - app.recordingAction.timestamp,
time: time,
x: e.clientX,
y: e.clientY
});
if (type === 'mouseup' && app.config.screenshotAfterMouseUp) {
app.recordingAction.ops.push({
time: time + 1, // TODO, Add delay time?
type: 'screenshot-auto'
});
}
app.$notify.info({
title: type,
message: `(x: ${e.clientX}, y: ${e.clientY})`,
......
......@@ -70,4 +70,12 @@ module.exports.buildRuntimeCode = async function () {
// seedrandom use crypto as external module. Set it to null to avoid not defined error.
// TODO
return 'window.crypto = null\n' + output.code;
};
\ No newline at end of file
};
module.exports.waitTime = function (time) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, time);
});
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册