UNPKG

@progress/kendo-e2e

Version:

Kendo UI end-to-end test utilities.

338 lines (247 loc) 9.21 kB
# Getting Started with kendo-e2e **kendo-e2e** is a Selenium-based testing library with **automatic waiting** built-in, making your tests faster to write and more reliable. ## Why kendo-e2e? Traditional Selenium tests are flaky because they require manual waits. kendo-e2e solves this by: ✅ **Automatic waiting** - Every method waits for elements automatically ✅ **No StaleElementReferenceException** - Built-in retry logic ✅ **Modern expect API** - Playwright-style assertions with auto-retry ✅ **Less code** - Write tests faster with intuitive methods ## Installation ```bash npm install @kendo/kendo-e2e --save-dev ``` ## Quick Start ### Basic Browser Test ```typescript import { Browser } from '@kendo/kendo-e2e'; describe('My First Test', () => { let browser: Browser; beforeAll(async () => { browser = new Browser(); }); afterAll(async () => { await browser.close(); }); it('should load the page and interact', async () => { // Navigate to URL await browser.navigateTo('https://example.com'); // Click button (automatically waits for it to appear) await browser.click('#login-button'); // Type in input (automatically waits and clears) await browser.type('#username', 'testuser'); await browser.type('#password', 'secret123'); // Submit form await browser.click('#submit'); // Assert with automatic retry (waits up to 3s by default) await browser.expect('.welcome-message').toHaveText('Welcome testuser'); await browser.expect('.dashboard').toBeVisible(); }); }); ``` ### Key Differences from Plain Selenium | Plain Selenium | kendo-e2e | |----------------|-----------| | `driver.findElement(By.css('#btn'))` then manual wait | `await browser.click('#btn')` | | Manual `WebDriverWait` everywhere | Automatic waiting built-in | | `StaleElementReferenceException` errors | Automatic retry handles it | | Complex wait conditions | Simple `expect()` API | ## Configuration Control browser settings via environment variables: ```bash # Browser selection BROWSER_NAME=chrome # chrome, firefox, safari, MicrosoftEdge HEADLESS=true # Run without UI (default: false) # Window size BROWSER_WIDTH=1920 BROWSER_HEIGHT=1080 ``` ### In Code Configuration ```typescript // Mobile emulation const browser = new Browser({ mobileEmulation: { deviceName: 'iPhone 14 Pro Max' } }); // Custom window size const browser = new Browser(); await browser.resizeWindow(1920, 1080); // BiDi protocol for advanced features const browser = new Browser({ enableBidi: true }); ``` ## Common Patterns ### Finding Elements ```typescript // Find single element (waits automatically) const button = await browser.find('#submit'); const header = await browser.find('.page-header'); // Find all matching elements (no wait) const items = await browser.findAll('.list-item'); // Find with wait for at least one const results = await browser.findAllWithTimeout('.search-result'); // Find child within parent const dialog = await browser.find('.modal'); const closeBtn = await browser.findChild(dialog, '.close-button'); ``` ### Interacting with Elements ```typescript // Click (waits for visible and enabled) await browser.click('#button'); // Type text (clears by default) await browser.type('#input', 'text'); // Type without clearing await browser.type('#notes', 'more text', { clear: false }); // Type and press Enter await browser.type('#search', 'query', { sendEnter: true }); // Hover over element await browser.hover('.menu-item'); // Double-click await browser.doubleClick('.file-icon'); // Right-click await browser.contextClick('.item'); // Drag and drop await browser.dragTo('#source', '#target'); ``` ### Waiting and Assertions ```typescript // Modern expect API (auto-retries) await browser.expect('#message').toHaveText('Success'); await browser.expect('.modal').toBeVisible(); await browser.expect('.spinner').not.toBeVisible(); // Wait for condition import { EC } from '@kendo/kendo-e2e'; await browser.wait(EC.isVisible('#element')); // Wait safely (returns boolean, doesn't throw) const appeared = await browser.waitSafely(EC.isVisible('.optional')); if (appeared) { await browser.click('.optional .close'); } ``` ### Keyboard Interactions ```typescript import { Key } from '@kendo/kendo-e2e'; // Send single key await browser.sendKey(Key.ENTER); await browser.sendKey(Key.TAB); await browser.sendKey(Key.ESCAPE); // Key combinations await browser.sendKeyCombination(Key.CONTROL, 'c'); // Copy // Cross-platform Ctrl/Cmd await browser.sendControlKeyCombination('v'); // Paste ``` ### Working with Forms ```typescript // Fill out a form await browser.type('#firstName', 'John'); await browser.type('#lastName', 'Doe'); await browser.type('#email', 'john@example.com'); await browser.click('#submit'); // Verify form submission await browser.expect('.success-message').toBeVisible(); await browser.expect('.success-message').toHaveText('Form submitted successfully'); ``` ## Best Practices ### ✅ Do This ```typescript // Use expect() for assertions with auto-retry await browser.expect('#status').toHaveText('Complete'); // Use CSS selectors (simpler, faster) await browser.click('#submit'); await browser.click('.btn-primary'); // Chain operations naturally await browser.navigateTo('https://example.com'); await browser.click('#login'); await browser.type('#username', 'user'); await browser.click('#submit'); await browser.expect('.dashboard').toBeVisible(); ``` ### ❌ Avoid This ```typescript // DON'T add manual sleeps - automatic waiting handles it await browser.sleep(2000); // ❌ Unnecessary // DON'T use complex XPath when CSS works await browser.click(By.xpath('//div[@id="submit"]')); // ❌ await browser.click('#submit'); // ✅ Better // DON'T manually wait before actions await browser.wait(EC.isVisible('#button')); await browser.click('#button'); // ❌ click() already waits! // Just do: await browser.click('#button'); // ✅ ``` ## Testing Checklist Essential checks for every test: ```typescript it('should work without errors', async () => { // Clear browser console await browser.clearLogs(); // Your test actions await browser.navigateTo('https://example.com'); await browser.click('#trigger-action'); // Check for console errors const errors = await browser.getErrorLogs(); expect(errors.length).toBe(0); // Check accessibility const a11yViolations = await browser.getAccessibilityViolations(); expect(a11yViolations.length).toBe(0); }); ``` ## Next Steps - 📚 Read [API Reference](./API_REFERENCE.md) for complete method documentation - 🎯 Check [Common Patterns](./PATTERNS.md) for real-world examples ## Example Test Suite ```typescript import { Browser } from '@kendo/kendo-e2e'; describe('Login Flow', () => { let browser: Browser; beforeAll(async () => { browser = new Browser(); }); afterAll(async () => { await browser.close(); }); beforeEach(async () => { await browser.navigateTo('https://example.com/login'); await browser.clearLogs(); }); it('should show login form', async () => { await browser.expect('#login-form').toBeVisible(); await browser.expect('#username').toBeVisible(); await browser.expect('#password').toBeVisible(); }); it('should login successfully', async () => { await browser.type('#username', 'testuser'); await browser.type('#password', 'password123'); await browser.click('#login-button'); await browser.expect('.welcome-message').toBeVisible(); await browser.expect('.welcome-message').toHaveText('Welcome testuser'); // Verify no console errors const errors = await browser.getErrorLogs(); expect(errors.length).toBe(0); }); it('should show error for invalid credentials', async () => { await browser.type('#username', 'invalid'); await browser.type('#password', 'wrong'); await browser.click('#login-button'); await browser.expect('.error-message').toBeVisible(); await browser.expect('.error-message').toHaveText('Invalid credentials'); }); }); ``` ## Troubleshooting ### Tests are slow - Reduce timeouts for faster feedback: `await browser.expect('#el').toBeVisible({ timeout: 5000 })` - Use headless mode: `HEADLESS=true npm test` ### Element not found - Check your selector is correct - Verify element exists in the DOM when expected - Try using `findAllWithTimeout` if content loads dynamically ### Flaky tests - Use `expect()` instead of manual waits - Ensure proper `beforeAll`/`afterAll` cleanup (start browser once, not per test) - Check for timing-dependent logic in your app ### Browser not starting - Ensure browser driver is installed - Check `BROWSER_NAME` environment variable - Verify browser is installed on your system ## Getting Help - Check [examples](../examples/) folder for more samples - Review [tests](../tests/) for advanced usage patterns - Read method JSDoc in your IDE for inline documentation