258 lines
7.9 KiB
JavaScript
258 lines
7.9 KiB
JavaScript
/**
|
|
* Canvas interaction utilities for UI testing
|
|
*/
|
|
|
|
/**
|
|
* Reset canvas scroll/pan to origin
|
|
*/
|
|
export async function resetCanvasView() {
|
|
await browser.execute(function() {
|
|
if (window.context && window.context.stageWidget) {
|
|
window.context.stageWidget.offsetX = 0;
|
|
window.context.stageWidget.offsetY = 0;
|
|
// Trigger redraw to apply the reset
|
|
if (window.context.updateUI) {
|
|
window.context.updateUI();
|
|
}
|
|
}
|
|
});
|
|
await browser.pause(100); // Wait for canvas to reset
|
|
}
|
|
|
|
/**
|
|
* Click at specific coordinates on the canvas
|
|
* @param {number} x - X coordinate relative to canvas
|
|
* @param {number} y - Y coordinate relative to canvas
|
|
*/
|
|
export async function clickCanvas(x, y) {
|
|
await resetCanvasView();
|
|
await browser.clickCanvas(x, y);
|
|
await browser.pause(100); // Wait for render
|
|
}
|
|
|
|
/**
|
|
* Drag from one point to another on the canvas
|
|
* @param {number} fromX - Starting X coordinate
|
|
* @param {number} fromY - Starting Y coordinate
|
|
* @param {number} toX - Ending X coordinate
|
|
* @param {number} toY - Ending Y coordinate
|
|
*/
|
|
export async function dragCanvas(fromX, fromY, toX, toY) {
|
|
await resetCanvasView();
|
|
await browser.dragCanvas(fromX, fromY, toX, toY);
|
|
await browser.pause(200); // Wait for render
|
|
}
|
|
|
|
/**
|
|
* Draw a rectangle on the canvas
|
|
* @param {number} x - Top-left X coordinate
|
|
* @param {number} y - Top-left Y coordinate
|
|
* @param {number} width - Rectangle width
|
|
* @param {number} height - Rectangle height
|
|
* @param {boolean} filled - Whether to fill the shape (default: true)
|
|
* @param {string} color - Fill color in hex format (e.g., '#ff0000')
|
|
*/
|
|
export async function drawRectangle(x, y, width, height, filled = true, color = null) {
|
|
// Select the rectangle tool
|
|
await selectTool('rectangle');
|
|
|
|
// Set fill option and color if provided
|
|
await browser.execute((filled, color) => {
|
|
if (window.context) {
|
|
window.context.fillShape = filled;
|
|
if (color) {
|
|
window.context.fillStyle = color;
|
|
}
|
|
}
|
|
}, filled, color);
|
|
|
|
// Draw by dragging from start to end point
|
|
await dragCanvas(x, y, x + width, y + height);
|
|
|
|
// Wait for shape to be created
|
|
await browser.pause(300);
|
|
}
|
|
|
|
/**
|
|
* Draw an ellipse on the canvas
|
|
* @param {number} x - Top-left X coordinate
|
|
* @param {number} y - Top-left Y coordinate
|
|
* @param {number} width - Ellipse width
|
|
* @param {number} height - Ellipse height
|
|
* @param {boolean} filled - Whether to fill the shape (default: true)
|
|
*/
|
|
export async function drawEllipse(x, y, width, height, filled = true) {
|
|
// Select the ellipse tool
|
|
await selectTool('ellipse');
|
|
|
|
// Set fill option
|
|
await browser.execute((filled) => {
|
|
if (window.context) {
|
|
window.context.fillShape = filled;
|
|
}
|
|
}, filled);
|
|
|
|
// Draw by dragging from start to end point
|
|
await dragCanvas(x, y, x + width, y + height);
|
|
|
|
// Wait for shape to be created
|
|
await browser.pause(300);
|
|
}
|
|
|
|
/**
|
|
* Select a tool from the toolbar
|
|
* @param {string} toolName - Name of the tool ('rectangle', 'ellipse', 'brush', etc.)
|
|
*/
|
|
export async function selectTool(toolName) {
|
|
const toolButton = await browser.$(`[data-tool="${toolName}"]`);
|
|
await toolButton.click();
|
|
await browser.pause(100);
|
|
}
|
|
|
|
/**
|
|
* Select multiple shapes by dragging a selection box over them
|
|
* @param {Array<{x: number, y: number}>} points - Array of points representing shapes to select
|
|
*/
|
|
export async function selectMultipleShapes(points) {
|
|
// First, make sure we're in select mode
|
|
await selectTool('select');
|
|
|
|
// Calculate bounding box that encompasses all points
|
|
const minX = Math.min(...points.map(p => p.x)) - 10;
|
|
const minY = Math.min(...points.map(p => p.y)) - 10;
|
|
const maxX = Math.max(...points.map(p => p.x)) + 10;
|
|
const maxY = Math.max(...points.map(p => p.y)) + 10;
|
|
|
|
// Drag a selection box from top-left to bottom-right
|
|
await dragCanvas(minX, minY, maxX, maxY);
|
|
await browser.pause(200);
|
|
}
|
|
|
|
/**
|
|
* Use keyboard shortcut / menu action
|
|
* Since Tauri menu shortcuts don't reach the browser, we invoke actions directly
|
|
* @param {string} key - Key to press (e.g., 'g' for group)
|
|
* @param {boolean} withCtrl - Whether to hold Ctrl (ignored, kept for compatibility)
|
|
*/
|
|
export async function useKeyboardShortcut(key, withCtrl = true) {
|
|
if (key === 'g') {
|
|
// Call group action directly without serializing the whole function
|
|
await browser.execute('window.actions.group.create()');
|
|
}
|
|
|
|
await browser.pause(300); // Give time for the action to process
|
|
}
|
|
|
|
/**
|
|
* Get pixel color at specific coordinates (requires canvas access)
|
|
* @param {number} x - X coordinate
|
|
* @param {number} y - Y coordinate
|
|
* @returns {Promise<string>} Color in hex format
|
|
*/
|
|
export async function getPixelColor(x, y) {
|
|
const result = await browser.execute(function(x, y) {
|
|
const canvas = document.querySelector('canvas.stage');
|
|
if (!canvas) return null;
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
const imageData = ctx.getImageData(x, y, 1, 1);
|
|
const data = imageData.data;
|
|
|
|
// Convert to hex
|
|
const r = data[0].toString(16).padStart(2, '0');
|
|
const g = data[1].toString(16).padStart(2, '0');
|
|
const b = data[2].toString(16).padStart(2, '0');
|
|
|
|
return `#${r}${g}${b}`;
|
|
}, x, y);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check if a shape exists at given coordinates by checking if pixel is not background
|
|
* @param {number} x - X coordinate
|
|
* @param {number} y - Y coordinate
|
|
* @param {string} backgroundColor - Expected background color (default white)
|
|
* @returns {Promise<boolean>}
|
|
*/
|
|
export async function hasShapeAt(x, y, backgroundColor = '#ffffff') {
|
|
const color = await getPixelColor(x, y);
|
|
return color && color.toLowerCase() !== backgroundColor.toLowerCase();
|
|
}
|
|
|
|
/**
|
|
* Double-click at specific coordinates on the canvas (to enter group editing mode)
|
|
* @param {number} x - X coordinate relative to canvas
|
|
* @param {number} y - Y coordinate relative to canvas
|
|
*/
|
|
export async function doubleClickCanvas(x, y) {
|
|
const canvas = await browser.$('canvas.stage');
|
|
const location = await canvas.getLocation();
|
|
|
|
// Perform double-click using performActions
|
|
await browser.performActions([{
|
|
type: 'pointer',
|
|
id: 'mouse',
|
|
parameters: { pointerType: 'mouse' },
|
|
actions: [
|
|
{ type: 'pointerMove', duration: 0, x: location.x + x, y: location.y + y },
|
|
{ type: 'pointerDown', button: 0 },
|
|
{ type: 'pointerUp', button: 0 },
|
|
{ type: 'pause', duration: 50 },
|
|
{ type: 'pointerDown', button: 0 },
|
|
{ type: 'pointerUp', button: 0 }
|
|
]
|
|
}]);
|
|
|
|
await browser.pause(300); // Wait for group to be entered
|
|
}
|
|
|
|
/**
|
|
* Set the timeline playhead to a specific time
|
|
* @param {number} time - Time in seconds
|
|
*/
|
|
export async function setPlayheadTime(time) {
|
|
await browser.execute(function(timeValue) {
|
|
if (window.context && window.context.activeObject) {
|
|
// Set time on both the active object and timeline state
|
|
window.context.activeObject.currentTime = timeValue;
|
|
if (window.context.timelineWidget && window.context.timelineWidget.timelineState) {
|
|
window.context.timelineWidget.timelineState.currentTime = timeValue;
|
|
}
|
|
|
|
// Trigger timeline redraw to show updated playhead position
|
|
if (window.context.timelineWidget && window.context.timelineWidget.requestRedraw) {
|
|
window.context.timelineWidget.requestRedraw();
|
|
}
|
|
|
|
// Trigger stage redraw to show shapes at new time
|
|
if (window.context.updateUI) {
|
|
window.context.updateUI();
|
|
}
|
|
}
|
|
}, time);
|
|
await browser.pause(200);
|
|
}
|
|
|
|
/**
|
|
* Get the current playhead time
|
|
* @returns {Promise<number>} Current time in seconds
|
|
*/
|
|
export async function getPlayheadTime() {
|
|
return await browser.execute(function() {
|
|
if (window.context && window.context.activeObject) {
|
|
return window.context.activeObject.currentTime;
|
|
}
|
|
return 0;
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Add a keyframe at the current playhead position for selected shapes/objects
|
|
*/
|
|
export async function addKeyframe() {
|
|
await browser.execute('window.addKeyframeAtPlayhead && window.addKeyframeAtPlayhead()');
|
|
await browser.pause(200);
|
|
}
|