blob: 787a93b6c36c8b71d83af6fca7c892651c363c21 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
const {waitTime} = require('./util');
module.exports = class Timeline {
constructor(page) {
this._page = page;
this._timer = 0;
this._current = 0;
this._ops = [];
this._currentOpIndex = 0;
this._client;
this._isLastOpMousewheel = false;
}
_reset() {
this._currentOpIndex = 0;
this._current = Date.now();
this._elapsedTime = 0;
this._isLastOpMousewheel = false;
}
async runAction(action, takeScreenshot, playbackSpeed) {
if (!this._client) {
this._client = await this._page.target().createCDPSession();
}
this.stop();
playbackSpeed = playbackSpeed || 1;
if (!action.ops.length) {
return;
}
this._ops = action.ops.slice().sort((a, b) => {
return a.time - b.time;
});
let firstOp = this._ops[0];
this._ops.forEach(op => {
op.time -= firstOp.time;
});
this._reset();
let self = this;
return new Promise(resolve => {
async function tick() {
let current = Date.now();
let dTime = current - self._current;
self._elapsedTime += dTime * playbackSpeed;
self._current = current;
await self._update(takeScreenshot, playbackSpeed);
if (self._currentOpIndex >= self._ops.length) {
// Finished
resolve();
}
else {
self._timer = setTimeout(tick, 16);
}
}
tick();
});
}
stop() {
if (this._timer) {
clearTimeout(this._timer);
this._timer = 0;
}
}
async _update(takeScreenshot, playbackSpeed) {
let op = this._ops[this._currentOpIndex];
if (op.time > this._elapsedTime) {
// Not yet.
return;
}
let page = this._page;
let takenScreenshot = false;
switch (op.type) {
case 'mousedown':
await page.mouse.move(op.x, op.y);
await page.mouse.down();
break;
case 'mouseup':
await page.mouse.move(op.x, op.y);
await page.mouse.up();
break;
case 'mousemove':
await page.mouse.move(op.x, op.y);
break;
case 'mousewheel':
await page.evaluate((x, y, deltaX, deltaY) => {
let element = document.elementFromPoint(x, y);
// Here dispatch mousewheel event because echarts used it.
// TODO Consider upgrade?
let event = new WheelEvent('mousewheel', {
// PENDING
// Needs inverse delta?
deltaY,
clientX: x, clientY: y,
// Needs bubble to parent container
bubbles: true
});
element.dispatchEvent(event);
}, op.x, op.y, op.deltaX || 0, op.deltaY);
this._isLastOpMousewheel = true;
// console.log('mousewheel', op.x, op.y, op.deltaX, op.deltaY);
// await this._client.send('Input.dispatchMouseEvent', {
// type: 'mouseWheel',
// x: op.x,
// y: op.y,
// deltaX: op.deltaX,
// deltaY: op.deltaY
// });
break;
case 'screenshot':
await takeScreenshot();
takenScreenshot = true;
break;
case 'valuechange':
if (op.target === 'select') {
await page.select(op.selector, op.value);
}
break;
}
this._currentOpIndex++;
// If next op is an auto screenshot
let nextOp = this._ops[this._currentOpIndex];
if (nextOp && nextOp.type === 'screenshot-auto') {
let delay = nextOp.delay == null ? 400 : nextOp.delay;
// TODO Configuration time
await waitTime(delay / playbackSpeed);
await takeScreenshot();
takenScreenshot = true;
this._currentOpIndex++;
}
if (this._isLastOpMousewheel && op.type !== 'mousewheel') {
// Only take screenshot after mousewheel finished
if (!takenScreenshot) {
takeScreenshot();
}
this._isLastOpMousewheel = false;
}
}
};