# 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 @progress/kendo-e2e --save-dev
```

## Quick Start

### Basic Browser Test

```typescript
import { Browser } from '@progress/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 '@progress/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 '@progress/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 '@progress/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

## AI Agent Integration (CLI + Skills)

kendo-e2e includes a CLI and Skills system for AI coding agents. Install the skill file so your AI assistant automatically learns how to write tests:

```bash
npx kendo-e2e install --skills
```

The AI can then use CLI commands to explore your app and generate tests:

```bash
npx kendo-e2e open https://your-app.com    # Open browser
npx kendo-e2e snapshot                      # Capture DOM → disk
npx kendo-e2e click ".k-button"             # Interact
npx kendo-e2e screenshot                    # Visual capture → disk
npx kendo-e2e close                         # Clean up
```

All heavy output is saved to `.kendo-e2e/` on disk (not in the AI context window), making it ~20-100x more token-efficient than MCP-based approaches.

## 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
