@progress/kendo-e2e 4.11.2 → 4.12.0

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.
@@ -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
- * This class encapsulates common location and interaction functionality over a {@link ThenableWebDriver}.
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 either globally (all input & textarea elements) or for a specific element.
1160
+ * Hides the text cursor/caret for cleaner screenshots.
410
1161
  *
411
- * When no element is provided, a <style> tag is injected that sets caret-color to transparent
412
- * for all input and textarea elements. This is reversible only by page reload or manually removing
413
- * the injected style element.
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
- * When an element is provided (WebElement, By locator, or CSS selector string), the element is
416
- * located (if needed) and its caret color is set to transparent.
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
- * @param element Optional element whose caret should be hidden (WebElement instance, By locator, or CSS selector string).
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 } = {}) {