@progress/kendo-e2e 4.11.3 → 4.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -111
- package/dist/selenium/browser.d.ts +245 -4
- package/dist/selenium/browser.js +245 -4
- package/dist/selenium/browser.js.map +1 -1
- package/dist/selenium/conditions.d.ts +255 -0
- package/dist/selenium/conditions.js +251 -0
- package/dist/selenium/conditions.js.map +1 -1
- package/dist/selenium/driver-manager.d.ts +123 -0
- package/dist/selenium/driver-manager.js +118 -0
- package/dist/selenium/driver-manager.js.map +1 -1
- package/dist/selenium/electron-app.d.ts +32 -2
- package/dist/selenium/electron-app.js +32 -2
- package/dist/selenium/electron-app.js.map +1 -1
- package/dist/selenium/expect.d.ts +227 -0
- package/dist/selenium/expect.js +22 -0
- package/dist/selenium/expect.js.map +1 -1
- package/dist/selenium/web-app.d.ts +779 -9
- package/dist/selenium/web-app.js +778 -9
- package/dist/selenium/web-app.js.map +1 -1
- package/docs/API_REFERENCE.md +1309 -0
- package/docs/GETTING_STARTED.md +337 -0
- package/docs/PATTERNS.md +629 -0
- package/package.json +4 -5
package/dist/selenium/web-app.js
CHANGED
|
@@ -18,12 +18,71 @@ const conditions_1 = require("./conditions");
|
|
|
18
18
|
const expect_1 = require("./expect");
|
|
19
19
|
const rgb2hex_1 = __importDefault(require("rgb2hex"));
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
21
|
+
* Core class that provides automatic waiting and simplified interaction with web elements.
|
|
22
|
+
*
|
|
23
|
+
* **Key Features:**
|
|
24
|
+
* - **Automatic waiting** - No need to add manual waits, all methods wait for elements automatically
|
|
25
|
+
* - **Smart element location** - Accepts CSS selectors, By locators, or WebElement instances
|
|
26
|
+
* - **Fluent API** - Chain methods for readable test code
|
|
27
|
+
* - **Built-in retry logic** - Handles timing issues that cause flaky tests
|
|
28
|
+
*
|
|
29
|
+
* This eliminates the common Selenium pitfalls of StaleElementReferenceException and timing issues.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const app = new WebApp(driver);
|
|
34
|
+
*
|
|
35
|
+
* // Simple element interaction with automatic waiting
|
|
36
|
+
* await app.click('#submit-button');
|
|
37
|
+
* await app.type('#username', 'testuser');
|
|
38
|
+
*
|
|
39
|
+
* // Find and interact with elements
|
|
40
|
+
* const header = await app.find('.page-header');
|
|
41
|
+
* const text = await app.getText(header);
|
|
42
|
+
*
|
|
43
|
+
* // Wait for conditions
|
|
44
|
+
* await app.wait(EC.isVisible('#success-message'));
|
|
45
|
+
*
|
|
46
|
+
* // Modern expect API with retry logic
|
|
47
|
+
* await app.expect('#result').toHaveText('Success');
|
|
48
|
+
* await app.expect('.modal').toBeVisible();
|
|
49
|
+
* ```
|
|
22
50
|
*/
|
|
23
51
|
class WebApp {
|
|
52
|
+
/**
|
|
53
|
+
* Creates a WebApp instance wrapping a Selenium WebDriver.
|
|
54
|
+
*
|
|
55
|
+
* @param driver - Selenium WebDriver instance to wrap
|
|
56
|
+
*/
|
|
24
57
|
constructor(driver) {
|
|
25
58
|
this.driver = driver;
|
|
26
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Finds a single element with automatic waiting.
|
|
62
|
+
*
|
|
63
|
+
* **Automatically waits** up to the specified timeout for the element to appear in the DOM.
|
|
64
|
+
* This eliminates the need for manual waits and handles dynamic content loading.
|
|
65
|
+
*
|
|
66
|
+
* @param locator - CSS selector string or Selenium By locator
|
|
67
|
+
* @param options - Optional configuration
|
|
68
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
69
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
70
|
+
* @returns Promise resolving to the WebElement
|
|
71
|
+
* @throws Error if element is not found within the timeout period
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```typescript
|
|
75
|
+
* // Using CSS selector (recommended)
|
|
76
|
+
* const button = await app.find('#submit-btn');
|
|
77
|
+
* const firstItem = await app.find('.list-item');
|
|
78
|
+
*
|
|
79
|
+
* // Using Selenium By locator
|
|
80
|
+
* const element = await app.find(By.xpath('//div[@data-test="value"]'));
|
|
81
|
+
*
|
|
82
|
+
* // With custom timeout
|
|
83
|
+
* const slowElement = await app.find('#async-content', { timeout: 20000 });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
27
86
|
find(locator_1) {
|
|
28
87
|
return __awaiter(this, arguments, void 0, function* (locator, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
29
88
|
const errorMessage = `Failed to find element located by ${locator}.`;
|
|
@@ -35,6 +94,31 @@ class WebApp {
|
|
|
35
94
|
}
|
|
36
95
|
});
|
|
37
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Finds all matching elements without waiting.
|
|
99
|
+
*
|
|
100
|
+
* Returns an empty array if no elements are found. For scenarios where you need to wait
|
|
101
|
+
* for at least one element, use {@link findAllWithTimeout} instead.
|
|
102
|
+
*
|
|
103
|
+
* @param locator - CSS selector string or Selenium By locator
|
|
104
|
+
* @returns Promise resolving to array of WebElements (empty if none found)
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* // Get all list items
|
|
109
|
+
* const items = await app.findAll('.list-item');
|
|
110
|
+
* console.log(`Found ${items.length} items`);
|
|
111
|
+
*
|
|
112
|
+
* // Iterate over elements
|
|
113
|
+
* for (const item of items) {
|
|
114
|
+
* const text = await item.getText();
|
|
115
|
+
* console.log(text);
|
|
116
|
+
* }
|
|
117
|
+
*
|
|
118
|
+
* // Using By locator
|
|
119
|
+
* const buttons = await app.findAll(By.css('button'));
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
38
122
|
findAll(locator) {
|
|
39
123
|
return __awaiter(this, void 0, void 0, function* () {
|
|
40
124
|
if (locator instanceof selenium_webdriver_1.By) {
|
|
@@ -45,6 +129,27 @@ class WebApp {
|
|
|
45
129
|
}
|
|
46
130
|
});
|
|
47
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Finds all matching elements with automatic waiting for at least one element to appear.
|
|
134
|
+
*
|
|
135
|
+
* Waits until at least one element matching the locator appears, then returns all matching elements.
|
|
136
|
+
* Use this when you expect elements to load dynamically and need to ensure they're present.
|
|
137
|
+
*
|
|
138
|
+
* @param locator - CSS selector string or Selenium By locator
|
|
139
|
+
* @param options - Optional configuration
|
|
140
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
141
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
142
|
+
* @returns Promise resolving to array of WebElements
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* // Wait for search results to load
|
|
147
|
+
* const results = await app.findAllWithTimeout('.search-result');
|
|
148
|
+
*
|
|
149
|
+
* // With custom timeout for slow-loading content
|
|
150
|
+
* const items = await app.findAllWithTimeout('.async-item', { timeout: 15000 });
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
48
153
|
findAllWithTimeout(locator_1) {
|
|
49
154
|
return __awaiter(this, arguments, void 0, function* (locator, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
50
155
|
const byLocator = locator instanceof selenium_webdriver_1.By ? locator : selenium_webdriver_1.By.css(locator);
|
|
@@ -62,6 +167,34 @@ class WebApp {
|
|
|
62
167
|
return elements;
|
|
63
168
|
});
|
|
64
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Finds a child element within a parent element with automatic waiting.
|
|
172
|
+
*
|
|
173
|
+
* Useful for scoped element searches within a specific container. Automatically waits
|
|
174
|
+
* for both the parent and child elements to appear.
|
|
175
|
+
*
|
|
176
|
+
* @param rootElement - Parent element (WebElement, By locator, or CSS selector)
|
|
177
|
+
* @param locator - Child element selector (By locator or CSS selector)
|
|
178
|
+
* @param options - Optional configuration
|
|
179
|
+
* @param options.waitForChild - Whether to wait for child to appear (default: true)
|
|
180
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
181
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
182
|
+
* @returns Promise resolving to the child WebElement
|
|
183
|
+
* @throws Error if child element is not found within the timeout period
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* // Find button within a specific dialog
|
|
188
|
+
* const dialog = await app.find('.modal-dialog');
|
|
189
|
+
* const closeBtn = await app.findChild(dialog, '.close-button');
|
|
190
|
+
*
|
|
191
|
+
* // Or find child directly using parent selector
|
|
192
|
+
* const button = await app.findChild('.modal-dialog', 'button.submit');
|
|
193
|
+
*
|
|
194
|
+
* // Without waiting for child (if you know it exists)
|
|
195
|
+
* const child = await app.findChild(parent, '.child', { waitForChild: false });
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
65
198
|
findChild(rootElement_1, locator_1) {
|
|
66
199
|
return __awaiter(this, arguments, void 0, function* (rootElement, locator, { waitForChild = true, timeout = 10000, pollTimeout = 25 } = {}) {
|
|
67
200
|
if (!(rootElement instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -76,6 +209,36 @@ class WebApp {
|
|
|
76
209
|
: rootElement.findElement(selenium_webdriver_1.By.css(locator));
|
|
77
210
|
});
|
|
78
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Finds all child elements within a parent element with automatic waiting.
|
|
214
|
+
*
|
|
215
|
+
* Similar to {@link findChild} but returns all matching children instead of just the first one.
|
|
216
|
+
* Waits for at least one child to appear before returning.
|
|
217
|
+
*
|
|
218
|
+
* @param rootElement - Parent element (WebElement, By locator, or CSS selector)
|
|
219
|
+
* @param locator - Child elements selector (By locator or CSS selector)
|
|
220
|
+
* @param options - Optional configuration
|
|
221
|
+
* @param options.waitForChild - Whether to wait for at least one child to appear (default: true)
|
|
222
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
223
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
224
|
+
* @returns Promise resolving to array of child WebElements
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* // Find all rows in a specific table
|
|
229
|
+
* const table = await app.find('#data-table');
|
|
230
|
+
* const rows = await app.findChildren(table, 'tr');
|
|
231
|
+
*
|
|
232
|
+
* // Or find children directly using parent selector
|
|
233
|
+
* const items = await app.findChildren('.dropdown-menu', 'li');
|
|
234
|
+
*
|
|
235
|
+
* // Process all children
|
|
236
|
+
* for (const row of rows) {
|
|
237
|
+
* const text = await row.getText();
|
|
238
|
+
* console.log(text);
|
|
239
|
+
* }
|
|
240
|
+
* ```
|
|
241
|
+
*/
|
|
79
242
|
findChildren(rootElement_1, locator_1) {
|
|
80
243
|
return __awaiter(this, arguments, void 0, function* (rootElement, locator, { waitForChild = true, timeout = 10000, pollTimeout = 25 } = {}) {
|
|
81
244
|
if (!(rootElement instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -90,6 +253,40 @@ class WebApp {
|
|
|
90
253
|
: rootElement.findElements(selenium_webdriver_1.By.css(locator));
|
|
91
254
|
});
|
|
92
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Clicks an element with automatic waiting and retry logic.
|
|
258
|
+
*
|
|
259
|
+
* **Handles common click issues automatically:**
|
|
260
|
+
* - Waits for element to appear in DOM
|
|
261
|
+
* - Waits for element to be visible
|
|
262
|
+
* - Waits for element to be enabled
|
|
263
|
+
* - Retries if click fails initially
|
|
264
|
+
*
|
|
265
|
+
* This eliminates flaky tests caused by timing issues.
|
|
266
|
+
*
|
|
267
|
+
* @param element - Element to click (WebElement, By locator, or CSS selector)
|
|
268
|
+
* @param options - Optional configuration
|
|
269
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
270
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
271
|
+
* @returns Promise that resolves when click succeeds
|
|
272
|
+
* @throws Error if element cannot be clicked within timeout
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```typescript
|
|
276
|
+
* // Simple click using CSS selector
|
|
277
|
+
* await app.click('#submit-button');
|
|
278
|
+
*
|
|
279
|
+
* // Click with custom timeout
|
|
280
|
+
* await app.click('.slow-loading-btn', { timeout: 15000 });
|
|
281
|
+
*
|
|
282
|
+
* // Click using By locator
|
|
283
|
+
* await app.click(By.xpath('//button[text()="Submit"]'));
|
|
284
|
+
*
|
|
285
|
+
* // Click a previously found element
|
|
286
|
+
* const button = await app.find('#my-button');
|
|
287
|
+
* await app.click(button);
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
93
290
|
click(element_1) {
|
|
94
291
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
95
292
|
try {
|
|
@@ -114,6 +311,33 @@ class WebApp {
|
|
|
114
311
|
}
|
|
115
312
|
});
|
|
116
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Moves the mouse cursor over an element (hover action).
|
|
316
|
+
*
|
|
317
|
+
* Useful for testing hover states, tooltips, dropdown menus, and other hover-triggered UI.
|
|
318
|
+
* Automatically waits for the element to be present before hovering.
|
|
319
|
+
*
|
|
320
|
+
* @param element - Element to hover over (WebElement, By locator, or CSS selector)
|
|
321
|
+
* @param options - Optional configuration
|
|
322
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
323
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
324
|
+
* @returns Promise that resolves when hover completes
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```typescript
|
|
328
|
+
* // Hover to reveal dropdown menu
|
|
329
|
+
* await app.hover('.menu-item');
|
|
330
|
+
* await app.click('.submenu-option');
|
|
331
|
+
*
|
|
332
|
+
* // Hover to show tooltip
|
|
333
|
+
* await app.hover('#info-icon');
|
|
334
|
+
* const tooltip = await app.find('.tooltip');
|
|
335
|
+
* const text = await app.getText(tooltip);
|
|
336
|
+
*
|
|
337
|
+
* // Hover on element found with By locator
|
|
338
|
+
* await app.hover(By.css('[data-test="hover-target"]'));
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
117
341
|
hover(element_1) {
|
|
118
342
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
119
343
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -123,6 +347,33 @@ class WebApp {
|
|
|
123
347
|
yield actions.move({ origin: element }).perform();
|
|
124
348
|
});
|
|
125
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Sets focus on an element programmatically.
|
|
352
|
+
*
|
|
353
|
+
* Directly focuses the element using JavaScript, which is more reliable than clicking for focus.
|
|
354
|
+
* Useful for testing keyboard interactions, input fields, and focus-dependent behaviors.
|
|
355
|
+
*
|
|
356
|
+
* @param element - Element to focus (WebElement, By locator, or CSS selector)
|
|
357
|
+
* @param options - Optional configuration
|
|
358
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
359
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
360
|
+
* @returns Promise that resolves when focus is set
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* // Focus an input field
|
|
365
|
+
* await app.focus('#username');
|
|
366
|
+
* await app.sendKey(Key.CONTROL, 'a'); // Select all
|
|
367
|
+
*
|
|
368
|
+
* // Focus before typing
|
|
369
|
+
* await app.focus('#search-box');
|
|
370
|
+
* await app.type('#search-box', 'test query');
|
|
371
|
+
*
|
|
372
|
+
* // Test focus-dependent behavior
|
|
373
|
+
* await app.focus('#email');
|
|
374
|
+
* await app.expect('.validation-hint').toBeVisible();
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
126
377
|
focus(element_1) {
|
|
127
378
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
128
379
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -132,6 +383,28 @@ class WebApp {
|
|
|
132
383
|
yield this.driver.sleep(50);
|
|
133
384
|
});
|
|
134
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Performs a right-click (context menu click) on an element.
|
|
388
|
+
*
|
|
389
|
+
* Opens the context menu for the element, allowing you to test right-click menus and actions.
|
|
390
|
+
*
|
|
391
|
+
* @param element - Element to right-click (WebElement, By locator, or CSS selector)
|
|
392
|
+
* @param options - Optional configuration
|
|
393
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
394
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
395
|
+
* @returns Promise that resolves when right-click completes
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```typescript
|
|
399
|
+
* // Open context menu and select option
|
|
400
|
+
* await app.contextClick('.file-item');
|
|
401
|
+
* await app.click('.context-menu-delete');
|
|
402
|
+
*
|
|
403
|
+
* // Right-click on canvas element
|
|
404
|
+
* await app.contextClick('#drawing-canvas');
|
|
405
|
+
* await app.expect('.context-menu').toBeVisible();
|
|
406
|
+
* ```
|
|
407
|
+
*/
|
|
135
408
|
contextClick(element_1) {
|
|
136
409
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
137
410
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -141,6 +414,30 @@ class WebApp {
|
|
|
141
414
|
yield actions.contextClick(element).perform();
|
|
142
415
|
});
|
|
143
416
|
}
|
|
417
|
+
/**
|
|
418
|
+
* Performs a double-click on an element.
|
|
419
|
+
*
|
|
420
|
+
* Useful for testing double-click interactions like selecting text, opening files, or
|
|
421
|
+
* triggering double-click-specific behaviors in your application.
|
|
422
|
+
*
|
|
423
|
+
* @param element - Element to double-click (WebElement, By locator, or CSS selector)
|
|
424
|
+
* @param options - Optional configuration
|
|
425
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
426
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
427
|
+
* @returns Promise that resolves when double-click completes
|
|
428
|
+
*
|
|
429
|
+
* @example
|
|
430
|
+
* ```typescript
|
|
431
|
+
* // Double-click to open file
|
|
432
|
+
* await app.doubleClick('.file-icon');
|
|
433
|
+
*
|
|
434
|
+
* // Double-click to select word
|
|
435
|
+
* await app.doubleClick('.text-content');
|
|
436
|
+
*
|
|
437
|
+
* // Double-click with custom wait
|
|
438
|
+
* await app.doubleClick('#expandable-item', { timeout: 5000 });
|
|
439
|
+
* ```
|
|
440
|
+
*/
|
|
144
441
|
doubleClick(element_1) {
|
|
145
442
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
146
443
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -150,12 +447,57 @@ class WebApp {
|
|
|
150
447
|
yield actions.doubleClick(element).perform();
|
|
151
448
|
});
|
|
152
449
|
}
|
|
450
|
+
/**
|
|
451
|
+
* Waits for an element to stop animating, then clicks it.
|
|
452
|
+
*
|
|
453
|
+
* Perfect for clicking elements that are animating into view (slides, fades, etc.).
|
|
454
|
+
* Prevents clicks during animation which can cause missed clicks or wrong targets.
|
|
455
|
+
*
|
|
456
|
+
* @param element - Element to wait for and click (WebElement, By locator, or CSS selector)
|
|
457
|
+
* @param options - Optional configuration
|
|
458
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
459
|
+
* @param options.pollTimeout - Interval between animation checks in milliseconds (default: 50)
|
|
460
|
+
* @returns Promise that resolves when element is stable and clicked
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* ```typescript
|
|
464
|
+
* // Click button that slides into view
|
|
465
|
+
* await app.waitForAnimationAndClick('.animated-button');
|
|
466
|
+
*
|
|
467
|
+
* // Click element in animated modal
|
|
468
|
+
* await app.waitForAnimationAndClick('.modal .submit-btn', { timeout: 5000 });
|
|
469
|
+
* ```
|
|
470
|
+
*/
|
|
153
471
|
waitForAnimationAndClick(element_1) {
|
|
154
472
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 50 } = {}) {
|
|
155
473
|
yield this.waitForAnimation(element, { timeout: timeout, pollTimeout: pollTimeout });
|
|
156
474
|
yield this.click(element, { timeout: timeout, pollTimeout: pollTimeout });
|
|
157
475
|
});
|
|
158
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* Scrolls element into view and then clicks it.
|
|
479
|
+
*
|
|
480
|
+
* Handles elements that are not initially in viewport. Scrolls the element to the center
|
|
481
|
+
* of the viewport before clicking, ensuring reliable clicks on off-screen elements.
|
|
482
|
+
*
|
|
483
|
+
* @param element - Element to scroll to and click (WebElement, By locator, or CSS selector)
|
|
484
|
+
* @param options - Optional configuration
|
|
485
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
486
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
487
|
+
* @returns Promise that resolves when element is scrolled into view and clicked
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* // Click element at bottom of page
|
|
492
|
+
* await app.scrollAndClick('#footer-button');
|
|
493
|
+
*
|
|
494
|
+
* // Scroll and click in long form
|
|
495
|
+
* await app.scrollAndClick('.form-submit', { timeout: 5000 });
|
|
496
|
+
*
|
|
497
|
+
* // Click element in scrollable container
|
|
498
|
+
* await app.scrollAndClick('.list-item:last-child');
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
159
501
|
scrollAndClick(element_1) {
|
|
160
502
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
161
503
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -165,12 +507,62 @@ class WebApp {
|
|
|
165
507
|
yield element.click();
|
|
166
508
|
});
|
|
167
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Scrolls an element into the viewport without clicking it.
|
|
512
|
+
*
|
|
513
|
+
* Useful when you need an element visible for screenshots, visibility checks, or
|
|
514
|
+
* before performing other actions. Centers the element in the viewport.
|
|
515
|
+
*
|
|
516
|
+
* @param locator - Element to scroll to (By locator or CSS selector)
|
|
517
|
+
* @param options - Optional configuration
|
|
518
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
519
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
520
|
+
* @returns Promise that resolves when element is scrolled into view
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```typescript
|
|
524
|
+
* // Scroll to element for screenshot
|
|
525
|
+
* await app.scrollIntoView('#chart');
|
|
526
|
+
* const screenshot = await app.getScreenshot();
|
|
527
|
+
*
|
|
528
|
+
* // Scroll before checking visibility
|
|
529
|
+
* await app.scrollIntoView('.lazy-load-content');
|
|
530
|
+
* await app.expect('.lazy-load-content').toBeVisible();
|
|
531
|
+
* ```
|
|
532
|
+
*/
|
|
168
533
|
scrollIntoView(locator_1) {
|
|
169
534
|
return __awaiter(this, arguments, void 0, function* (locator, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
170
535
|
const element = yield this.find(locator, { timeout: timeout, pollTimeout: pollTimeout });
|
|
171
536
|
yield this.driver.executeScript("arguments[0].scrollIntoView({ behavior: 'instant', block: 'center', inline: 'nearest' });", element);
|
|
172
537
|
});
|
|
173
538
|
}
|
|
539
|
+
/**
|
|
540
|
+
* Drags a source element and drops it onto a target element.
|
|
541
|
+
*
|
|
542
|
+
* Performs a complete drag-and-drop operation, useful for testing sortable lists,
|
|
543
|
+
* drag-and-drop file uploads, kanban boards, and similar interactions.
|
|
544
|
+
*
|
|
545
|
+
* @param source - Element to drag (WebElement or By locator)
|
|
546
|
+
* @param target - Element to drop onto (WebElement or By locator)
|
|
547
|
+
* @returns Promise that resolves when drag-and-drop completes
|
|
548
|
+
*
|
|
549
|
+
* @example
|
|
550
|
+
* ```typescript
|
|
551
|
+
* // Drag list item to reorder
|
|
552
|
+
* await app.dragTo(
|
|
553
|
+
* By.css('.list-item:nth-child(1)'),
|
|
554
|
+
* By.css('.list-item:nth-child(3)')
|
|
555
|
+
* );
|
|
556
|
+
*
|
|
557
|
+
* // Drag file to upload area
|
|
558
|
+
* const file = await app.find('.file-icon');
|
|
559
|
+
* const dropzone = await app.find('.upload-dropzone');
|
|
560
|
+
* await app.dragTo(file, dropzone);
|
|
561
|
+
*
|
|
562
|
+
* // Drag card between columns (kanban)
|
|
563
|
+
* await app.dragTo('#card-1', '#column-done');
|
|
564
|
+
* ```
|
|
565
|
+
*/
|
|
174
566
|
dragTo(source, target) {
|
|
175
567
|
return __awaiter(this, void 0, void 0, function* () {
|
|
176
568
|
let sourceElement;
|
|
@@ -191,6 +583,29 @@ class WebApp {
|
|
|
191
583
|
yield actions.dragAndDrop(sourceElement, targetElement).perform();
|
|
192
584
|
});
|
|
193
585
|
}
|
|
586
|
+
/**
|
|
587
|
+
* Drags an element by a specified pixel offset.
|
|
588
|
+
*
|
|
589
|
+
* Moves an element by dragging it a specific number of pixels in X and Y directions.
|
|
590
|
+
* Useful for testing sliders, resizable panels, draggable windows, and custom drag interactions.
|
|
591
|
+
*
|
|
592
|
+
* @param element - Element to drag (WebElement, By locator, or CSS selector)
|
|
593
|
+
* @param offsetX - Horizontal offset in pixels (positive = right, negative = left)
|
|
594
|
+
* @param offsetY - Vertical offset in pixels (positive = down, negative = up)
|
|
595
|
+
* @returns Promise that resolves when drag completes
|
|
596
|
+
*
|
|
597
|
+
* @example
|
|
598
|
+
* ```typescript
|
|
599
|
+
* // Drag slider to the right
|
|
600
|
+
* await app.dragByOffset('.slider-handle', 100, 0);
|
|
601
|
+
*
|
|
602
|
+
* // Drag element down and left
|
|
603
|
+
* await app.dragByOffset('.draggable-box', -50, 75);
|
|
604
|
+
*
|
|
605
|
+
* // Resize panel by dragging splitter
|
|
606
|
+
* await app.dragByOffset('.splitter', 0, -100);
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
194
609
|
dragByOffset(element, offsetX, offsetY) {
|
|
195
610
|
return __awaiter(this, void 0, void 0, function* () {
|
|
196
611
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -200,6 +615,36 @@ class WebApp {
|
|
|
200
615
|
yield actions.dragAndDrop(element, { x: offsetX, y: offsetY }).perform();
|
|
201
616
|
});
|
|
202
617
|
}
|
|
618
|
+
/**
|
|
619
|
+
* Types text into an input element.
|
|
620
|
+
*
|
|
621
|
+
* Automatically finds the element, optionally clears existing content, types the text,
|
|
622
|
+
* and can optionally press Enter after typing. Perfect for form filling and text input.
|
|
623
|
+
*
|
|
624
|
+
* @param element - Input element to type into (WebElement, By locator, or CSS selector)
|
|
625
|
+
* @param text - Text to type
|
|
626
|
+
* @param options - Optional configuration
|
|
627
|
+
* @param options.clear - Whether to clear existing content first (default: true)
|
|
628
|
+
* @param options.sendEnter - Whether to press Enter after typing (default: false)
|
|
629
|
+
* @returns Promise that resolves when typing completes
|
|
630
|
+
*
|
|
631
|
+
* @example
|
|
632
|
+
* ```typescript
|
|
633
|
+
* // Type into input (clears existing text by default)
|
|
634
|
+
* await app.type('#username', 'testuser');
|
|
635
|
+
*
|
|
636
|
+
* // Type and submit with Enter
|
|
637
|
+
* await app.type('#search', 'search query', { sendEnter: true });
|
|
638
|
+
*
|
|
639
|
+
* // Type without clearing existing text
|
|
640
|
+
* await app.type('#notes', 'additional text', { clear: false });
|
|
641
|
+
*
|
|
642
|
+
* // Fill form fields
|
|
643
|
+
* await app.type('#email', 'user@example.com');
|
|
644
|
+
* await app.type('#password', 'secret123');
|
|
645
|
+
* await app.click('#login-button');
|
|
646
|
+
* ```
|
|
647
|
+
*/
|
|
203
648
|
type(element_1, text_1) {
|
|
204
649
|
return __awaiter(this, arguments, void 0, function* (element, text, { clear = true, sendEnter = false } = {}) {
|
|
205
650
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -214,16 +659,87 @@ class WebApp {
|
|
|
214
659
|
}
|
|
215
660
|
});
|
|
216
661
|
}
|
|
662
|
+
/**
|
|
663
|
+
* Sends a single keyboard key press.
|
|
664
|
+
*
|
|
665
|
+
* Simulates pressing a keyboard key. Use Selenium's Key constants for special keys.
|
|
666
|
+
*
|
|
667
|
+
* @param key - Key to press (use Key.ENTER, Key.TAB, Key.ESCAPE, etc.)
|
|
668
|
+
* @returns Promise that resolves when key press completes
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* ```typescript
|
|
672
|
+
* import { Key } from '@kendo/kendo-e2e';
|
|
673
|
+
*
|
|
674
|
+
* // Press Enter
|
|
675
|
+
* await app.sendKey(Key.ENTER);
|
|
676
|
+
*
|
|
677
|
+
* // Press Tab to move focus
|
|
678
|
+
* await app.sendKey(Key.TAB);
|
|
679
|
+
*
|
|
680
|
+
* // Press Escape to close modal
|
|
681
|
+
* await app.sendKey(Key.ESCAPE);
|
|
682
|
+
*
|
|
683
|
+
* // Press Arrow Down
|
|
684
|
+
* await app.sendKey(Key.ARROW_DOWN);
|
|
685
|
+
* ```
|
|
686
|
+
*/
|
|
217
687
|
sendKey(key) {
|
|
218
688
|
return __awaiter(this, void 0, void 0, function* () {
|
|
219
689
|
yield this.driver.actions({ async: true, bridge: true }).sendKeys(key).perform();
|
|
220
690
|
});
|
|
221
691
|
}
|
|
692
|
+
/**
|
|
693
|
+
* Sends a two-key combination (e.g., Ctrl+C).
|
|
694
|
+
*
|
|
695
|
+
* Convenience method for common two-key combinations. For more keys, use {@link sendKeysCombination}.
|
|
696
|
+
*
|
|
697
|
+
* @param key1 - First key (typically a modifier like Key.CONTROL)
|
|
698
|
+
* @param key2 - Second key
|
|
699
|
+
* @returns Promise that resolves when key combination completes
|
|
700
|
+
*
|
|
701
|
+
* @example
|
|
702
|
+
* ```typescript
|
|
703
|
+
* import { Key } from '@kendo/kendo-e2e';
|
|
704
|
+
*
|
|
705
|
+
* // Copy text
|
|
706
|
+
* await app.sendKeyCombination(Key.CONTROL, 'c');
|
|
707
|
+
*
|
|
708
|
+
* // Paste text
|
|
709
|
+
* await app.sendKeyCombination(Key.CONTROL, 'v');
|
|
710
|
+
*
|
|
711
|
+
* // Open browser dev tools (F12 might not work in some contexts)
|
|
712
|
+
* await app.sendKeyCombination(Key.CONTROL, Key.SHIFT);
|
|
713
|
+
* ```
|
|
714
|
+
*/
|
|
222
715
|
sendKeyCombination(key1, key2) {
|
|
223
716
|
return __awaiter(this, void 0, void 0, function* () {
|
|
224
717
|
yield this.sendKeysCombination([key1, key2]);
|
|
225
718
|
});
|
|
226
719
|
}
|
|
720
|
+
/**
|
|
721
|
+
* Sends a combination of multiple keyboard keys simultaneously.
|
|
722
|
+
*
|
|
723
|
+
* Holds down all keys in order, then releases them in reverse order, simulating
|
|
724
|
+
* a realistic key combination press.
|
|
725
|
+
*
|
|
726
|
+
* @param keys - Array of keys to press together
|
|
727
|
+
* @returns Promise that resolves when key combination completes
|
|
728
|
+
*
|
|
729
|
+
* @example
|
|
730
|
+
* ```typescript
|
|
731
|
+
* import { Key } from '@kendo/kendo-e2e';
|
|
732
|
+
*
|
|
733
|
+
* // Ctrl+Shift+Delete
|
|
734
|
+
* await app.sendKeysCombination([Key.CONTROL, Key.SHIFT, Key.DELETE]);
|
|
735
|
+
*
|
|
736
|
+
* // Select all (Ctrl+A)
|
|
737
|
+
* await app.sendKeysCombination([Key.CONTROL, 'a']);
|
|
738
|
+
*
|
|
739
|
+
* // Custom three-key combo
|
|
740
|
+
* await app.sendKeysCombination([Key.ALT, Key.SHIFT, 'F']);
|
|
741
|
+
* ```
|
|
742
|
+
*/
|
|
227
743
|
sendKeysCombination(keys) {
|
|
228
744
|
return __awaiter(this, void 0, void 0, function* () {
|
|
229
745
|
const actions = this.driver.actions({ async: false, bridge: true });
|
|
@@ -236,6 +752,30 @@ class WebApp {
|
|
|
236
752
|
yield actions.perform();
|
|
237
753
|
});
|
|
238
754
|
}
|
|
755
|
+
/**
|
|
756
|
+
* Sends Ctrl+key on Windows/Linux or Cmd+key on macOS automatically.
|
|
757
|
+
*
|
|
758
|
+
* Cross-platform helper that uses the appropriate modifier key for the current OS.
|
|
759
|
+
* Perfect for common shortcuts like copy, paste, save, etc.
|
|
760
|
+
*
|
|
761
|
+
* @param key - Key to combine with Ctrl/Cmd
|
|
762
|
+
* @returns Promise that resolves when key combination completes
|
|
763
|
+
*
|
|
764
|
+
* @example
|
|
765
|
+
* ```typescript
|
|
766
|
+
* // Copy (Ctrl+C on Windows/Linux, Cmd+C on macOS)
|
|
767
|
+
* await app.sendControlKeyCombination('c');
|
|
768
|
+
*
|
|
769
|
+
* // Paste (cross-platform)
|
|
770
|
+
* await app.sendControlKeyCombination('v');
|
|
771
|
+
*
|
|
772
|
+
* // Select all (cross-platform)
|
|
773
|
+
* await app.sendControlKeyCombination('a');
|
|
774
|
+
*
|
|
775
|
+
* // Save (cross-platform)
|
|
776
|
+
* await app.sendControlKeyCombination('s');
|
|
777
|
+
* ```
|
|
778
|
+
*/
|
|
239
779
|
sendControlKeyCombination(key) {
|
|
240
780
|
return __awaiter(this, void 0, void 0, function* () {
|
|
241
781
|
const control = (process.platform === 'darwin' ? selenium_webdriver_1.Key.COMMAND : selenium_webdriver_1.Key.CONTROL);
|
|
@@ -309,11 +849,73 @@ class WebApp {
|
|
|
309
849
|
yield this.driver.sleep(milliseconds);
|
|
310
850
|
});
|
|
311
851
|
}
|
|
852
|
+
/**
|
|
853
|
+
* Waits for a condition to become true.
|
|
854
|
+
*
|
|
855
|
+
* Core waiting method that polls a condition until it returns true or timeout is reached.
|
|
856
|
+
* Use predefined conditions from {@link EC} class or create custom conditions.
|
|
857
|
+
*
|
|
858
|
+
* @param condition - Condition function or WebElementCondition to wait for
|
|
859
|
+
* @param options - Optional configuration
|
|
860
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
861
|
+
* @param options.message - Custom error message if condition times out (default: 'Failed to satisfy condition.')
|
|
862
|
+
* @param options.pollTimeout - Interval between condition checks in milliseconds (default: 25)
|
|
863
|
+
* @returns Promise that resolves when condition is met
|
|
864
|
+
* @throws Error with the specified message if condition is not met within timeout
|
|
865
|
+
*
|
|
866
|
+
* @example
|
|
867
|
+
* ```typescript
|
|
868
|
+
* // Wait for element to be visible
|
|
869
|
+
* await app.wait(EC.isVisible('#modal'));
|
|
870
|
+
*
|
|
871
|
+
* // Wait with custom timeout and message
|
|
872
|
+
* await app.wait(EC.hasText(element, 'Success'), {
|
|
873
|
+
* timeout: 15000,
|
|
874
|
+
* message: 'Success message did not appear'
|
|
875
|
+
* });
|
|
876
|
+
*
|
|
877
|
+
* // Wait for custom condition
|
|
878
|
+
* await app.wait(async () => {
|
|
879
|
+
* const count = await app.findAll('.item');
|
|
880
|
+
* return count.length > 5;
|
|
881
|
+
* }, { message: 'Less than 5 items found' });
|
|
882
|
+
* ```
|
|
883
|
+
*/
|
|
312
884
|
wait(condition_1) {
|
|
313
885
|
return __awaiter(this, arguments, void 0, function* (condition, { timeout = 10000, message = 'Failed to satisfy condition.', pollTimeout = 25 } = {}) {
|
|
314
886
|
yield this.driver.wait(condition, timeout, message, pollTimeout);
|
|
315
887
|
});
|
|
316
888
|
}
|
|
889
|
+
/**
|
|
890
|
+
* Waits for a condition without throwing an error if it fails.
|
|
891
|
+
*
|
|
892
|
+
* Returns true if condition is met, false if timeout is reached. Perfect for conditional
|
|
893
|
+
* logic in tests where you want to check if something happened without failing the test.
|
|
894
|
+
*
|
|
895
|
+
* @param condition - Condition function or WebElementCondition to wait for
|
|
896
|
+
* @param options - Optional configuration
|
|
897
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
898
|
+
* @param options.pollTimeout - Interval between condition checks in milliseconds (default: 25)
|
|
899
|
+
* @returns Promise resolving to true if condition met, false if timeout reached
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* ```typescript
|
|
903
|
+
* // Check if element appears (don't fail if it doesn't)
|
|
904
|
+
* const appeared = await app.waitSafely(EC.isVisible('.optional-message'));
|
|
905
|
+
* if (appeared) {
|
|
906
|
+
* console.log('Message was shown');
|
|
907
|
+
* }
|
|
908
|
+
*
|
|
909
|
+
* // Conditional test flow
|
|
910
|
+
* const hasModal = await app.waitSafely(EC.isVisible('.modal'), { timeout: 3000 });
|
|
911
|
+
* if (hasModal) {
|
|
912
|
+
* await app.click('.modal .close');
|
|
913
|
+
* }
|
|
914
|
+
*
|
|
915
|
+
* // Check if element has specific text
|
|
916
|
+
* const hasText = await app.waitSafely(EC.hasText('#status', 'Complete'));
|
|
917
|
+
* ```
|
|
918
|
+
*/
|
|
317
919
|
waitSafely(condition_1) {
|
|
318
920
|
return __awaiter(this, arguments, void 0, function* (condition, { timeout = 10000, pollTimeout = 25 } = {}) {
|
|
319
921
|
try {
|
|
@@ -325,6 +927,34 @@ class WebApp {
|
|
|
325
927
|
}
|
|
326
928
|
});
|
|
327
929
|
}
|
|
930
|
+
/**
|
|
931
|
+
* Waits for an element to stop moving or resizing (animation to complete).
|
|
932
|
+
*
|
|
933
|
+
* Monitors element position and size, waiting until they remain stable for a poll interval.
|
|
934
|
+
* Essential for reliable interaction with animated elements.
|
|
935
|
+
*
|
|
936
|
+
* @param element - Element to monitor (WebElement, By locator, or CSS selector)
|
|
937
|
+
* @param options - Optional configuration
|
|
938
|
+
* @param options.timeout - Maximum time to wait in milliseconds (default: 10000)
|
|
939
|
+
* @param options.pollTimeout - Interval between stability checks in milliseconds (default: 50)
|
|
940
|
+
* @returns Promise that resolves when element is stable
|
|
941
|
+
* @throws Error if element doesn't stabilize within timeout
|
|
942
|
+
*
|
|
943
|
+
* @example
|
|
944
|
+
* ```typescript
|
|
945
|
+
* // Wait for sliding panel to stop
|
|
946
|
+
* await app.waitForAnimation('.slide-panel');
|
|
947
|
+
* await app.click('.slide-panel button');
|
|
948
|
+
*
|
|
949
|
+
* // Wait for expanding accordion
|
|
950
|
+
* await app.click('.accordion-header');
|
|
951
|
+
* await app.waitForAnimation('.accordion-content');
|
|
952
|
+
*
|
|
953
|
+
* // Use before taking screenshots of animated content
|
|
954
|
+
* await app.waitForAnimation('.chart');
|
|
955
|
+
* const screenshot = await app.getScreenshot();
|
|
956
|
+
* ```
|
|
957
|
+
*/
|
|
328
958
|
waitForAnimation(element_1) {
|
|
329
959
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 50 } = {}) {
|
|
330
960
|
const locatorStringValue = (element instanceof selenium_webdriver_1.WebElement)
|
|
@@ -352,11 +982,38 @@ class WebApp {
|
|
|
352
982
|
return yield this.driver.takeScreenshot();
|
|
353
983
|
});
|
|
354
984
|
}
|
|
985
|
+
/**
|
|
986
|
+
* @deprecated Use executeScript with proper types instead
|
|
987
|
+
* @internal
|
|
988
|
+
*/
|
|
355
989
|
executeScript(script) {
|
|
356
990
|
return __awaiter(this, void 0, void 0, function* () {
|
|
357
991
|
return yield this.driver.executeScript(script);
|
|
358
992
|
});
|
|
359
993
|
}
|
|
994
|
+
/**
|
|
995
|
+
* Gets the visible text content of an element.
|
|
996
|
+
*
|
|
997
|
+
* Returns the text that would be visible to a user, excluding hidden elements.
|
|
998
|
+
* Automatically finds the element if a locator is provided.
|
|
999
|
+
*
|
|
1000
|
+
* @param element - Element to get text from (WebElement, By locator, or CSS selector)
|
|
1001
|
+
* @returns Promise resolving to the element's text, or undefined if element has no text
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* ```typescript
|
|
1005
|
+
* // Get button text
|
|
1006
|
+
* const buttonText = await app.getText('#submit-btn');
|
|
1007
|
+
* console.log(buttonText); // 'Submit Form'
|
|
1008
|
+
*
|
|
1009
|
+
* // Get paragraph content
|
|
1010
|
+
* const message = await app.getText('.success-message');
|
|
1011
|
+
*
|
|
1012
|
+
* // Get text from found element
|
|
1013
|
+
* const element = await app.find('.label');
|
|
1014
|
+
* const text = await app.getText(element);
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
360
1017
|
getText(element) {
|
|
361
1018
|
return __awaiter(this, void 0, void 0, function* () {
|
|
362
1019
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -370,6 +1027,31 @@ class WebApp {
|
|
|
370
1027
|
}
|
|
371
1028
|
});
|
|
372
1029
|
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Gets the value of an HTML attribute from an element.
|
|
1032
|
+
*
|
|
1033
|
+
* Retrieves attribute values like 'href', 'src', 'disabled', 'data-*', etc.
|
|
1034
|
+
* Returns null if the attribute doesn't exist.
|
|
1035
|
+
*
|
|
1036
|
+
* @param element - Element to get attribute from (WebElement, By locator, or CSS selector)
|
|
1037
|
+
* @param attribute - Name of the attribute to retrieve
|
|
1038
|
+
* @returns Promise resolving to the attribute value or null
|
|
1039
|
+
*
|
|
1040
|
+
* @example
|
|
1041
|
+
* ```typescript
|
|
1042
|
+
* // Get link href
|
|
1043
|
+
* const url = await app.getAttribute('a.download', 'href');
|
|
1044
|
+
*
|
|
1045
|
+
* // Check if button is disabled
|
|
1046
|
+
* const isDisabled = await app.getAttribute('#submit', 'disabled');
|
|
1047
|
+
*
|
|
1048
|
+
* // Get data attribute
|
|
1049
|
+
* const userId = await app.getAttribute('.user', 'data-user-id');
|
|
1050
|
+
*
|
|
1051
|
+
* // Get input value
|
|
1052
|
+
* const value = await app.getAttribute('#email', 'value');
|
|
1053
|
+
* ```
|
|
1054
|
+
*/
|
|
373
1055
|
getAttribute(element, attribute) {
|
|
374
1056
|
return __awaiter(this, void 0, void 0, function* () {
|
|
375
1057
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -378,6 +1060,31 @@ class WebApp {
|
|
|
378
1060
|
return yield element.getAttribute(attribute);
|
|
379
1061
|
});
|
|
380
1062
|
}
|
|
1063
|
+
/**
|
|
1064
|
+
* Gets a JavaScript property value from an element.
|
|
1065
|
+
*
|
|
1066
|
+
* Different from {@link getAttribute} - this gets DOM properties (like 'value', 'checked')
|
|
1067
|
+
* which may differ from HTML attributes. Properties reflect the current state.
|
|
1068
|
+
*
|
|
1069
|
+
* @param element - Element to get property from (WebElement, By locator, or CSS selector)
|
|
1070
|
+
* @param property - Name of the property to retrieve
|
|
1071
|
+
* @returns Promise resolving to the property value
|
|
1072
|
+
*
|
|
1073
|
+
* @example
|
|
1074
|
+
* ```typescript
|
|
1075
|
+
* // Get checkbox checked state (property, not attribute)
|
|
1076
|
+
* const isChecked = await app.getProperty('#agree', 'checked');
|
|
1077
|
+
*
|
|
1078
|
+
* // Get input value (current value, not initial)
|
|
1079
|
+
* const currentValue = await app.getProperty('#username', 'value');
|
|
1080
|
+
*
|
|
1081
|
+
* // Get element's innerHTML
|
|
1082
|
+
* const html = await app.getProperty('.container', 'innerHTML');
|
|
1083
|
+
*
|
|
1084
|
+
* // Get computed style property
|
|
1085
|
+
* const display = await app.getProperty('#element', 'style');
|
|
1086
|
+
* ```
|
|
1087
|
+
*/
|
|
381
1088
|
getProperty(element, property) {
|
|
382
1089
|
return __awaiter(this, void 0, void 0, function* () {
|
|
383
1090
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -387,6 +1094,28 @@ class WebApp {
|
|
|
387
1094
|
return yield this.driver.executeScript(script, element, property);
|
|
388
1095
|
});
|
|
389
1096
|
}
|
|
1097
|
+
/**
|
|
1098
|
+
* Gets the text color (color CSS property) of an element as hex value.
|
|
1099
|
+
*
|
|
1100
|
+
* Converts the color from any format (rgb, rgba, named) to hex format (#RRGGBB).
|
|
1101
|
+
* Useful for visual testing and theme verification.
|
|
1102
|
+
*
|
|
1103
|
+
* @param element - Element to get color from (WebElement, By locator, or CSS selector)
|
|
1104
|
+
* @returns Promise resolving to hex color string (e.g., '#ff0000')
|
|
1105
|
+
*
|
|
1106
|
+
* @example
|
|
1107
|
+
* ```typescript
|
|
1108
|
+
* // Check error message color
|
|
1109
|
+
* const errorColor = await app.getColor('.error-message');
|
|
1110
|
+
* expect(errorColor).toBe('#ff0000'); // red
|
|
1111
|
+
*
|
|
1112
|
+
* // Verify link color
|
|
1113
|
+
* const linkColor = await app.getColor('a.primary');
|
|
1114
|
+
*
|
|
1115
|
+
* // Check themed text color
|
|
1116
|
+
* const textColor = await app.getColor('.themed-text');
|
|
1117
|
+
* ```
|
|
1118
|
+
*/
|
|
390
1119
|
getColor(element) {
|
|
391
1120
|
return __awaiter(this, void 0, void 0, function* () {
|
|
392
1121
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -396,6 +1125,28 @@ class WebApp {
|
|
|
396
1125
|
return (0, rgb2hex_1.default)(color).hex;
|
|
397
1126
|
});
|
|
398
1127
|
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Gets the background color (background-color CSS property) of an element as hex value.
|
|
1130
|
+
*
|
|
1131
|
+
* Converts the background color from any format to hex format (#RRGGBB).
|
|
1132
|
+
* Useful for verifying button states, highlights, and theme colors.
|
|
1133
|
+
*
|
|
1134
|
+
* @param element - Element to get background color from (WebElement, By locator, or CSS selector)
|
|
1135
|
+
* @returns Promise resolving to hex color string (e.g., '#ffffff')
|
|
1136
|
+
*
|
|
1137
|
+
* @example
|
|
1138
|
+
* ```typescript
|
|
1139
|
+
* // Check button background
|
|
1140
|
+
* const btnBg = await app.getBackgroundColor('#primary-btn');
|
|
1141
|
+
* expect(btnBg).toBe('#007bff'); // bootstrap primary
|
|
1142
|
+
*
|
|
1143
|
+
* // Verify selected item highlight
|
|
1144
|
+
* const selectedBg = await app.getBackgroundColor('.selected');
|
|
1145
|
+
*
|
|
1146
|
+
* // Check alert background color
|
|
1147
|
+
* const alertBg = await app.getBackgroundColor('.alert-warning');
|
|
1148
|
+
* ```
|
|
1149
|
+
*/
|
|
399
1150
|
getBackgroundColor(element) {
|
|
400
1151
|
return __awaiter(this, void 0, void 0, function* () {
|
|
401
1152
|
if (!(element instanceof selenium_webdriver_1.WebElement)) {
|
|
@@ -406,17 +1157,35 @@ class WebApp {
|
|
|
406
1157
|
});
|
|
407
1158
|
}
|
|
408
1159
|
/**
|
|
409
|
-
* Hides the text caret
|
|
1160
|
+
* Hides the text cursor/caret for cleaner screenshots.
|
|
410
1161
|
*
|
|
411
|
-
* When
|
|
412
|
-
*
|
|
413
|
-
*
|
|
1162
|
+
* When called without arguments, hides the cursor globally for all input/textarea elements.
|
|
1163
|
+
* When called with an element, hides the cursor only for that specific element.
|
|
1164
|
+
* Global hiding persists until page reload.
|
|
414
1165
|
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
1166
|
+
* @param element - Optional specific element to hide cursor for (WebElement, By locator, or CSS selector)
|
|
1167
|
+
* @param options - Optional configuration
|
|
1168
|
+
* @param options.timeout - Maximum time to wait for element in milliseconds (default: 10000)
|
|
1169
|
+
* @param options.pollTimeout - Interval between retry attempts in milliseconds (default: 25)
|
|
1170
|
+
* @returns Promise that resolves when cursor is hidden
|
|
1171
|
+
*
|
|
1172
|
+
* @example
|
|
1173
|
+
* ```typescript
|
|
1174
|
+
* // Hide cursor globally before screenshot
|
|
1175
|
+
* await app.hideCursor();
|
|
1176
|
+
* const screenshot = await app.getScreenshot();
|
|
1177
|
+
*
|
|
1178
|
+
* // Hide cursor for specific input
|
|
1179
|
+
* await app.focus('#username');
|
|
1180
|
+
* await app.hideCursor('#username');
|
|
1181
|
+
* const inputScreenshot = await app.getScreenshot();
|
|
1182
|
+
*
|
|
1183
|
+
* // Hide cursor in focused field for visual test
|
|
1184
|
+
* await app.type('#search', 'test');
|
|
1185
|
+
* await app.hideCursor('#search');
|
|
1186
|
+
* ```
|
|
417
1187
|
*
|
|
418
|
-
* @
|
|
419
|
-
* @param options Optional timeout and pollTimeout for element location.
|
|
1188
|
+
* @see For more details, see [hideCursor documentation](../../docs/hideCursor.md)
|
|
420
1189
|
*/
|
|
421
1190
|
hideCursor(element_1) {
|
|
422
1191
|
return __awaiter(this, arguments, void 0, function* (element, { timeout = 10000, pollTimeout = 25 } = {}) {
|