@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.
- 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 -3
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# Getting Started with kendo-e2e
|
|
2
|
+
|
|
3
|
+
**kendo-e2e** is a Selenium-based testing library with **automatic waiting** built-in, making your tests faster to write and more reliable.
|
|
4
|
+
|
|
5
|
+
## Why kendo-e2e?
|
|
6
|
+
|
|
7
|
+
Traditional Selenium tests are flaky because they require manual waits. kendo-e2e solves this by:
|
|
8
|
+
|
|
9
|
+
✅ **Automatic waiting** - Every method waits for elements automatically
|
|
10
|
+
✅ **No StaleElementReferenceException** - Built-in retry logic
|
|
11
|
+
✅ **Modern expect API** - Playwright-style assertions with auto-retry
|
|
12
|
+
✅ **Less code** - Write tests faster with intuitive methods
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @kendo/kendo-e2e --save-dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Basic Browser Test
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Browser } from '@kendo/kendo-e2e';
|
|
26
|
+
|
|
27
|
+
describe('My First Test', () => {
|
|
28
|
+
let browser: Browser;
|
|
29
|
+
|
|
30
|
+
beforeAll(async () => {
|
|
31
|
+
browser = new Browser();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterAll(async () => {
|
|
35
|
+
await browser.close();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should load the page and interact', async () => {
|
|
39
|
+
// Navigate to URL
|
|
40
|
+
await browser.navigateTo('https://example.com');
|
|
41
|
+
|
|
42
|
+
// Click button (automatically waits for it to appear)
|
|
43
|
+
await browser.click('#login-button');
|
|
44
|
+
|
|
45
|
+
// Type in input (automatically waits and clears)
|
|
46
|
+
await browser.type('#username', 'testuser');
|
|
47
|
+
await browser.type('#password', 'secret123');
|
|
48
|
+
|
|
49
|
+
// Submit form
|
|
50
|
+
await browser.click('#submit');
|
|
51
|
+
|
|
52
|
+
// Assert with automatic retry (waits up to 3s by default)
|
|
53
|
+
await browser.expect('.welcome-message').toHaveText('Welcome testuser');
|
|
54
|
+
await browser.expect('.dashboard').toBeVisible();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Key Differences from Plain Selenium
|
|
60
|
+
|
|
61
|
+
| Plain Selenium | kendo-e2e |
|
|
62
|
+
|----------------|-----------|
|
|
63
|
+
| `driver.findElement(By.css('#btn'))` then manual wait | `await browser.click('#btn')` |
|
|
64
|
+
| Manual `WebDriverWait` everywhere | Automatic waiting built-in |
|
|
65
|
+
| `StaleElementReferenceException` errors | Automatic retry handles it |
|
|
66
|
+
| Complex wait conditions | Simple `expect()` API |
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
Control browser settings via environment variables:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Browser selection
|
|
74
|
+
BROWSER_NAME=chrome # chrome, firefox, safari, MicrosoftEdge
|
|
75
|
+
HEADLESS=true # Run without UI (default: false)
|
|
76
|
+
|
|
77
|
+
# Window size
|
|
78
|
+
BROWSER_WIDTH=1920
|
|
79
|
+
BROWSER_HEIGHT=1080
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### In Code Configuration
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Mobile emulation
|
|
86
|
+
const browser = new Browser({
|
|
87
|
+
mobileEmulation: { deviceName: 'iPhone 14 Pro Max' }
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Custom window size
|
|
91
|
+
const browser = new Browser();
|
|
92
|
+
await browser.resizeWindow(1920, 1080);
|
|
93
|
+
|
|
94
|
+
// BiDi protocol for advanced features
|
|
95
|
+
const browser = new Browser({ enableBidi: true });
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Common Patterns
|
|
99
|
+
|
|
100
|
+
### Finding Elements
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// Find single element (waits automatically)
|
|
104
|
+
const button = await browser.find('#submit');
|
|
105
|
+
const header = await browser.find('.page-header');
|
|
106
|
+
|
|
107
|
+
// Find all matching elements (no wait)
|
|
108
|
+
const items = await browser.findAll('.list-item');
|
|
109
|
+
|
|
110
|
+
// Find with wait for at least one
|
|
111
|
+
const results = await browser.findAllWithTimeout('.search-result');
|
|
112
|
+
|
|
113
|
+
// Find child within parent
|
|
114
|
+
const dialog = await browser.find('.modal');
|
|
115
|
+
const closeBtn = await browser.findChild(dialog, '.close-button');
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Interacting with Elements
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// Click (waits for visible and enabled)
|
|
122
|
+
await browser.click('#button');
|
|
123
|
+
|
|
124
|
+
// Type text (clears by default)
|
|
125
|
+
await browser.type('#input', 'text');
|
|
126
|
+
|
|
127
|
+
// Type without clearing
|
|
128
|
+
await browser.type('#notes', 'more text', { clear: false });
|
|
129
|
+
|
|
130
|
+
// Type and press Enter
|
|
131
|
+
await browser.type('#search', 'query', { sendEnter: true });
|
|
132
|
+
|
|
133
|
+
// Hover over element
|
|
134
|
+
await browser.hover('.menu-item');
|
|
135
|
+
|
|
136
|
+
// Double-click
|
|
137
|
+
await browser.doubleClick('.file-icon');
|
|
138
|
+
|
|
139
|
+
// Right-click
|
|
140
|
+
await browser.contextClick('.item');
|
|
141
|
+
|
|
142
|
+
// Drag and drop
|
|
143
|
+
await browser.dragTo('#source', '#target');
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Waiting and Assertions
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Modern expect API (auto-retries)
|
|
150
|
+
await browser.expect('#message').toHaveText('Success');
|
|
151
|
+
await browser.expect('.modal').toBeVisible();
|
|
152
|
+
await browser.expect('.spinner').not.toBeVisible();
|
|
153
|
+
|
|
154
|
+
// Wait for condition
|
|
155
|
+
import { EC } from '@kendo/kendo-e2e';
|
|
156
|
+
await browser.wait(EC.isVisible('#element'));
|
|
157
|
+
|
|
158
|
+
// Wait safely (returns boolean, doesn't throw)
|
|
159
|
+
const appeared = await browser.waitSafely(EC.isVisible('.optional'));
|
|
160
|
+
if (appeared) {
|
|
161
|
+
await browser.click('.optional .close');
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Keyboard Interactions
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { Key } from '@kendo/kendo-e2e';
|
|
169
|
+
|
|
170
|
+
// Send single key
|
|
171
|
+
await browser.sendKey(Key.ENTER);
|
|
172
|
+
await browser.sendKey(Key.TAB);
|
|
173
|
+
await browser.sendKey(Key.ESCAPE);
|
|
174
|
+
|
|
175
|
+
// Key combinations
|
|
176
|
+
await browser.sendKeyCombination(Key.CONTROL, 'c'); // Copy
|
|
177
|
+
|
|
178
|
+
// Cross-platform Ctrl/Cmd
|
|
179
|
+
await browser.sendControlKeyCombination('v'); // Paste
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Working with Forms
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Fill out a form
|
|
186
|
+
await browser.type('#firstName', 'John');
|
|
187
|
+
await browser.type('#lastName', 'Doe');
|
|
188
|
+
await browser.type('#email', 'john@example.com');
|
|
189
|
+
await browser.click('#submit');
|
|
190
|
+
|
|
191
|
+
// Verify form submission
|
|
192
|
+
await browser.expect('.success-message').toBeVisible();
|
|
193
|
+
await browser.expect('.success-message').toHaveText('Form submitted successfully');
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Best Practices
|
|
197
|
+
|
|
198
|
+
### ✅ Do This
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// Use expect() for assertions with auto-retry
|
|
202
|
+
await browser.expect('#status').toHaveText('Complete');
|
|
203
|
+
|
|
204
|
+
// Use CSS selectors (simpler, faster)
|
|
205
|
+
await browser.click('#submit');
|
|
206
|
+
await browser.click('.btn-primary');
|
|
207
|
+
|
|
208
|
+
// Chain operations naturally
|
|
209
|
+
await browser.navigateTo('https://example.com');
|
|
210
|
+
await browser.click('#login');
|
|
211
|
+
await browser.type('#username', 'user');
|
|
212
|
+
await browser.click('#submit');
|
|
213
|
+
await browser.expect('.dashboard').toBeVisible();
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### ❌ Avoid This
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
// DON'T add manual sleeps - automatic waiting handles it
|
|
220
|
+
await browser.sleep(2000); // ❌ Unnecessary
|
|
221
|
+
|
|
222
|
+
// DON'T use complex XPath when CSS works
|
|
223
|
+
await browser.click(By.xpath('//div[@id="submit"]')); // ❌
|
|
224
|
+
await browser.click('#submit'); // ✅ Better
|
|
225
|
+
|
|
226
|
+
// DON'T manually wait before actions
|
|
227
|
+
await browser.wait(EC.isVisible('#button'));
|
|
228
|
+
await browser.click('#button'); // ❌ click() already waits!
|
|
229
|
+
// Just do:
|
|
230
|
+
await browser.click('#button'); // ✅
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
## Testing Checklist
|
|
234
|
+
|
|
235
|
+
Essential checks for every test:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
it('should work without errors', async () => {
|
|
239
|
+
// Clear browser console
|
|
240
|
+
await browser.clearLogs();
|
|
241
|
+
|
|
242
|
+
// Your test actions
|
|
243
|
+
await browser.navigateTo('https://example.com');
|
|
244
|
+
await browser.click('#trigger-action');
|
|
245
|
+
|
|
246
|
+
// Check for console errors
|
|
247
|
+
const errors = await browser.getErrorLogs();
|
|
248
|
+
expect(errors.length).toBe(0);
|
|
249
|
+
|
|
250
|
+
// Check accessibility
|
|
251
|
+
const a11yViolations = await browser.getAccessibilityViolations();
|
|
252
|
+
expect(a11yViolations.length).toBe(0);
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Next Steps
|
|
257
|
+
|
|
258
|
+
- 📚 Read [API Reference](./API_REFERENCE.md) for complete method documentation
|
|
259
|
+
- 🎯 Check [Common Patterns](./PATTERNS.md) for real-world examples
|
|
260
|
+
|
|
261
|
+
## Example Test Suite
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { Browser } from '@kendo/kendo-e2e';
|
|
265
|
+
|
|
266
|
+
describe('Login Flow', () => {
|
|
267
|
+
let browser: Browser;
|
|
268
|
+
|
|
269
|
+
beforeAll(async () => {
|
|
270
|
+
browser = new Browser();
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
afterAll(async () => {
|
|
274
|
+
await browser.close();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
beforeEach(async () => {
|
|
278
|
+
await browser.navigateTo('https://example.com/login');
|
|
279
|
+
await browser.clearLogs();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should show login form', async () => {
|
|
283
|
+
await browser.expect('#login-form').toBeVisible();
|
|
284
|
+
await browser.expect('#username').toBeVisible();
|
|
285
|
+
await browser.expect('#password').toBeVisible();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should login successfully', async () => {
|
|
289
|
+
await browser.type('#username', 'testuser');
|
|
290
|
+
await browser.type('#password', 'password123');
|
|
291
|
+
await browser.click('#login-button');
|
|
292
|
+
|
|
293
|
+
await browser.expect('.welcome-message').toBeVisible();
|
|
294
|
+
await browser.expect('.welcome-message').toHaveText('Welcome testuser');
|
|
295
|
+
|
|
296
|
+
// Verify no console errors
|
|
297
|
+
const errors = await browser.getErrorLogs();
|
|
298
|
+
expect(errors.length).toBe(0);
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('should show error for invalid credentials', async () => {
|
|
302
|
+
await browser.type('#username', 'invalid');
|
|
303
|
+
await browser.type('#password', 'wrong');
|
|
304
|
+
await browser.click('#login-button');
|
|
305
|
+
|
|
306
|
+
await browser.expect('.error-message').toBeVisible();
|
|
307
|
+
await browser.expect('.error-message').toHaveText('Invalid credentials');
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## Troubleshooting
|
|
313
|
+
|
|
314
|
+
### Tests are slow
|
|
315
|
+
- Reduce timeouts for faster feedback: `await browser.expect('#el').toBeVisible({ timeout: 5000 })`
|
|
316
|
+
- Use headless mode: `HEADLESS=true npm test`
|
|
317
|
+
|
|
318
|
+
### Element not found
|
|
319
|
+
- Check your selector is correct
|
|
320
|
+
- Verify element exists in the DOM when expected
|
|
321
|
+
- Try using `findAllWithTimeout` if content loads dynamically
|
|
322
|
+
|
|
323
|
+
### Flaky tests
|
|
324
|
+
- Use `expect()` instead of manual waits
|
|
325
|
+
- Ensure proper `beforeAll`/`afterAll` cleanup (start browser once, not per test)
|
|
326
|
+
- Check for timing-dependent logic in your app
|
|
327
|
+
|
|
328
|
+
### Browser not starting
|
|
329
|
+
- Ensure browser driver is installed
|
|
330
|
+
- Check `BROWSER_NAME` environment variable
|
|
331
|
+
- Verify browser is installed on your system
|
|
332
|
+
|
|
333
|
+
## Getting Help
|
|
334
|
+
|
|
335
|
+
- Check [examples](../examples/) folder for more samples
|
|
336
|
+
- Review [tests](../tests/) for advanced usage patterns
|
|
337
|
+
- Read method JSDoc in your IDE for inline documentation
|