| /* |
| * 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 seedrandom from 'seedrandom'; |
| import { ActionPlayback } from './ActionPlayback'; |
| import * as timeline from './timeline'; |
| |
| let myRandom = new seedrandom('echarts-random'); |
| // Random for echarts code. |
| // In case different echarts version called random different times. |
| // It will cause following random number all wrong. |
| let myRandom2 = new seedrandom('echarts-random-inner'); |
| // Fixed random generator |
| Math.random = function () { |
| const val = myRandom(); |
| return val; |
| }; |
| |
| window.__random__inner__ = function () { |
| const val = myRandom2(); |
| return val; |
| }; |
| |
| let vstStarted = false; |
| |
| window.__VRT_START__ = function () { |
| if (vstStarted) { |
| return; |
| } |
| vstStarted = true; |
| timeline.start(); |
| |
| // TODO not support reload without simpleRequire |
| if (window.__VRT_RUN_CONTEXT__) { |
| // Restore from previous run |
| setTimeout(async () => { |
| await __VRT_RUN_ACTIONS__( |
| window.__VRT_RUN_CONTEXT__.actions, |
| window.__VRT_RUN_CONTEXT__.currentActionIndex, |
| window.__VRT_RUN_CONTEXT__.currentActionContext |
| ) |
| }, 1000); |
| } |
| else { |
| // Screenshot after 1000ms (200ms if 5x speed), wait the animation to be finished |
| setTimeout(async () => { |
| // Pause timeline until run actions. |
| timeline.pause(); |
| await __VRT_FULL_SCREENSHOT__(); |
| }, 1000); |
| } |
| } |
| |
| function saveRunningContext(actions, actionIndex, playback) { |
| localStorage.setItem('vstRunContext', JSON.stringify({ |
| actions: actions, |
| currentActionIndex: actionIndex, |
| currentActionContext: playback.getContext() |
| })); |
| } |
| |
| window.__VRT_RUN_ACTIONS__ = async function (actions, restoredActionIndex, restoredActionContext) { |
| // Actions can only bu runned once. |
| timeline.resume(); |
| |
| const actionPlayback = new ActionPlayback(); |
| |
| const nativeLocation = window.location; |
| let currentActionIndex = 0; |
| |
| // Some test cases change the params through reload(). |
| // We need to save the running info and keep running after reload. |
| // window.location seems can't be redefined anymore. So we can only provide helper functions. |
| window.__VRT_RELOAD__ = function () { |
| // Mark reload triggered and let ActionPlayback stop. |
| window.__VRT_RELOAD_TRIGGERED__ = true; |
| saveRunningContext(actions, currentActionIndex, actionPlayback); |
| timeline.nativeSetTimeout(() => { |
| // CDPSession pay be disconnected if reload immediately. |
| nativeLocation.reload(); |
| }, 100); |
| } |
| |
| for (const [index, action] of actions.entries()) { |
| currentActionIndex = index; |
| if (index < restoredActionIndex) { |
| continue; |
| } |
| window.scrollTo(action.scrollX, action.scrollY); |
| await actionPlayback.runAction(action, index === restoredActionIndex ? restoredActionContext : null); |
| |
| } |
| actionPlayback.stop(); |
| |
| __VRT_FINISH_ACTIONS__(); |
| } |
| |
| |
| window.addEventListener('DOMContentLoaded', () => { |
| let style = document.createElement('style'); |
| // Disable all css animation since it will cause screenshot inconsistent. |
| // PENDING Only tooltip? div[_echarts_instance_]>div |
| style.innerHTML = ` |
| * { |
| transition-delay: 0s !important; |
| transition-duration: 0s !important; |
| animation-delay: -0.0001s !important; |
| animation-duration: 0s !important; |
| } |
| `; |
| document.head.appendChild(style); |
| |
| |
| // Prevent triggered mouseout event when mouse move out of window. |
| // DON"T know why, but it happens occasionally and cause hover state/ tooltip wrong before screenshot. |
| |
| |
| document.body.addEventListener('mouseout', e => { |
| if (!e.relatedTarget) { |
| e.stopPropagation(); |
| e.stopImmediatePropagation(); |
| } |
| }, true); |
| |
| // Draw mouse |
| 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)); |
| } |
| } |
| }); |