|
1 | | -import type { Locator, Page } from '@playwright/test' |
| 1 | +import { expect } from '@playwright/test' |
| 2 | +import type { Locator } from '@playwright/test' |
2 | 3 |
|
3 | | -import type { NodeReference } from './litegraphUtils' |
4 | | - |
5 | | -/** |
6 | | - * VueNodeFixture provides Vue-specific testing utilities for interacting with |
7 | | - * Vue node components. It bridges the gap between litegraph node references |
8 | | - * and Vue UI components. |
9 | | - */ |
| 4 | +/** DOM-centric helper for a single Vue-rendered node on the canvas. */ |
10 | 5 | export class VueNodeFixture { |
11 | | - constructor( |
12 | | - private readonly nodeRef: NodeReference, |
13 | | - private readonly page: Page |
14 | | - ) {} |
| 6 | + constructor(private readonly locator: Locator) {} |
15 | 7 |
|
16 | | - /** |
17 | | - * Get the node's header element using data-testid |
18 | | - */ |
19 | | - async getHeader(): Promise<Locator> { |
20 | | - const nodeId = this.nodeRef.id |
21 | | - return this.page.locator(`[data-testid="node-header-${nodeId}"]`) |
| 8 | + get header(): Locator { |
| 9 | + return this.locator.locator('[data-testid^="node-header-"]') |
22 | 10 | } |
23 | 11 |
|
24 | | - /** |
25 | | - * Get the node's title element |
26 | | - */ |
27 | | - async getTitleElement(): Promise<Locator> { |
28 | | - const header = await this.getHeader() |
29 | | - return header.locator('[data-testid="node-title"]') |
| 12 | + get title(): Locator { |
| 13 | + return this.locator.locator('[data-testid="node-title"]') |
30 | 14 | } |
31 | 15 |
|
32 | | - /** |
33 | | - * Get the current title text |
34 | | - */ |
35 | | - async getTitle(): Promise<string> { |
36 | | - const titleElement = await this.getTitleElement() |
37 | | - return (await titleElement.textContent()) || '' |
| 16 | + get titleInput(): Locator { |
| 17 | + return this.locator.locator('[data-testid="node-title-input"]') |
38 | 18 | } |
39 | 19 |
|
40 | | - /** |
41 | | - * Set a new title by double-clicking and entering text |
42 | | - */ |
43 | | - async setTitle(newTitle: string): Promise<void> { |
44 | | - const titleElement = await this.getTitleElement() |
45 | | - await titleElement.dblclick() |
46 | | - |
47 | | - const input = (await this.getHeader()).locator( |
48 | | - '[data-testid="node-title-input"]' |
49 | | - ) |
50 | | - await input.fill(newTitle) |
51 | | - await input.press('Enter') |
| 20 | + get body(): Locator { |
| 21 | + return this.locator.locator('[data-testid^="node-body-"]') |
52 | 22 | } |
53 | 23 |
|
54 | | - /** |
55 | | - * Cancel title editing |
56 | | - */ |
57 | | - async cancelTitleEdit(): Promise<void> { |
58 | | - const titleElement = await this.getTitleElement() |
59 | | - await titleElement.dblclick() |
60 | | - |
61 | | - const input = (await this.getHeader()).locator( |
62 | | - '[data-testid="node-title-input"]' |
63 | | - ) |
64 | | - await input.press('Escape') |
| 24 | + get collapseButton(): Locator { |
| 25 | + return this.locator.locator('[data-testid="node-collapse-button"]') |
65 | 26 | } |
66 | 27 |
|
67 | | - /** |
68 | | - * Check if the title is currently being edited |
69 | | - */ |
70 | | - async isEditingTitle(): Promise<boolean> { |
71 | | - const header = await this.getHeader() |
72 | | - const input = header.locator('[data-testid="node-title-input"]') |
73 | | - return await input.isVisible() |
| 28 | + get collapseIcon(): Locator { |
| 29 | + return this.collapseButton.locator('i') |
74 | 30 | } |
75 | 31 |
|
76 | | - /** |
77 | | - * Get the collapse/expand button |
78 | | - */ |
79 | | - async getCollapseButton(): Promise<Locator> { |
80 | | - const header = await this.getHeader() |
81 | | - return header.locator('[data-testid="node-collapse-button"]') |
| 32 | + get root(): Locator { |
| 33 | + return this.locator |
82 | 34 | } |
83 | 35 |
|
84 | | - /** |
85 | | - * Toggle the node's collapsed state |
86 | | - */ |
87 | | - async toggleCollapse(): Promise<void> { |
88 | | - const button = await this.getCollapseButton() |
89 | | - await button.click() |
| 36 | + async getTitle(): Promise<string> { |
| 37 | + return (await this.title.textContent()) ?? '' |
90 | 38 | } |
91 | 39 |
|
92 | | - /** |
93 | | - * Get the collapse icon element |
94 | | - */ |
95 | | - async getCollapseIcon(): Promise<Locator> { |
96 | | - const button = await this.getCollapseButton() |
97 | | - return button.locator('i') |
| 40 | + async setTitle(value: string): Promise<void> { |
| 41 | + await this.header.dblclick() |
| 42 | + const input = this.titleInput |
| 43 | + await expect(input).toBeVisible() |
| 44 | + await input.fill(value) |
| 45 | + await input.press('Enter') |
98 | 46 | } |
99 | 47 |
|
100 | | - /** |
101 | | - * Get the collapse icon's CSS classes |
102 | | - */ |
103 | | - async getCollapseIconClass(): Promise<string> { |
104 | | - const icon = await this.getCollapseIcon() |
105 | | - return (await icon.getAttribute('class')) || '' |
| 48 | + async cancelTitleEdit(): Promise<void> { |
| 49 | + await this.header.dblclick() |
| 50 | + const input = this.titleInput |
| 51 | + await expect(input).toBeVisible() |
| 52 | + await input.press('Escape') |
106 | 53 | } |
107 | 54 |
|
108 | | - /** |
109 | | - * Check if the collapse button is visible |
110 | | - */ |
111 | | - async isCollapseButtonVisible(): Promise<boolean> { |
112 | | - const button = await this.getCollapseButton() |
113 | | - return await button.isVisible() |
| 55 | + async toggleCollapse(): Promise<void> { |
| 56 | + await this.collapseButton.click() |
114 | 57 | } |
115 | 58 |
|
116 | | - /** |
117 | | - * Get the node's body/content element |
118 | | - */ |
119 | | - async getBody(): Promise<Locator> { |
120 | | - const nodeId = this.nodeRef.id |
121 | | - return this.page.locator(`[data-testid="node-body-${nodeId}"]`) |
| 59 | + async getCollapseIconClass(): Promise<string> { |
| 60 | + return (await this.collapseIcon.getAttribute('class')) ?? '' |
122 | 61 | } |
123 | 62 |
|
124 | | - /** |
125 | | - * Check if the node body is visible (not collapsed) |
126 | | - */ |
127 | | - async isBodyVisible(): Promise<boolean> { |
128 | | - const body = await this.getBody() |
129 | | - return await body.isVisible() |
| 63 | + boundingBox(): ReturnType<Locator['boundingBox']> { |
| 64 | + return this.locator.boundingBox() |
130 | 65 | } |
131 | 66 | } |
0 commit comments