/** * Vue Node Test Helpers */ import type { Locator, Page } from '@playwright/test' export class VueNodeHelpers { constructor(private page: Page) {} /** * Get locator for all Vue node components in the DOM */ get nodes(): Locator { return this.page.locator('[data-node-id]') } /** * Get locator for a Vue node by its NodeId */ getNodeLocator(nodeId: string): Locator { return this.page.locator(`[data-node-id="${nodeId}"]`) } /** * Get locator for selected Vue node components (using visual selection indicators) */ get selectedNodes(): Locator { return this.page.locator('[data-node-id].outline-node-component-outline') } /** * Get locator for a Vue node by the node's title (displayed name in the header) */ getNodeByTitle(title: string): Locator { return this.page.locator(`[data-node-id]`).filter({ hasText: title }) } /** * Get total count of Vue nodes in the DOM */ async getNodeCount(): Promise { return await this.nodes.count() } /** * Get count of selected Vue nodes */ async getSelectedNodeCount(): Promise { return await this.selectedNodes.count() } /** * Get all Vue node IDs currently in the DOM */ async getNodeIds(): Promise { return await this.nodes.evaluateAll((nodes) => nodes .map((n) => n.getAttribute('data-node-id')) .filter((id): id is string => id !== null) ) } /** * Select a specific Vue node by ID */ async selectNode(nodeId: string): Promise { await this.page.locator(`[data-node-id="${nodeId}"]`).click() } /** * Select multiple Vue nodes by IDs using Ctrl+click */ async selectNodes(nodeIds: string[]): Promise { if (nodeIds.length === 0) return // Select first node normally await this.selectNode(nodeIds[0]) // Add additional nodes with Ctrl+click for (let i = 1; i < nodeIds.length; i++) { await this.page.locator(`[data-node-id="${nodeIds[i]}"]`).click({ modifiers: ['Control'] }) } } /** * Clear all selections by clicking empty space */ async clearSelection(): Promise { await this.page.mouse.click(50, 50) } /** * Delete selected Vue nodes using Delete key */ async deleteSelected(): Promise { await this.page.locator('#graph-canvas').focus() await this.page.keyboard.press('Delete') } /** * Delete selected Vue nodes using Backspace key */ async deleteSelectedWithBackspace(): Promise { await this.page.locator('#graph-canvas').focus() await this.page.keyboard.press('Backspace') } /** * Wait for Vue nodes to be rendered */ async waitForNodes(expectedCount?: number): Promise { if (expectedCount !== undefined) { await this.page.waitForFunction( (count) => document.querySelectorAll('[data-node-id]').length >= count, expectedCount ) } else { await this.page.waitForSelector('[data-node-id]') } } /** * Get a specific widget by node title and widget name */ getWidgetByName(nodeTitle: string, widgetName: string): Locator { return this.getNodeByTitle(nodeTitle).locator( `_vue=[widget.name="${widgetName}"]` ) } /** * Get controls for input number widgets (increment/decrement buttons and input) */ getInputNumberControls(widget: Locator) { return { input: widget.locator('input'), incrementButton: widget.locator('button').first(), decrementButton: widget.locator('button').last() } } }