Skip to content

Commit c072852

Browse files
committed
synchronized with docs
1 parent b46a6c1 commit c072852

24 files changed

+2472
-370
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('user@example.com')
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+
```

docs/basics.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,29 @@ Like in Mocha you can use `x` and `only` to skip tests or to run a single test.
961961
- `Scenario.only` - executes only the current test
962962
- `xFeature` - skips current suite <Badge text="Since 2.6.6" type="warning"/>
963963
- `Feature.skip` - skips the current suite <Badge text="Since 2.6.6" type="warning"/>
964+
- `Feature.only` - executes only the current suite <Badge text="Since 3.7.5" type="warning"/>
965+
966+
When using `Feature.only`, only scenarios within that feature will be executed:
967+
968+
```js
969+
Feature.only('My Important Feature')
970+
971+
Scenario('test something', ({ I }) => {
972+
I.amOnPage('https://github.com')
973+
I.see('GitHub')
974+
})
975+
976+
Scenario('test something else', ({ I }) => {
977+
I.amOnPage('https://github.com')
978+
I.see('GitHub')
979+
})
980+
981+
Feature('Another Feature') // This will be skipped
982+
983+
Scenario('will not run', ({ I }) => {
984+
// This scenario will be skipped
985+
})
986+
```
964987
965988
## Todo Test
966989

docs/build/JSONResponse.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@ class JSONResponse extends Helper {
7272
if (!this.helpers[this.options.requestHelper]) {
7373
throw new Error(`Error setting JSONResponse, helper ${this.options.requestHelper} is not enabled in config, helpers: ${Object.keys(this.helpers)}`)
7474
}
75-
// connect to REST helper
75+
const origOnResponse = this.helpers[this.options.requestHelper].config.onResponse;
7676
this.helpers[this.options.requestHelper].config.onResponse = response => {
77-
this.response = response
78-
}
77+
this.response = response;
78+
if (typeof origOnResponse === 'function') origOnResponse(response);
79+
};
7980
}
8081

8182
_before() {
@@ -349,7 +350,25 @@ class JSONResponse extends Helper {
349350
for (const key in expected) {
350351
assert(key in actual, `Key "${key}" not found in ${JSON.stringify(actual)}`)
351352
if (typeof expected[key] === 'object' && expected[key] !== null) {
352-
this._assertContains(actual[key], expected[key])
353+
if (Array.isArray(expected[key])) {
354+
// Handle array comparison: each expected element should have a match in actual array
355+
assert(Array.isArray(actual[key]), `Expected array for key "${key}", but got ${typeof actual[key]}`)
356+
for (const expectedItem of expected[key]) {
357+
let found = false
358+
for (const actualItem of actual[key]) {
359+
try {
360+
this._assertContains(actualItem, expectedItem)
361+
found = true
362+
break
363+
} catch (err) {
364+
continue
365+
}
366+
}
367+
assert(found, `No matching element found in array for ${JSON.stringify(expectedItem)}`)
368+
}
369+
} else {
370+
this._assertContains(actual[key], expected[key])
371+
}
353372
} else {
354373
assert.deepStrictEqual(actual[key], expected[key], `Values for key "${key}" don't match`)
355374
}

docs/build/Mochawesome.js

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,20 @@ class Mochawesome extends Helper {
3737
}
3838

3939
_test(test) {
40-
currentTest = { test }
40+
// If this is a retried test, we want to add context to the retried test
41+
// but also potentially preserve context from the original test
42+
const originalTest = test.retriedTest && test.retriedTest()
43+
if (originalTest) {
44+
// This is a retried test - use the retried test for context
45+
currentTest = { test }
46+
47+
// Optionally copy context from original test if it exists
48+
// Note: mochawesome context is stored in test.ctx, but we need to be careful
49+
// not to break the mocha context structure
50+
} else {
51+
// Normal test (not a retry)
52+
currentTest = { test }
53+
}
4154
}
4255

4356
_failed(test) {
@@ -64,7 +77,16 @@ class Mochawesome extends Helper {
6477

6578
addMochawesomeContext(context) {
6679
if (currentTest === '') currentTest = { test: currentSuite.ctx.test }
67-
return this._addContext(currentTest, context)
80+
81+
// For retried tests, make sure we're adding context to the current (retried) test
82+
// not the original test
83+
let targetTest = currentTest
84+
if (currentTest.test && currentTest.test.retriedTest && currentTest.test.retriedTest()) {
85+
// This test has been retried, make sure we're using the current test for context
86+
targetTest = { test: currentTest.test }
87+
}
88+
89+
return this._addContext(targetTest, context)
6890
}
6991
}
7092

0 commit comments

Comments
 (0)