mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-09-07 17:47:17 +02:00
Direct function generation
This commit is contained in:
@@ -4,55 +4,37 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
export type PlaybackEvent = (
|
||||
| {
|
||||
type: "mouse.move";
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
| {
|
||||
type: "mouse.down" | "mouse.up";
|
||||
button: "left" | "right" | "middle";
|
||||
}
|
||||
| {
|
||||
type: "keyboard.down" | "keyboard.up";
|
||||
key: string;
|
||||
}
|
||||
| {
|
||||
type: "header";
|
||||
width: number;
|
||||
height: number;
|
||||
localStorage: { [key: string]: string };
|
||||
}
|
||||
) & {
|
||||
delay: number;
|
||||
};
|
||||
|
||||
export class Record {
|
||||
private static events: PlaybackEvent[] = [];
|
||||
private static events: string = "";
|
||||
private static timestamp: number = 0;
|
||||
|
||||
public static start() {
|
||||
Record.events += ` await page.setViewportSize({ width: ${window.innerWidth}, height: ${window.innerHeight} });\n`;
|
||||
Record.events += `await page.goto("http://localhost:3000");`;
|
||||
Record.events += `await page.waitForLoadState("load");`;
|
||||
|
||||
// Record.events += " const mask = [\n";
|
||||
// Record.events += ` page.getByRole("button", { name: "Share" }),\n`;
|
||||
// Record.events += ` page.getByTitle("Library").locator("div"),\n`;
|
||||
// Record.events += " ];\n";
|
||||
|
||||
// capture a snapshot of localStorage (if available) to include in the header
|
||||
const lsSnapshot: { [key: string]: string } = {};
|
||||
Record.events += " await page.evaluate(() => {\n";
|
||||
try {
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key != null) {
|
||||
lsSnapshot[key] = localStorage.getItem(key) ?? "";
|
||||
const value = JSON.stringify(localStorage.getItem(key));
|
||||
Record.events += ` localStorage.getItem("${key}");\n`;
|
||||
Record.events += ` localStorage.setItem("${key}", ${value});\n`;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
Record.events += " });\n";
|
||||
Record.events += " await page.reload();\n";
|
||||
Record.events += ` await page.waitForLoadState("load");\n`;
|
||||
|
||||
Record.events = [
|
||||
{
|
||||
type: "header",
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
localStorage: lsSnapshot,
|
||||
delay: 0,
|
||||
},
|
||||
];
|
||||
// Set up the events
|
||||
Record.timestamp = performance.now();
|
||||
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
@@ -60,17 +42,6 @@ export class Record {
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
|
||||
try {
|
||||
const canvases = Array.from(
|
||||
document.querySelectorAll("canvas"),
|
||||
) as HTMLCanvasElement[];
|
||||
for (const c of canvases) {
|
||||
c.addEventListener("mousemove", this.onMouseMove);
|
||||
c.addEventListener("mousedown", this.onMouseDown);
|
||||
c.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
public static stop() {
|
||||
@@ -79,18 +50,6 @@ export class Record {
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
|
||||
// Remove listeners from any canvases we attached to
|
||||
try {
|
||||
const canvases = Array.from(
|
||||
document.querySelectorAll("canvas"),
|
||||
) as HTMLCanvasElement[];
|
||||
for (const c of canvases) {
|
||||
c.removeEventListener("mousemove", this.onMouseMove);
|
||||
c.removeEventListener("mousedown", this.onMouseDown);
|
||||
c.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/// Displays a window as an absolutely positioned DIV with the generated
|
||||
@@ -107,7 +66,7 @@ export class Record {
|
||||
div.style.zIndex = "10000";
|
||||
|
||||
const pre = document.createElement("pre");
|
||||
pre.textContent = JSON.stringify(this.events, null, 2);
|
||||
pre.textContent = this.events;
|
||||
// avoid overlap with the close button and limit height for large event dumps
|
||||
pre.style.marginTop = "18px";
|
||||
pre.style.maxHeight = "60vh";
|
||||
@@ -183,12 +142,10 @@ export class Record {
|
||||
const delay = now - Record.timestamp;
|
||||
Record.timestamp = now;
|
||||
|
||||
Record.events.push({
|
||||
type: "mouse.move",
|
||||
x: event.clientX,
|
||||
y: event.clientY,
|
||||
delay,
|
||||
});
|
||||
if (delay > 0) {
|
||||
Record.events += ` await page.waitForTimeout(${delay});\n`;
|
||||
}
|
||||
Record.events += ` await page.mouse.move(${event.clientX}, ${event.clientY});\n`;
|
||||
}
|
||||
|
||||
private static onMouseDown(event: MouseEvent) {
|
||||
@@ -196,12 +153,12 @@ export class Record {
|
||||
const delay = now - Record.timestamp;
|
||||
Record.timestamp = now;
|
||||
|
||||
Record.events.push({
|
||||
type: "mouse.down",
|
||||
button:
|
||||
event.button === 0 ? "left" : event.button === 1 ? "middle" : "right",
|
||||
delay,
|
||||
});
|
||||
if (delay > 0) {
|
||||
Record.events += ` await page.waitForTimeout(${delay});\n`;
|
||||
}
|
||||
const button =
|
||||
event.button === 0 ? "left" : event.button === 1 ? "middle" : "right";
|
||||
Record.events += ` await page.mouse.down({ button: "${button}" });\n`;
|
||||
}
|
||||
|
||||
private static onMouseUp(event: MouseEvent) {
|
||||
@@ -209,12 +166,15 @@ export class Record {
|
||||
const delay = now - Record.timestamp;
|
||||
Record.timestamp = now;
|
||||
|
||||
Record.events.push({
|
||||
type: "mouse.up",
|
||||
button:
|
||||
event.button === 0 ? "left" : event.button === 1 ? "middle" : "right",
|
||||
delay,
|
||||
});
|
||||
if (delay > 0) {
|
||||
Record.events += ` await page.waitForTimeout(${delay});\n`;
|
||||
}
|
||||
const button =
|
||||
event.button === 0 ? "left" : event.button === 1 ? "middle" : "right";
|
||||
Record.events += ` await page.mouse.down({ button: "${button}" });\n`;
|
||||
|
||||
Record.events +=
|
||||
" await expect(page).toHaveScreenshot({ maxDiffPixels: 100 });\n";
|
||||
}
|
||||
|
||||
private static onKeyDown(event: KeyboardEvent) {
|
||||
@@ -222,11 +182,10 @@ export class Record {
|
||||
const delay = now - Record.timestamp;
|
||||
Record.timestamp = now;
|
||||
|
||||
Record.events.push({
|
||||
type: "keyboard.down",
|
||||
key: event.key,
|
||||
delay,
|
||||
});
|
||||
if (delay > 0) {
|
||||
Record.events += ` await page.waitForTimeout(${delay});\n`;
|
||||
}
|
||||
Record.events += ` await page.keyboard.down("${event.key}");\n`;
|
||||
}
|
||||
|
||||
private static onKeyUp(event: KeyboardEvent) {
|
||||
@@ -234,11 +193,13 @@ export class Record {
|
||||
const delay = now - Record.timestamp;
|
||||
Record.timestamp = now;
|
||||
|
||||
Record.events.push({
|
||||
type: "keyboard.up",
|
||||
key: event.key,
|
||||
delay,
|
||||
});
|
||||
if (delay > 0) {
|
||||
Record.events += ` await page.waitForTimeout(${delay});\n`;
|
||||
}
|
||||
Record.events += ` await page.keyboard.up("${event.key}");\n`;
|
||||
|
||||
Record.events +=
|
||||
" await expect(page).toHaveScreenshot({ maxDiffPixels: 100 });\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
@@ -1,77 +0,0 @@
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
|
||||
import type { PlaybackEvent } from "../../record";
|
||||
|
||||
export async function playbackEvents(page: Page, events: PlaybackEvent[]) {
|
||||
const mask = [
|
||||
page.getByRole("button", { name: "Share" }),
|
||||
page.getByTitle("Library").locator("div"),
|
||||
];
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
page.setDefaultTimeout(100000);
|
||||
for (const event of events) {
|
||||
// Handle header event specially: set viewport and localStorage before playback
|
||||
if (event.type === "header") {
|
||||
// set viewport to recorded size
|
||||
await page.setViewportSize({ width: event.width, height: event.height });
|
||||
width = event.width;
|
||||
height = event.height;
|
||||
|
||||
// apply localStorage snapshot in page context
|
||||
if (event.localStorage) {
|
||||
await page.evaluate((ls: Record<string, string>) => {
|
||||
try {
|
||||
for (const k in ls) {
|
||||
if (Object.prototype.hasOwnProperty.call(ls, k)) {
|
||||
localStorage.setItem(k, ls[k]);
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}, event.localStorage as Record<string, string>);
|
||||
}
|
||||
|
||||
await page.reload();
|
||||
|
||||
await page.waitForLoadState("load");
|
||||
|
||||
// header has no further action
|
||||
continue;
|
||||
}
|
||||
// wait the recorded delay before dispatching the event
|
||||
const ms = Math.max(0, Math.round((event as any).delay ?? 0));
|
||||
if (ms > 0) {
|
||||
await page.waitForTimeout(ms);
|
||||
}
|
||||
switch (event.type) {
|
||||
case "mouse.move":
|
||||
// Simulate mouse movement
|
||||
if (event.x < 0 || event.x > width || event.y < 0 || event.y > height) {
|
||||
break;
|
||||
}
|
||||
await page.mouse.move(event.x, event.y);
|
||||
break;
|
||||
case "mouse.down":
|
||||
// Simulate mouse button down
|
||||
await page.mouse.down({ button: event.button });
|
||||
break;
|
||||
case "mouse.up":
|
||||
// Simulate mouse button up
|
||||
await page.mouse.up({ button: event.button });
|
||||
await expect(page).toHaveScreenshot({ maxDiffPixels: 100, mask });
|
||||
break;
|
||||
case "keyboard.down":
|
||||
// Simulate key down
|
||||
await page.keyboard.down(event.key);
|
||||
break;
|
||||
case "keyboard.up":
|
||||
// Simulate key up
|
||||
await page.keyboard.up(event.key);
|
||||
await expect(page).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
mask,
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user