/
githubmirror
/
echarts
Обзор
Документация
Войти
/
githubmirror
/
echarts
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/runTest/runtime/ActionPlayback.js
241 строка
8 KB
100pah
[test] Fix test facilities. (1) Fix that visual test runtime thrown error may block the entire text execution without notification. (2) Recored double mousedown without a mouseup will cause error in puppeteer. Added error message to users.
08 апр 2025, 13:17
08 апр 2025, 13:17
90395d5
Код
Авторство
О чём код?
/* * 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. */ import * as timeline from './timeline'; function waitTime(time) { return new Promise(resolve => { setTimeout(() => { resolve(); }, time); }); }; export class ActionPlayback { constructor() { this._timer = 0; this._current = 0; this._ops = []; this._currentOpIndex = 0; this._isLastOpMousewheel = false; this._isMouseDown = false; } getContext() { return { elapsedTime: this._elapsedTime, currentOpIndex: this._currentOpIndex, isLastOpMouseWheel: this._isLastOpMousewheel } } _reset() { this._currentOpIndex = 0; this._current = Date.now(); this._elapsedTime = 0; this._isLastOpMousewheel = false; this._isMouseDown = false; } _restoreContext(ctx) { this._elapsedTime = ctx.elapsedTime; this._currentOpIndex = ctx.currentOpIndex; this._isLastOpMousewheel = ctx.isLastOpMouseWheel; } async runAction(action, ctxToRestore) { this.stop(); 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(); if (ctxToRestore) { this._restoreContext(ctxToRestore); // Usually restore context happens when page is reloaded after mouseup. // In this case the _currentOpIndex is not increased yet. this._currentOpIndex++; } let self = this; async function takeScreenshot() { // Pause timeline when doing screenshot to avoid screenshot needs taking a while. timeline.pause(); await __VRT_ACTION_SCREENSHOT__(action); timeline.resume(); } return new Promise((resolve, reject) => { async function tick() { let current = Date.now(); let dTime = current - self._current; self._elapsedTime += dTime; self._current = current; try { // Execute all if there are multiple ops in one frame. do { const executed = await self._update(takeScreenshot, action.name); if (!executed) { break; } } while (true); } catch (e) { // Stop running and throw error. reject(e); return; } if (self._currentOpIndex >= self._ops.length) { // Finished if (self._isMouseDown) { reject(new Error( action.name + ' has finished, but the page remains in mousedown state.' + ' A mouseup is needed; otherwise the subsequent test case may be affected.' + ' Please re-record this test case.' )); return; } resolve(); } else { self._timer = setTimeout(tick, 0); } } tick(); }); } stop() { if (this._timer) { clearTimeout(this._timer); this._timer = 0; } } async _update(takeScreenshot, actionName) { let op = this._ops[this._currentOpIndex]; if (!op || (op.time > this._elapsedTime)) { // Not yet. return; } let screenshotTaken = false; let errMsgPart; switch (op.type) { case 'mousedown': errMsgPart = actionName + ' ' + op.type + ' (' + op.x + ',' + op.y + ')'; // Pause timeline to avoid frame not sync. timeline.pause(); await __VRT_MOUSE_MOVE__(op.x, op.y); await __VRT_MOUSE_DOWN__(errMsgPart); this._isMouseDown = true; timeline.resume(); break; case 'mouseup': errMsgPart = actionName + ' ' + op.type + ' (' + op.x + ',' + op.y + ')'; timeline.pause(); await __VRT_MOUSE_MOVE__(op.x, op.y); await __VRT_MOUSE_UP__(errMsgPart); this._isMouseDown = false; if (window.__VRT_RELOAD_TRIGGERED__) { return; } timeline.resume(); break; case 'mousemove': timeline.pause(); await __VRT_MOUSE_MOVE__(op.x, op.y); timeline.resume(); break; case 'mousewheel': let element = document.elementFromPoint(op.x, op.y); // Here dispatch mousewheel event because echarts used it. // TODO Consider upgrade? let event = new WheelEvent('mousewheel', { // PENDING // Needs inverse delta? deltaY: op.deltaY, clientX: op.x, clientY: op.y, // Needs bubble to parent container bubbles: true }); element.dispatchEvent(event); this._isLastOpMousewheel = true; break; case 'screenshot': await takeScreenshot(); screenshotTaken = true; break; case 'valuechange': const selector = document.querySelector(op.selector); if (!selector) { throw new Error( '[Test Case Error] (' + actionName + ') Selector not found: ' + op.selector + '. It may be caused by test case changes.' ); } selector.value = op.value; // changing value via js won't trigger `change` event, so trigger it manually selector.dispatchEvent(new Event('change')); 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); await takeScreenshot(); screenshotTaken = true; this._currentOpIndex++; } if (this._isLastOpMousewheel && op.type !== 'mousewheel') { // Only take screenshot after mousewheel finished if (!screenshotTaken) { await takeScreenshot(); } this._isLastOpMousewheel = false; } return true; } };