Skip to content

Commit 0797716

Browse files
Copilotkobenguyent
andauthored
feat: Introduce CodeceptJS WebElement Class to mirror chosen helpers' element instance (#5091)
* Initial plan * Changes before error encountered Co-authored-by: kobenguyent <[email protected]> * Fix helper tests to expect WebElement instances instead of native elements Co-authored-by: kobenguyent <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: kobenguyent <[email protected]>
1 parent 2047cf2 commit 0797716

File tree

9 files changed

+1341
-9
lines changed

9 files changed

+1341
-9
lines changed

docs/WebElement.md

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# WebElement API
2+
3+
The WebElement class provides a unified interface for interacting with elements across different CodeceptJS helpers (Playwright, WebDriver, Puppeteer). It wraps native element instances and provides consistent methods regardless of the underlying helper.
4+
5+
## Basic Usage
6+
7+
```javascript
8+
// Get WebElement instances from any helper
9+
const element = await I.grabWebElement('#button')
10+
const elements = await I.grabWebElements('.items')
11+
12+
// Use consistent API across all helpers
13+
const text = await element.getText()
14+
const isVisible = await element.isVisible()
15+
await element.click()
16+
await element.type('Hello World')
17+
18+
// Find child elements
19+
const childElement = await element.$('.child-selector')
20+
const childElements = await element.$$('.child-items')
21+
```
22+
23+
## API Methods
24+
25+
### Element Properties
26+
27+
#### `getText()`
28+
29+
Get the text content of the element.
30+
31+
```javascript
32+
const text = await element.getText()
33+
console.log(text) // "Button Text"
34+
```
35+
36+
#### `getAttribute(name)`
37+
38+
Get the value of a specific attribute.
39+
40+
```javascript
41+
const id = await element.getAttribute('id')
42+
const className = await element.getAttribute('class')
43+
```
44+
45+
#### `getProperty(name)`
46+
47+
Get the value of a JavaScript property.
48+
49+
```javascript
50+
const value = await element.getProperty('value')
51+
const checked = await element.getProperty('checked')
52+
```
53+
54+
#### `getInnerHTML()`
55+
56+
Get the inner HTML content of the element.
57+
58+
```javascript
59+
const html = await element.getInnerHTML()
60+
console.log(html) // "<span>Content</span>"
61+
```
62+
63+
#### `getValue()`
64+
65+
Get the value of input elements.
66+
67+
```javascript
68+
const inputValue = await element.getValue()
69+
```
70+
71+
### Element State
72+
73+
#### `isVisible()`
74+
75+
Check if the element is visible.
76+
77+
```javascript
78+
const visible = await element.isVisible()
79+
if (visible) {
80+
console.log('Element is visible')
81+
}
82+
```
83+
84+
#### `isEnabled()`
85+
86+
Check if the element is enabled (not disabled).
87+
88+
```javascript
89+
const enabled = await element.isEnabled()
90+
if (enabled) {
91+
await element.click()
92+
}
93+
```
94+
95+
#### `exists()`
96+
97+
Check if the element exists in the DOM.
98+
99+
```javascript
100+
const exists = await element.exists()
101+
if (exists) {
102+
console.log('Element exists')
103+
}
104+
```
105+
106+
#### `getBoundingBox()`
107+
108+
Get the element's bounding box (position and size).
109+
110+
```javascript
111+
const box = await element.getBoundingBox()
112+
console.log(box) // { x: 100, y: 200, width: 150, height: 50 }
113+
```
114+
115+
### Element Interactions
116+
117+
#### `click(options)`
118+
119+
Click the element.
120+
121+
```javascript
122+
await element.click()
123+
// With options (Playwright/Puppeteer)
124+
await element.click({ button: 'right' })
125+
```
126+
127+
#### `type(text, options)`
128+
129+
Type text into the element.
130+
131+
```javascript
132+
await element.type('Hello World')
133+
// With options (Playwright/Puppeteer)
134+
await element.type('Hello', { delay: 100 })
135+
```
136+
137+
### Child Element Search
138+
139+
#### `$(locator)`
140+
141+
Find the first child element matching the locator.
142+
143+
```javascript
144+
const childElement = await element.$('.child-class')
145+
if (childElement) {
146+
await childElement.click()
147+
}
148+
```
149+
150+
#### `$$(locator)`
151+
152+
Find all child elements matching the locator.
153+
154+
```javascript
155+
const childElements = await element.$$('.child-items')
156+
for (const child of childElements) {
157+
const text = await child.getText()
158+
console.log(text)
159+
}
160+
```
161+
162+
### Native Access
163+
164+
#### `getNativeElement()`
165+
166+
Get the original native element instance.
167+
168+
```javascript
169+
const nativeElement = element.getNativeElement()
170+
// For Playwright: ElementHandle
171+
// For WebDriver: WebElement
172+
// For Puppeteer: ElementHandle
173+
```
174+
175+
#### `getHelper()`
176+
177+
Get the helper instance that created this WebElement.
178+
179+
```javascript
180+
const helper = element.getHelper()
181+
console.log(helper.constructor.name) // "Playwright", "WebDriver", or "Puppeteer"
182+
```
183+
184+
## Locator Support
185+
186+
The `$()` and `$$()` methods support various locator formats:
187+
188+
```javascript
189+
// CSS selectors
190+
await element.$('.class-name')
191+
await element.$('#element-id')
192+
193+
// CodeceptJS locator objects
194+
await element.$({ css: '.my-class' })
195+
await element.$({ xpath: '//div[@class="test"]' })
196+
await element.$({ id: 'element-id' })
197+
await element.$({ name: 'field-name' })
198+
await element.$({ className: 'my-class' })
199+
```
200+
201+
## Cross-Helper Compatibility
202+
203+
The same WebElement code works across all supported helpers:
204+
205+
```javascript
206+
// This code works identically with Playwright, WebDriver, and Puppeteer
207+
const loginForm = await I.grabWebElement('#login-form')
208+
const usernameField = await loginForm.$('[name="username"]')
209+
const passwordField = await loginForm.$('[name="password"]')
210+
const submitButton = await loginForm.$('button[type="submit"]')
211+
212+
await usernameField.type('[email protected]')
213+
await passwordField.type('password123')
214+
await submitButton.click()
215+
```
216+
217+
## Migration from Native Elements
218+
219+
If you were previously using native elements, you can gradually migrate:
220+
221+
```javascript
222+
// Old way - helper-specific
223+
const nativeElements = await I.grabWebElements('.items')
224+
// Different API for each helper
225+
226+
// New way - unified
227+
const webElements = await I.grabWebElements('.items')
228+
// Same API across all helpers
229+
230+
// Backward compatibility
231+
const nativeElement = webElements[0].getNativeElement()
232+
// Use native methods if needed
233+
```
234+
235+
## Error Handling
236+
237+
WebElement methods will throw appropriate errors when operations fail:
238+
239+
```javascript
240+
try {
241+
const element = await I.grabWebElement('#nonexistent')
242+
} catch (error) {
243+
console.log('Element not found')
244+
}
245+
246+
try {
247+
await element.click()
248+
} catch (error) {
249+
console.log('Click failed:', error.message)
250+
}
251+
```

0 commit comments

Comments
 (0)