This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch test-autorun
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git


The following commit(s) were added to refs/heads/test-autorun by this push:
     new a7c84f7  test: add select change event. fix page size issue caused by 
scrollbar. fix some html cases.
a7c84f7 is described below

commit a7c84f74e7e56ca4000fae0ed58c59916725fbed
Author: pissang <[email protected]>
AuthorDate: Mon Sep 9 19:41:56 2019 +0800

    test: add select change event. fix page size issue caused by scrollbar. fix 
some html cases.
    
    vertical scroll bar may lead to chart container width less than 800. And 
it's not same in the iframe and puppeteer enviroment. It may cause recorded 
interaction can't replay correctly.
---
 test/bar-label-rotation.html       |   2 +-
 test/lib/config.js                 |  11 ++++
 test/runTest/Timeline.js           |   5 ++
 test/runTest/actions/update.js     |  14 +++++
 test/runTest/cli.js                |   5 +-
 test/runTest/client/index.html     |   6 +-
 test/runTest/installMouseHelper.js |  73 ++++++++++++++++++++++++
 test/runTest/recorder/index.html   |   4 +-
 test/runTest/recorder/recorder.js  | 114 ++++++++++++++++++++++++++++++-------
 test/runTest/runtime/main.js       |   3 +-
 test/runTest/server.js             |   8 ++-
 test/runTest/store.js              |  34 +++++++----
 12 files changed, 238 insertions(+), 41 deletions(-)

diff --git a/test/bar-label-rotation.html b/test/bar-label-rotation.html
index 0a75321..01e78c2 100644
--- a/test/bar-label-rotation.html
+++ b/test/bar-label-rotation.html
@@ -33,7 +33,7 @@ under the License.
                 margin: 0;
             }
             #main {
-                width: 1000px;
+                width: 100%;
                 background: #fff;
             }
         </style>
diff --git a/test/lib/config.js b/test/lib/config.js
index 58ec69e..5409c4a 100644
--- a/test/lib/config.js
+++ b/test/lib/config.js
@@ -77,6 +77,17 @@
         });
     }
 
+    // Not let scrollbar affect page size.
+    // It will AFFECT interaction in the automatic testing.
+    // TODO it only works on webkit
+    var styleEl = document.createElement('style');
+    document.head.appendChild(styleEl);
+    styleEl.innerHTML = `
+    body {
+        overflow: overlay!important;
+    }
+    `;
+
     // It is not a good solution.
     // Do not need it any more:
 
diff --git a/test/runTest/Timeline.js b/test/runTest/Timeline.js
index 1882c42..e2ef925 100644
--- a/test/runTest/Timeline.js
+++ b/test/runTest/Timeline.js
@@ -92,6 +92,11 @@ module.exports = class Timeline {
             case 'screenshot':
                 await takeScreenshot();
                 break;
+            case 'valuechange':
+                if (op.target === 'select') {
+                    await page.select(op.selector, op.value);
+                }
+                break;
         }
 
         this._currentOpIndex++;
diff --git a/test/runTest/actions/update.js b/test/runTest/actions/update.js
new file mode 100644
index 0000000..3a65ed4
--- /dev/null
+++ b/test/runTest/actions/update.js
@@ -0,0 +1,14 @@
+const glob = require('glob');
+const fs = require('fs');
+
+const result = {};
+glob('*.json', (err, files) => {
+    files.forEach(file => {
+        if (file.match('__meta__')) {
+            return;
+        }
+        const actions = JSON.parse(fs.readFileSync(file, 'utf-8'));
+        result[file.replace(/.json$/, '')] = actions.length;
+    });
+    fs.writeFileSync('__meta__.json', JSON.stringify(result, null, 2), 
'utf-8');
+});
\ No newline at end of file
diff --git a/test/runTest/cli.js b/test/runTest/cli.js
index 689d41f..436a879 100644
--- a/test/runTest/cli.js
+++ b/test/runTest/cli.js
@@ -5,6 +5,7 @@ const fs = require('fs');
 const path = require('path');
 const program = require('commander');
 const compareScreenshot = require('./compareScreenshot');
+const {installMouseHelper} = require('./installMouseHelper');
 const {testNameFromFile, fileNameFromTest, getVersionDir, buildRuntimeCode, 
waitTime} = require('./util');
 const {origin} = require('./config');
 const Timeline = require('./Timeline');
@@ -18,13 +19,11 @@ program
 program.parse(process.argv);
 
 program.speed = +program.speed || 1;
-console.log(program.speed);
 
 if (!program.tests) {
     throw new Error('Tests are required');
 }
 
-
 function getScreenshotDir() {
     return 'tmp/__screenshot__';
 }
@@ -119,6 +118,8 @@ async function runTestPage(browser, testOpt, version, 
runtimeCode) {
     const page = await browser.newPage();
     page.setRequestInterception(true);
     page.on('request', replaceEChartsVersion);
+
+    installMouseHelper(page);
     await page.evaluateOnNewDocument(runtimeCode);
 
     page.on('console', msg => {
diff --git a/test/runTest/client/index.html b/test/runTest/client/index.html
index 2201025..abc67de 100644
--- a/test/runTest/client/index.html
+++ b/test/runTest/client/index.html
@@ -75,7 +75,11 @@
                             :percentage="test.percentage"
                             :status="test.summary"
                         ></el-progress>
-                        <a :href="'#' + test.name" 
class="menu-link">{{test.name}}<i v-if="test.hasActions" 
class="el-icon-video-camera-solid"></i></a>
+                        <a :href="'#' + test.name" class="menu-link">
+                            {{test.name}}
+                            <i v-if="test.actions" 
class="el-icon-video-camera-solid"></i>
+                            <span v-if="test.actions" style="font-size: 
12px;">({{test.actions}})</span>
+                        </a>
                     </li>
                 </ul>
             </el-aside>
diff --git a/test/runTest/installMouseHelper.js 
b/test/runTest/installMouseHelper.js
new file mode 100644
index 0000000..5c3dba7
--- /dev/null
+++ b/test/runTest/installMouseHelper.js
@@ -0,0 +1,73 @@
+// https://gist.github.com/aslushnikov/94108a4094532c7752135c42e12a00eb
+
+// This injects a box into the page that moves with the mouse;
+// Useful for debugging
+async function installMouseHelper(page) {
+  await page.evaluateOnNewDocument(() => {
+    // Install mouse helper only for top-level frame.
+    if (window !== window.parent)
+      return;
+    window.addEventListener('DOMContentLoaded', () => {
+      const box = document.createElement('puppeteer-mouse-pointer');
+      const styleElement = document.createElement('style');
+      styleElement.innerHTML = `
+        puppeteer-mouse-pointer {
+          pointer-events: none;
+          position: absolute;
+          top: 0;
+          z-index: 10000;
+          left: 0;
+          width: 20px;
+          height: 20px;
+          background: rgba(0,0,0,.4);
+          border: 1px solid white;
+          border-radius: 10px;
+          margin: -10px 0 0 -10px;
+          padding: 0;
+          transition: background .2s, border-radius .2s, border-color .2s;
+        }
+        puppeteer-mouse-pointer.button-1 {
+          transition: none;
+          background: rgba(0,0,0,0.9);
+        }
+        puppeteer-mouse-pointer.button-2 {
+          transition: none;
+          border-color: rgba(0,0,255,0.9);
+        }
+        puppeteer-mouse-pointer.button-3 {
+          transition: none;
+          border-radius: 4px;
+        }
+        puppeteer-mouse-pointer.button-4 {
+          transition: none;
+          border-color: rgba(255,0,0,0.9);
+        }
+        puppeteer-mouse-pointer.button-5 {
+          transition: none;
+          border-color: rgba(0,255,0,0.9);
+        }
+      `;
+      document.head.appendChild(styleElement);
+      document.body.appendChild(box);
+      document.addEventListener('mousemove', event => {
+        box.style.left = event.pageX + 'px';
+        box.style.top = event.pageY + 'px';
+        updateButtons(event.buttons);
+      }, true);
+      document.addEventListener('mousedown', event => {
+        updateButtons(event.buttons);
+        box.classList.add('button-' + event.which);
+      }, true);
+      document.addEventListener('mouseup', event => {
+        updateButtons(event.buttons);
+        box.classList.remove('button-' + event.which);
+      }, true);
+      function updateButtons(buttons) {
+        for (let i = 0; i < 5; i++)
+          box.classList.toggle('button-' + i, buttons & (1 << i));
+      }
+    }, false);
+  });
+};
+
+module.exports = {installMouseHelper};
diff --git a/test/runTest/recorder/index.html b/test/runTest/recorder/index.html
index 09a7e9c..078c603 100644
--- a/test/runTest/recorder/index.html
+++ b/test/runTest/recorder/index.html
@@ -64,7 +64,8 @@
             <li v-for="test in tests">
                 <a :href="'#' + test.name" :class="{active: test.name === 
currentTestName}">
                     {{test.name}}
-                    <i v-if="test.hasActions" 
class="el-icon-video-camera-solid"></i>
+                    <i v-if="test.actions" 
class="el-icon-video-camera-solid"></i>
+                    <span v-if="test.actions" style="font-size: 
14px;">({{test.actions}})</span>
                 </a>
             </li>
         </ul>
@@ -80,6 +81,7 @@
 <link rel="stylesheet" 
href="https://cdn.jsdelivr.net/npm/[email protected]/lib/theme-chalk/index.css";>
 <script 
src="https://cdn.jsdelivr.net/npm/[email protected]/lib/index.js";></script>
 <script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.js";></script>
+
 <script src="recorder.js"></script>
 
 <link rel="stylesheet" href="recorder.css">
diff --git a/test/runTest/recorder/recorder.js 
b/test/runTest/recorder/recorder.js
index 158ebb7..b11d274 100644
--- a/test/runTest/recorder/recorder.js
+++ b/test/runTest/recorder/recorder.js
@@ -1,5 +1,27 @@
 const socket = io('/recorder');
 
+function getUniqueSelector(el) {
+    if (el.tagName.toLowerCase() === 'body') {
+        return '';
+    }
+    let selector = '';
+    if (el.id) {
+        // id has highest priority.
+        return el.id;
+    }
+    else {
+        selector = el.tagName.toLowerCase();
+        for (let className of el.classList) {
+            selector += '.' + className;
+        }
+    }
+    let parentSelector = el.parentNode && getUniqueSelector(el.parentNode);
+    if (parentSelector) {
+        selector = parentSelector + '>' + selector;
+    }
+    return selector;
+}
+
 const app = new Vue({
     el: '#app',
     data: {
@@ -66,6 +88,9 @@ const app = new Vue({
                 this.deletePopoverVisible = false;
                 let idx = _.findIndex(this.actions, action => action.name === 
actionName);
                 if (idx >= 0) {
+                    if (this.currentAction === this.actions[idx]) {
+                        this.currentAction = this.actions[idx + 1] || 
this.actions[idx - 1];
+                    }
                     this.actions.splice(idx, 1);
                     saveData();
                 }
@@ -116,9 +141,24 @@ function saveData() {
             testName: app.currentTestName,
             actions: app.actions
         });
+
+        let test = app.tests.find(testOpt => testOpt.name === 
app.currentTestName);
+        test.actions = app.actions.length;
     }
 }
 
+function getEventTime() {
+    return Date.now() - app.recordingAction.timestamp;
+}
+function notify(title, message) {
+    app.$notify.info({
+        title,
+        message,
+        position: 'top-left',
+        customClass: 'op-notice'
+    });
+}
+
 function keyboardRecordingHandler(e) {
     if (e.key.toLowerCase() === 'r' && e.shiftKey) {
         if (!app.recordingAction) {
@@ -147,13 +187,9 @@ function keyboardRecordingHandler(e) {
         if (app.recordingAction) {
             app.recordingAction.ops.push({
                 type: 'screenshot',
-                time: Date.now() - app.recordingAction.timestamp
-            });
-            app.$notify.info({
-                title: 'screenshot',
-                position: 'top-left',
-                customClass: 'op-notice'
+                time: getEventTime()
             });
+            notify('screenshot', '');
         }
     }
 }
@@ -161,9 +197,10 @@ function keyboardRecordingHandler(e) {
 function recordIframeEvents(iframe, app) {
     let innerDocument = iframe.contentWindow.document;
 
+
     function addMouseOp(type, e) {
         if (app.recordingAction) {
-            let time = Date.now() - app.recordingAction.timestamp;
+            let time = getEventTime();
             app.recordingAction.ops.push({
                 type,
                 time: time,
@@ -171,31 +208,69 @@ function recordIframeEvents(iframe, app) {
                 y: e.clientY
             });
             if (type === 'mouseup' && app.config.screenshotAfterMouseUp) {
+                // Add a auto screenshot after mouseup
                 app.recordingAction.ops.push({
-                    time: time + 1, // TODO, Add delay time?
+                    time: time + 1,
                     delay: app.config.screenshotDelay,
                     type: 'screenshot-auto'
                 });
             }
-            app.$notify.info({
-                title: type,
-                message: `(x: ${e.clientX}, y: ${e.clientY})`,
-                position: 'top-left',
-                customClass: 'op-notice'
-            });
+            notify(type, `(x: ${e.clientX}, y: ${e.clientY})`);
         }
     }
 
     innerDocument.addEventListener('keyup', keyboardRecordingHandler);
 
+    let preventRecordingFollowingMouseEvents = false;
     innerDocument.body.addEventListener('mousemove', _.throttle(e => {
-        addMouseOp('mousemove', e);
+        if (!preventRecordingFollowingMouseEvents) {
+            addMouseOp('mousemove', e);
+        }
     }, 200), true);
+    innerDocument.body.addEventListener('mousedown', e => {
+        // Can't recording mouse event on select.
+        // So just prevent it and add a specific 'select' change event.
+        if (e.target.tagName.toLowerCase() === 'select') {
+            preventRecordingFollowingMouseEvents = true;
+            return;
+        }
+        addMouseOp('mousedown', e);
+    }, true);
+    innerDocument.body.addEventListener('mouseup', e => {
+        if (!preventRecordingFollowingMouseEvents) {
+            addMouseOp('mouseup', e);
+        }
+        preventRecordingFollowingMouseEvents = false;
+    }, true);
 
-    ['mouseup', 'mousedown'].forEach(eventType => {
-        innerDocument.body.addEventListener(eventType, e => {
-            addMouseOp(eventType, e);
-        }, true);
+
+    innerDocument.body.addEventListener('change', e => {
+        if (app.recordingAction) {
+            let selector = getUniqueSelector(e.target);
+            let time = getEventTime();
+            let commonData = {
+                type: 'valuechange',
+                selector,
+                value: e.target.value,
+                time: time
+            };
+            if (e.target.tagName.toLowerCase() === 'select') {
+                commonData.target = 'select';
+                notify('valuechange', `select(${commonData.value})`);
+            }
+            if (commonData.target) {
+                app.recordingAction.ops.push(commonData);
+
+                if (app.config.screenshotAfterMouseUp) {
+                    // Add a auto screenshot after mouseup
+                    app.recordingAction.ops.push({
+                        time: time + 1,
+                        delay: app.config.screenshotDelay,
+                        type: 'screenshot-auto'
+                    });
+                }
+            }
+        }
     });
 }
 
@@ -219,7 +294,6 @@ function init() {
 
     let $iframe = getIframe();
     $iframe.onload = () => {
-        console.log('loaded:' + app.currentTestName);
         recordIframeEvents($iframe, app);
     };
 
diff --git a/test/runTest/runtime/main.js b/test/runTest/runtime/main.js
index d527f3b..0d79b12 100644
--- a/test/runTest/runtime/main.js
+++ b/test/runTest/runtime/main.js
@@ -5,10 +5,9 @@ if (typeof __TEST_PLAYBACK_SPEED__ === 'undefined') {
     window.__TEST_PLAYBACK_SPEED__ = 1;
 }
 
-
 let myRandom = new seedrandom('echarts-test');
 // Fixed random generator
 Math.random = function () {
     const val = myRandom();
     return val;
-};
+};
\ No newline at end of file
diff --git a/test/runTest/server.js b/test/runTest/server.js
index 3e6b2f6..120762d 100644
--- a/test/runTest/server.js
+++ b/test/runTest/server.js
@@ -5,7 +5,7 @@ const path = require('path');
 const {fork} = require('child_process');
 const semver = require('semver');
 const {port, origin} = require('./config');
-const {getTestsList, updateTestsList, saveTestsList, mergeTestsResults} = 
require('./store');
+const {getTestsList, updateTestsList, saveTestsList, mergeTestsResults, 
updateActionsMeta} = require('./store');
 const {prepareEChartsVersion, getActionsFullPath} = require('./util');
 const fse = require('fs-extra');
 const fs = require('fs');
@@ -205,6 +205,7 @@ async function start() {
                     JSON.stringify(data.actions),
                     'utf-8'
                 );
+                updateActionsMeta(data.testName, data.actions);
             }
             // TODO Broadcast the change?
         });
@@ -224,7 +225,8 @@ async function start() {
             try {
                 await startTests([data.testName], socket, {
                     noHeadless: true,
-                    threadsCount: 1
+                    threadsCount: 1,
+                    replaySpeed: 2
                 });
             }
             catch (e) { console.error(e); }
@@ -236,7 +238,7 @@ async function start() {
             tests: getTestsList().map(test => {
                 return {
                     name: test.name,
-                    hasActions: test.hasActions
+                    actions: test.actions
                 };
             })
         });
diff --git a/test/runTest/store.js b/test/runTest/store.js
index f5adc5e..9457904 100644
--- a/test/runTest/store.js
+++ b/test/runTest/store.js
@@ -62,18 +62,17 @@ module.exports.updateTestsList = async function () {
         _tests.push(test);
         _testsMap[fileUrl] = test;
     });
-    let statAsync = util.promisify(fs.stat);
-    // Find if file has actions.
-    await Promise.all(_tests.map(testOpt => {
-        return statAsync(path.join(__dirname, 'actions', testOpt.name + 
'.json'))
-            .then(() => {
-                testOpt.hasActions = true;
-            })
-            .catch(() => {
-                testOpt.hasActions = false;
-            });
-    }));
 
+    let actionsMetaData = {};
+    let metaPath = path.join(__dirname, 'actions/__meta__.json');
+    try {
+        actionsMetaData = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
+    }
+    catch(e) {}
+
+    _tests.forEach(testOpt => {
+        testOpt.actions = actionsMetaData[testOpt.name] || 0;
+    });
     return _tests;
 };
 
@@ -87,4 +86,17 @@ module.exports.mergeTestsResults = function (testsResults) {
             Object.assign(_testsMap[testResult.fileUrl], testResult);
         }
     });
+};
+
+module.exports.updateActionsMeta = function (testName, actions) {
+    let metaData;
+    let metaPath = path.join(__dirname, 'actions/__meta__.json');
+    try {
+        metaData = JSON.parse(fs.readFileSync(metaPath, 'utf-8'));
+    }
+    catch(e) {
+        metaData = {};
+    }
+    metaData[testName] = actions.length;
+    fs.writeFileSync(metaPath, JSON.stringify(metaData, null, 2), 'utf-8');
 };
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to